>>> import re,urllib2
>>> re.search(’\d+\.\d+\.\d+\.\d+’,urllib2.urlopen(”http://www.hereismyip.com”).read()).group()
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
“”"ping.py
ping.py uses the ICMP protocol’s mandatory ECHO_REQUEST
datagram to elicit an ICMP ECHO_RESPONSE from a
host or gateway.
Copyright (C) 2004 - Lars Strand <lars strand at gnist org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Must be running as root, or write a suid-wrapper. Since newer *nix
variants, the kernel ignores the set[ug]id flags on #! scripts for
security reasons
RFC792, echo/reply message:
0
1
2
3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type
| Code
|
Checksum
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
Identifier
| Sequence
Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data … +-+-+-+-+-
TODO:
- do not create socket inside ‘while’ (but if not: ipv6 won’t work)
- add support for broadcast/multicast
- add support for own payload string
CHANGELOG:
DONE –> bugfix from Filip Van Raemdonck mechanix debian org
DONE –> add more support for modules (raise instead of sys.exit)
DONE –> locale func names
DONE –> package def
DONE –> some code cleanup
“”"
import sys
import os
import struct
import array
import time
import select
import binascii
import math
import getopt
import string
import socket
# total size of data (payload)
ICMP_DATA_STR = 56
# initial values of header variables
ICMP_TYPE = 8
ICMP_TYPE_IP6 = 128
ICMP_CODE = 0
ICMP_CHECKSUM = 0
ICMP_ID = 0
ICMP_SEQ_NR = 0
# Package definitions.
__program__ = ‘ping’
__version__ = ‘0.5a’
__date__ = ‘2004/15/12′
__author__ = ‘Lars Strand <lars at unik no>’
__licence__ = ‘GPL’
__copyright__ = ‘Copyright (C) 2004 Lars Strand’
def _construct(id, size, ipv6):
“”"Constructs a ICMP echo packet of variable size
“”"
# size must be big enough to
contain time sent if size < int(struct.calcsize(”d”)):
_error(”packetsize to small, must
be at least %d” % int(struct.calcsize(”d”)))
# construct header if ipv6:
header = struct.pack(’BbHHh’,
ICMP_TYPE_IP6, ICMP_CODE, ICMP_CHECKSUM,ICMP_ID, ICMP_SEQ_NR+id)
else: header =
struct.pack(’bbHHh’, ICMP_TYPE, ICMP_CODE, ICMP_CHECKSUM,ICMP_ID,
ICMP_SEQ_NR+id)
# if size big enough, embed this payload
load = “– IF YOU ARE READING THIS YOU ARE A NERD! –”
# space for time
size -= struct.calcsize(”d”)
# construct payload based on size, may be omitted
rest = “”
if size > len(load):
rest = load
size -= len(load)
# pad the rest of payload
rest += size * “X”
# pack
data = struct.pack(”d”, time.time()) + rest
packet = header + data # ping packet without checksum
checksum = _in_cksum(packet) # make checksum
# construct header with correct checksum
if ipv6:
header = struct.pack(’BbHHh’, ICMP_TYPE_IP6, ICMP_CODE, checksum, ICMP_ID, ICMP_SEQ_NR+id)
else:
header = struct.pack(’bbHHh’, ICMP_TYPE, ICMP_CODE, checksum, ICMP_ID, ICMP_SEQ_NR+id)
# ping packet *with* checksum
packet = header + data
# a perfectly formatted ICMP echo packet
return packet
def _in_cksum(packet):
“”"THE RFC792 states: ‘The 16 bit one’s complement of
the one’s complement sum of all 16 bit words in the header.’
Generates a checksum of a (ICMP) packet. Based on in_chksum found
in ping.c on FreeBSD.
“”"
# add byte if not dividable by 2
if len(packet) & 1:
packet = packet + ‘\0′
# split into 16-bit word and insert into a binary array
words = array.array(’h', packet)
sum = 0
# perform ones complement arithmetic on 16-bit words
for word in words:
sum += (word & 0xffff)
hi = sum >> 16
lo = sum & 0xffff
sum = hi + lo
sum = sum + (sum >> 16)
return (~sum) & 0xffff # return ones complement
def pingNode(alive=0, timeout=1.0, ipv6=0, number=sys.maxint, node=None, flood=0, size=ICMP_DATA_STR):
“”"Pings a node based on input given to the function.
“”"
# if no node, exit
if not node:
_error(”")
# if not a valid host, exit
if ipv6: if
socket.has_ipv6:
try:
info, port = socket.getaddrinfo(node, None)
host = info[4][0]
# do not print ipv6 twice if ipv6 address given as node
if host == node:
noPrintIPv6adr = 1
except:
_error(”cannot resolve %s: Unknow host” % node)
else:
_error(”No
support for IPv6 on this plattform”)
else: # IPv4
try: host
= socket.gethostbyname(node)
except:
_error(”cannot resolve %s: Unknow host” % node)
# trying to ping a network?
if not ipv6:
if int(string.split(host, “.”)[-1]) == 0:
_error(”no support for network ping”)
# do some sanity check
if number == 0:
_error(”invalid count of packets to transmit: ‘%s’” % str(a))
if alive:
number = 1
# Send the ping(s)
start = 1; mint = 999; maxt = 0.0; avg = 0.0
lost = 0; tsum = 0.0; tsumsq = 0.0
# tell the user what we do
if not alive: if
ipv6: # do
not print the ipv6 twice if ip adress given as node
# (it can
be to long in term window)
if
noPrintIPv6adr == 1:
# add 40 (header) + 8 (icmp header) + payload
print “PING %s : %d data bytes (40+8+%d)” % (str(node), 40+8+size,
size)
else:
# add 40 (header) + 8 (icmp header) + payload
print “PING %s (%s): %d data bytes (40+8+%d)” % (str(node), str(host),
40+8+size, size) else:
# add 20
(header) + 8 (icmp header) + payload
print
“PING %s (%s): %d data bytes (20+8+%d)” % (str(node), str(host),
20+8+size, size)
# trap ctrl-d and ctrl-c try:
# send the number of ping packets
as given while start <= number:
lost += 1
# in case user hit ctrl-c
# create
the IPv6/IPv4 socket
if ipv6:
# can not create a raw socket if not root or setuid to root
try:
pingSocket = socket.socket(socket.AF_INET6, socket.SOCK_RAW,
socket.getprotobyname(”ipv6-icmp”))
except socket.error, e:
print “socket error: %s” % e
_error(”You must be root (uses raw sockets)” %
os.path.basename(sys.argv[0]))
# IPv4
else:
# can not create a raw socket if not root or setuid to root
try:
pingSocket = socket.socket(socket.AF_INET,
socket.SOCK_RAW,socket.getprotobyname(”icmp”))
except socket.error, e:
print “socket error: %s” % e
_error(”You must be root (%s uses raw sockets)” %
os.path.basename(sys.argv[0]))
packet =
_construct(start, size, ipv6) # make a ping packet
# send the ping
try:
pingSocket.sendto(packet,(node,1))
except socket.error, e:
_error(”socket error: %s” % e)
# reset values
pong = “”; iwtd = []
# wait until there is data in the socket
while 1:
# input, output, exceptional conditions
iwtd, owtd, ewtd = select.select([pingSocket], [], [], timeout)
break # no data and timout occurred
# data on socket - this means we have an answer
if
iwtd: # ok, data on socket
endtime = time.time() # time packet received
# read data (we only need the header)
pong, address = pingSocket.recvfrom(size+48)
lost -= 1 # in case user hit ctrl-c
# examine packet
# fetch TTL from IP header
if ipv6:
# since IPv6 header and any extension header are never passed
# to a raw socket, we can *not* get hoplimit field..
# I hoped that a socket option would help, but it’s not
# supported:
# pingSocket.setsockopt(IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1)
# so we can’t fetch hoplimit..
# fetch hoplimit
#rawPongHop = struct.unpack(”c”, pong[7])[0]
# fetch pong header
pongHeader = pong[0:8]
pongType, pongCode, pongChksum, pongID, pongSeqnr =
struct.unpack(”bbHHh”, pongHeader)
# fetch starttime from pong
starttime = struct.unpack(”d”, pong[8:16])[0]
# IPv4
else:
# time to live
rawPongHop = struct.unpack(”s”, pong[8])[0]
# convert TTL from 8 bit to 16 bit integer
pongHop = int(binascii.hexlify(str(rawPongHop)), 16)
# fetch pong header
pongHeader = pong[20:28]
pongType, pongCode, pongChksum, pongID, pongSeqnr =
struct.unpack(”bbHHh”, pongHeader)
# fetch starttime from pong
starttime = struct.unpack(”d”, pong[28:36])[0]
# valid ping packet received?
if not pongSeqnr == start:
pong = None
# NO data on socket - timeout waiting for answer
if not
pong:
if alive:
print “no reply from %s (%s)” % (str(node), str(host))
else:
print “ping timeout: %s (icmp_seq=%d) ” % (host, start)
# do not wait if just sending one packet
if number != 1 and start < number:
time.sleep(flood ^ 1)
start += 1
continue # lost a packet - try again
triptime = endtime - starttime # compute RRT
tsum +=
triptime
# triptime for all packets (stddev)
tsumsq += triptime * triptime # triptime^2 for all
packets (stddev)
# compute statistic
maxt = max ((triptime, maxt))
mint = min ((triptime, mint))
if alive:
print str(node) + ” (” + str(host) +”) is alive”
else:
if ipv6:
# size + 8 = payload + header
print “%d bytes from %s: icmp_seq=%d time=%.5f ms” % (size+8,
host, pongSeqnr, triptime*1000)
else:
print “%d bytes from %s: icmp_seq=%d ttl=%s time=%.5f ms” % (size+8,
host, pongSeqnr, pongHop, triptime*1000)
# do not wait if just sending one packet
if number
!= 1 and start < number:
# if flood = 1; do not sleep - just
ping
time.sleep(flood ^ 1) # wait before send new packet
# the last thing to do is update the counter - else the value
# (can) get wrong when computing summary at the end (if user
# hit ctrl-c when pinging)
start += 1
# end ping send/recv while
# if user ctrl-d or ctrl-c
except (EOFError, KeyboardInterrupt):
# if user disrupts ping, it is most likly done before
# the counter get updates - if do not update it here, the
# summary get all wrong.
start += 1
pass
# compute and print som stats
# stddev computation based on ping.c from FreeBSD
if start != 0 or lost > 0: # do not print stats
if 0 packet sent start -=
1
# since while is ‘<=’ avg =
tsum / start # avg round trip
vari = tsumsq / start - avg * avg
# %-packet lost
if start == lost:
plost =
100 else:
plost =
(lost/start)*100
if not
alive:
print “\n— %s ping statistics —” % node
print “%d
packets transmitted, %d packets received, %d%% packet loss” % (start,
start-lost, plost)
# don’t
display summary if 100% packet-loss
if plost
!= 100:
print “round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms” %
(mint*1000, (tsum/start)*1000, maxt*1000, math.sqrt(vari)*1000)
pingSocket.close()
def _error(err):
“”"Exit if running standalone, else raise an exception
“”"
if __name__ == ‘__main__’:
print “%s: %s” % (os.path.basename(sys.argv[0]), str(err))
print “Try `%s –help’ for more information.” % os.path.basename(sys.argv[0])
sys.exit(1)
else:
raise Exception, str(err)
def _usage():
“”"Print usage if run as a standalone program
“”"
print “”"usage: %s [OPTIONS] HOST
Send ICMP ECHO_REQUEST packets to network hosts.
Mandatory arguments to long options are
mandatory for short options too. -c, –count=N
Stop after sending (and receiving) ‘N’ ECHO_RESPONSE
packets. -s, –size=S Specify the number
of data bytes to be sent. The default
is 56, which translates into 64 ICMP data bytes when
combined with the 8 bytes of ICMP header data. -f,
–flood Flood ping. Outputs packets as
fast as they come back. Use
with caution! -6, –ipv6 Ping
using IPv6. -t, –timeout=s Specify a timeout, in seconds,
before a ping packet is
considered ‘lost’. -h, –help
Display this help and exit
Report bugs to lars [at] gnist org”"” % os.path.basename(sys.argv[0])
if __name__ == ‘__main__’:
“”"Main loop
“”"
# version control
version = string.split(string.split(sys.version)[0][:3], “.”)
if map(int, version) < [2, 3]:
_error(”You need Python ver 2.3 or higher to run!”)
try:
# opts = arguments recognized,
# args = arguments NOT recognized
(leftovers) opts, args =
getopt.getopt(sys.argv[1:-1], “hat:6c:fs:”, ["help", "alive",
"timeout=", "ipv6", "count=", "flood", "packetsize="])
except getopt.GetoptError:
# print help information and exit:
_error(”illegal option(s) — ” +
str(sys.argv[1:]))
# test whether any host given
if len(sys.argv) >= 2:
node = sys.argv[-1:][0] # host to be pinged
if node[0] == ‘-’ or node == ‘-h’ or node == ‘–help’ :
_usage()
else:
_error(”No arguments given”)
if args:
_error(”illegal option — %s” % str(args))
# default variables
alive = 0; timeout = 1.0; ipv6 = 0; count = sys.maxint;
flood = 0; size = ICMP_DATA_STR
# run through arguments and set
variables for o, a in opts:
if o == “-h” or o ==
“–help”: # display help and exit
_usage()
sys.exit(0) if o == “-t” or o ==
“–timeout”: # timeout before “lost”
try:
timeout = float(a)
except:
_error(”invalid timout: ‘%s’” % str(a))
if o == “-6″ or o ==
“–ipv6″: # ping ipv6
ipv6 = 1
if o == “-c” or o ==
“–count”: # how many pings?
try:
count = int(a)
except:
_error(”invalid count of packets to transmit: ‘%s’” % str(a))
if o == “-f” or o ==
“–flood”: # no delay between ping send
flood = 1
if o == “-s” or o ==
“–packetsize”: # set the ping payload size
try:
size = int(a)
except:
_error(”invalid packet size: ‘%s’” % str(a))
# just send one packet and say
“it’s alive” if o == “-a” or o ==
“–alive”:
alive = 1
# here we send
pingNode(alive=alive, timeout=timeout, ipv6=ipv6, number=count, node=node, flood=flood, size=size)
# if we made it this far, do a clean exit
sys.exit(0)
### end
#!/usr/bin/env python
# -*- coding:gb2312 -*-
#导入wxPython基础模块
from wxPython.wx import *
#定义一个新的frame类
class TestFrame(wxFrame):
def __init__(self):
#调用父类的构造函数,并仔细看其窗口类型的设定
#关键的问题就是窗口的边框类型的设定
wxFrame.__init__(self, None, -1, u”无边框窗口”, \
style = \
wxFRAME_SHAPED\
|wxSIMPLE_BORDER\
|wxFRAME_NO_TASKBAR\
|wxSTAY_ON_TOP
)
label1 = wxStaticText(self, -1, u”看到了吗?”, size=(300,200))
self.hasShape = False
self.delta = wxPoint(0, 0)
class MyApp(wxApp):
def OnInit(self):
f = TestFrame()
f.Show(true)
self.SetTopWindow(f)
return true
if ( __name__ == “__main__” ):
a = MyApp(0)
a.MainLoop()
ChenPeng 2006-3-6 BeiJing
源代码: 本工程只包括两个文件start.bat 和CustomParser.py:
start.bat
//make the dir for files and run the project
mkdir files
python CustomParser.py
CustomParser.py
from sgmllib import SGMLParser
from string import find, replace, rjust
from threading import Thread
import urllib
__author__ = “Chen Peng (peng.ch@hotmail.com)”
__version__ = “$Revision: 1.0 $”
__date__ = “$Date: 2006/03/03 $”
__copyright__ = “Copyright (c) 2006 Chen Peng”
__license__ = “Python”
__all__ = ["Gif_163_Parser"]
class PDownloadThread( Thread ):
“”"
Download the files in the dict and save them to
local files with the given name
“”"
def __init__( self, DictList,i ):
Thread.__init__( self )
self.DictList=DictList
self.pageno=str(i);
def run( self
):
for k in self.DictList.keys():
try:
print ‘Download’+self.DictList[k]+’……’
uFile=urllib.urlretrieve( self.DictList[k],
‘.\\files\\’+k+’.'+self.DictList[k].split(’.')[self.DictList[k].split(’.').__len__()-1])
except :
logfile = open(’error.log’, ‘a’)
logfile.write(self.pageno+’ ‘+self.DictList[k]+’ ‘+k+’\n’)
logfile.close()
print ‘Save to file ‘+k
class Gif_163_Parser( SGMLParser ):
“”"
任务:下载163彩图
原理:http://mms.163.com/new_web/cm_lv2_pic.jsp?catID=&ord=dDate&
page=2&type=1&key=
从1到415页(共6637)分析得到如下路径:“/fgwx/hhsj/1_060302175613_186/128×128.gif”
eg:<script>showPic(’22930′,’1′,’/fgwx/hhsj/1_060302175613_186/128×128.gif’,
‘1′,’编号:22930\n名字:
因为有你\n人气:100′);</script>
下载路径:http://mmsimg.163.com/new_web/loaditem.jsp/type=1/path=
/fgwx/llfj/1_060302175612_995/176×176.gif
“”"
def reset( self
):
SGMLParser.reset( self )
self.headURL=’http://mmsimg.163.com/new_web/loaditem.jsp/type=1/path=’
self.SubURL = []
self.Links = {}
def
start_script( self, attrs ):
#self.SubURL.extend( [' %s="%s"' % ( key, value ) for key, value in
attrs] ) pass
def end_script( self ):
pass
def handle_data( self, text ):
if find( text, ’showPic’
)!=-1:
self.Links[replace( text.split( '\\n' )[1], ‘\xc3\xfb\xd7\xd6: ‘, ”
)]=self.headURL+replace ( text.split( ‘,’ )[2], ‘\”, ” );
def Execute( self ):
for i in range( 1, 415 ):
self.Links.clear;
try:
usock = urllib.urlopen( “http://mms.163.com/new_web/cm_lv2_pic.jsp?catID=&ord=dDate&page=”+str(i)+”&type=1&key=”
)
self.feed( usock.read() )
usock.close()
TestThread=PDownloadThread( self.Links ,i)
TestThread.start()
self.close()
except
IOError:
pass
#print ( ["%s=%sn"% ( k, self.Links[k] ) for k in self.Links.keys()] )
#print self.Links
if __name__ == ‘__main__’:
#Gif_163_Parser().Execute();
testtask=Gif_163_Parser()
testtask.Execute()
mod_python中文文档
mod_python是apache组织的一个项目,通过它,可以开发psp或cgi,mod_python功能强大,速度快,是非常优秀的web开发工具。
第一章 简介
1.1 性能
mod_python的一个最主要优点就是在性能上超越传统CGI。下面是一个非常粗略的测试。该测试是在一台运
行Red Hat Linux 7.3的奔腾1.2G的机器上完成的。Ab
用来对4种脚本进行测试,所有的都是从标准cgi模块输入(因为这是一个典型Python cgi脚本开始的方式),
然后输出一个简单的单词”Hello!”。这个结果是基于10000次并发为1的请求。
Standard CGI: 23 requests/s
Mod_python cgihandler: 385 requests/s
Mod_python publisher: 476 requests/s
Mod_python handler: 1203 requests/s
1.2 适应性
Apache分阶段的处理请求(比方说:读取请求,解析header,
检查存取路径,等等)。这些阶段能被称为”处理器”(handler)的函数实现。传统上,
“处理器”是由C语言编写,并编译成Apache的模块。Mod_python提供了一个通过Python写的Apache处理器的来扩展Apache功
能的方法。关于Apache请求处理过程的详尽描述,请参阅 Apache API Notes, 也可以参阅 Mod_python - Integrating Python with Apache。
为了轻松地从CGI移植,一个标准的mod_python处理器提供了模拟的CGI环境,允许用户在不对代码做任何修改的情况下,使遗留的脚本运行在mod_python下(大多数情况)。
See Also: http://dev.apache.org/ Apache Developer Resources http://www.modpython.org/python10/ Mod_Python - Integrating Python with Apache, presented at Python 10
1.3 历史
Mod_python起源于一个被称为Httpdapy(1997)的项目。很长时间以来,
Httpdapy并没有被称作mod_python,因为Httpdapy不是专门用于Apache的。
Httpdapy被设计成跨平台的,实际上最初是为Netscape server写的(那个时候被称为Nsapy)(1997)
这个Httpdapy的README文件的摘要,很好的阐述了在HTTP server中嵌入Python所带来的挑战和解决方案。
第二章 安装
注意:得到关于安装和其他问题帮助的最好地方是mod_python的邮件列表。请花几分钟,发送一封主题为”subscribe”的e-mail至mod_python-request@modpython.org, 加入mod_python的邮件列表。
2.1 前提条件
- Python 2.2.1或以后版本。Python的早期版本将不能工作。
- Apache 2.0.40或以后版本(对于Apache 1.3.x版本,请使用mod_python 2.7.x版本)
为了编译mod_python,你将需要Apache和Python的包含文件(include
files),也包括安装在你系统上的Python
的库。如果你是从源码安装的python和Apahce,那么你已经有了所需要的一切。但是,如果你是使用预打包(prepackaged)的软件(比
如,Red Hat Linux RPM, Debian, 或者从sunsite得到的Solaris
packages,等等),那么很可能你的系统里只有二进制文件而没有源文件。通常,编译mod_python所需要的Apache和Python得包含
文件和库文件是分离的”开发”包的一部分。如果你不确定你是否有所有需要的文件,或者从源码编译和安装Python和Apache,或者参考你系统中关于
如何得到开发包的文档。
2.2 编译
有两种方法将模块编译并连接到Apache — 静态的,或者作为DSO(Dynamic Shared Object).
DSO是mod_python现在更流行的并且被推荐的方法。模块被编译为一个共享库,在运行时有服务器动态装入。
DSO的好处是模块安装时不需要重新编译Apache,并且需要时再使用。关于Apahce DSO机制更详尽的描述, http://httpd.apache.org/docs-2.0/dso.html.
现在mod_python只支持DSO。
静态连接是一种较老的方法。随着动态连接在大多数的平台上的实现,它被越来越少的用到。它主要的缺点是需要重新编译Apache,在很多情况下这可不是一个好的选择。
2.2.1 运行 ./configure
./configure脚本将分析你的环境,并且创建为你系统特别定制的Makefiles。除了所有标准autoconf所做的以外,./configure还要作如下的工作:
- 检查一个被成为apxs的程序是否可用。这个程序是标准Apache分发包的一部分,并且也是编译DSO所必需的。如果在你的PATH目录或者/usr/local/apache/bin目录下无法找到apxs,那么DSO的编译将无法进行.
你可以通过–with-apxs选项,手动指出apxs所在目录,比如:
$ ./configure --with-apxs=/usr/local/apache/bin/apxs
推荐你指定该选项。
- 检查你的Python的版本,并试图通过参看编译到你的Python二进制文件中的不同参数,判断libpython的位
置。通常,它会用到在你的PATH目录中所找到的python程序。如果在这些目录中所找到的Python二进制文件不合适,或者不是
mod_python所期望的,你可以通过–with-python选项,指定一个供选择的位置,例如:
$ ./configure --with-python=/usr/local/bin/python2.2
2.2.2 开始 make
开始编译,只要简单的输入命令
$ make
2.3 安装
2.3.1 运行 make install
这部分安装需要以根用户(root)身份进行。
$ su
# make install
- 这会简单的将库文件拷贝到你的Apache libexec目录下,其他所有的模块均在这里。
- 最后它将安装Python的库文件到site-packages目录并且编译它们。
注意: 如果你希望选择性的仅仅安装Python库文件或者仅安装DSO(这可能不总是需要超级用户权限),你可以用如下的make目标:install_py_lib and install_dso
2.3.2 配置 Apache
如果你将mod_python编译成DSO,那么你需要通过在Apache的配置文件(通常称为httpd.conf 或者 apache.conf)里面加入如下一行,来载入模块:
LoadModule python_module libexec/mod_python.so
mod_python.so的实际路径可能会变化,但是make install在近结束时会报告mod_python.so的位置和LoadModule目录的形式。
2.4 测试
- 使你网站上的某些目录可见,比如,htdocs/test
-
在主配置文件或者.htaccess中加入下面几句话。如果你准备使用.htaccess,你就不需要<Directory>标记。(这个目录变成.htaccess文件所在的目录),并且你必须确定应用在这个目录上的AllowOverride至少指定了FileInfo。(默认是None,将无法工作)
<Directory /some/directory/htdocs/test>
AddHandler mod_python .py
PythonHandler mptest
PythonDebug On
</Directory>
-
(将上面的/some/directory替换成你系统中有效的目录,通常是你Apache的ServerRoot)
- 如果你改变了主配置文件,你需要重启Apache,以使这些改变生效。
- 编辑htdocs/test中的mptest.py文件,使其有如下的代码(从你的浏览器剪切和粘贴的时候要小心,可能会产生错误的缩进和符号错):
Toggle line numbers
1 from mod_python import apache
2
3 def handler(req):
4 req.write(“Hello World!”)
5 return apache.OK
- 将你的浏览器指向mptest.py所在的URL; 你将会看到”Hello World!”. 如果你没有看到这些,请参阅下一节”故障排除”.
- 如果一切顺利,请前进到第三章《指南》。
2.5 故障排除
你可以尝试从如下方面确定问题所在
- 如果有错误输出,仔细研究之
- 检查服务器的日志文件,它可能含有有用的线索
- 尝试从命令行方式单进程的执行Apache
./httpd -X
这防止它在后台运行,并可以提供一些有用的信息。
- 在mod_python的邮件列表上咨询。确保包含如下特定的信息。
- Mod_python版本
- 你的操作系统的类型,名称,版本
- 你的Python的版本,以及任何不寻常的编译选项
- 你的Apache的版本
- Apache配置文件(.htaccess)相关的部分
- Python代码中相关的部分
第三章 指南
现在通过前一章的学习,我们已经学会并安装好mod_python,现在可以通过快速阅读本章进入程序开发阶段。 我强烈推荐你在阅读完本章以后接着阅读(至少是前面的部分)第4章python API的内容。
3.1 快速开始,学习发布处理器(Publisher Handler)
这一节为我们提供了一个快速的关于发布处理器的概况,从而使我们可以不用关注太多的细节就可以写出程序。在这本指南的最后一节,会有一个更详细全面的关于mod_python处理器如何工作的讲解。
发布处理器是mod_python的众多标准处理器中的一个,为了使你的发布处理器能够正常工作,需要在你的配置文件(通常是httpd.conf)中加入下面几句话:
<Directory D:/apache/Apache2/cgi-bin/oa>
AddHandler mod_python .py
PythonHandler mod_python.publisher
PythonDebug On
</Directory>
(注意:D:/apache/Apache2/cgi-bin/oa是我的机器上的路径)
接下来的这个例子展示了一个简单的返回表单,这个表单询问用户的名称,电子邮箱,地址和意见,然后发送一封电子邮
件给网络管理员,信的内容就是用户填写的意见。这个简单的程序包括两个文件:form.html—用来收集数据,form.py—表单的
action指令发送的目标。
下面是form.html的代码:
<html>
请填写下面的回馈表单:
<form action=”form.py/email” method=”POST”>
用户名: <input type=”text” name=”name”><br>
电子邮件: <input type=”text” name=”email”><br>
意见: <textarea name=”comment” rows=4 cols=20></textarea><br>
<input type=”submit”>
</form>
</html>
注意<form>标签的action指向form.py/email。接下来我们编写这个名叫form.py的脚本,如下:
import smtplib
WEBMASTER = “webmaster” # webmaster e-mail
SMTP_SERVER = “localhost” # your SMTP server
def email(req, name, email, comment):
#确定用户提供了所有的参数
if not (name and email and comment):
return “A required parameter is missing, \
please go back and correct the error”
# 创建消息对话框
msg = “”"\
From: %s
Subject: feedback
To: %s
I have the following comment:
%s
Thank You,
%s
“”" % (email, WEBMASTER, comment, name)
#发出信件
conn = smtplib.SMTP(SMTP_SERVER)
conn.sendmail(email, [WEBMASTER], msg)
conn.quit()
# 返回用户信息
s = “”"\
<html>
亲爱的%s,<br>
谢谢你的意见,我们会在近期与你联系.
</html>
“”" % name
return s
当用户点击确定按钮的时候,发布处理器就会调用form模块中的email方法,把表单中各个域的值做为email方法的参数传递给email方法,并且也会把request的对象req一并传递过去。
并不是非要把req做为email方法的一个参数不可,如果你不需要它,可以省略掉。发布处理器很灵活,它只会把那些在方法的参数列表中存在参数所相对应的域的值传递过去。
方法的返回值在浏览器中显示出来。
虽然发布处理器极大的简化了mod_python编程,但是mod_python所具有的强大功能却没有损失,因为发布处理器可以访问到request对象,所以你可以做到与原生(native)mod_python处理器完全相同的事情。 举例来说:
通过req.headers可以自定义头(header),通过抛出apache.SERVERERROR返回异常,通过req.write()和req.read()直接读写客户端等等。
阅读6.1节可以获得更多关于发布处理器的信息.
3.2 快速学习,看一下apache怎样处理请求
如果你想更深入的研究mod_python的功能,你需要弄明白一个处理器到底是什么。
apache是分步骤处理请求的,例如,第一步可能是确定用户,接下来验证用户是不是允许访问特定的文件,然后读
这个文件并把它发送到客户端。一个典型的静态文件请求包括三步:(1)解析请求的URI为文件在服务器上的路径。(2)读文件并把它发送到客户端。(3)
记录请求日志。这个日志中记录了执行的步骤,记录内容的多少取决于配置文件中的设置。
一个处理器就是处理其中某一步的方法,有可能在处理某一步的时候需要用到不止一个处理器,在这种情况下,这几个处
理器将被apache依次调用。每一步都会有一个默认的apache动作(大多数情况下都是很基础的方法或者不做任何事情)。与此同时,apache模块
还提供了附加的处理器,比如mod_python.
mod_python为apache提供了几乎所有需要用到的处理器,mod_python的处理器默认的不会提
供任何方法,除非我们明确的在配置文件中指示。这些指示以”python”开始,以”Handler”结束(比如:
pythonAuthenHandler
)它对应于一个python方法,这个方法处理众多步骤中的一步。所以mod_python的方法扮演的是发报机(dispatcher)的角色,通过它
联系apache动作和开发人员(比如你)写的python方法。
最经常用到的处理器是 pythonHandler,它处理那些请求提供内容的步骤。因为它没有名字,所以有时候它被作为泛指的处理器。这个处理器的默认的apache动作是读文件并把它发送的客户端。你写的大多数程序都会重载这个处理器。 如果想了解所有的处理器,请查看第5章 apache配置文件指示
3.3 那么mod_python到底做了些什么呢?
假设我们有下面的配置文件:
<Directory /mywebdir>
AddHandler mod_python .py
PythonHandler myscript
PythonDebug On
</Directory>
注意: /mywebdir 是一个物理绝对路径。 还有一个python程序‘/mywedir/myscript.py’如下:
from mod_python import apache def handler(req): req.content_type = “text/plain” req.write(”Hello World!”) return apache.OK
解释一下这个程序:
addHandler 指示告诉 apache,
所有在mywebdir目录或者是它的子目录下的所有.py文件,有关于这些文件的任何请求都用mod_python 来处理,
pythonHandler myscript 指示告诉 mod_python 用 myscript 执行默认的处理器. “pythonDebug On” 指示告诉mod_python如果发生错误,就把错误信息打印到客户端 (相对于写入日志文件),在开发的时候这个选项非常有用。
当一个请求发出时,apache通过调用mod_python中的处理器分步处理请求,Mod_python首先
检查请求的那个处理器是否在配置文件中指定了(记住,它的角色是发报机dispatcher),在我们的例子中,mod_python除了调用默认的那个
处理器外不会调用其他的任何处理器,然后,mod_python会发现”pythonHandler
myscript”指示,并按照下面的步骤来进行:
1.如果以前没有做过,那么就把pythonHandler指定的那个目录加到sys.path中。
2.尝试引入myscript的模块(注意,如果myscript在pythonHandler指定那个目录的子
目录中的话,引入会出错,因为子目录并没有加到sys.path中,解决这种情况的方法是使用包)例如:”pythonHandler
subdir.myscript”
3.在myscript中寻找名字叫handler的方法。
4.调用这个方法,并把request对象传递给它。
5.现在让我们深入这段脚本看一下:
from mod_python import apache
这个引入语句提供给我们一个访问apache的接口。除了极少数情况外,每一个mod_python程序一般都会有这一行。
def handler(req):
这是处理器方法的声明,它之所以叫”handler”是因为mod_python在指示中使用这个名字,转换它为
小写并移除”python”,所以”pythonHandler”变成了”handler”,你可以给它起别的名字,并且通过在指示中使用”::”明确的
指定它.举个例子,如果处理器方法叫”spam”,那么指示就应该是”pythonHandler myscript::spam”。
注意处理器必须有一个参数
request对象。Request对象提供了所有可能用到的信息,比如客户端的IP,头,URI等等.返回客户端的信息仍然通过request对象传递,注意,在mod_python中没有response对象。
req.content_type = "text/plain"
这条语句设置文档类型为” text/plain”。默认的通常是” text/html”,但是因为我们的处理器不处理任何html, 所以“text/plain”更合适一些。
req.write("Hello World!")
这条语句把字符串” Hello World!”写到客户端(再次强调没有response对象,所以写到客户端仍然有request对象)。
return apache.OK
这条语句告诉apache一切正常,而且请求也已经被处理了。如果出现异常,这一行应该返回
apache.HTTP INTERNAL SERVER ERROR或返回apache.HTTP
FORBIDDEN,而且apache会在日志中记录这个错误,并产生一条错误信息给客户端。
一些提示:如果你仔细阅读的话,就会发现URI只是指向了myscript.py这个文件,并没有指定处理器代码执行的顺序,实际上只要告诉处理器需要处理的是一个.py文件就可以了,文件的名字并不重要,即使URL中指向的文件并不存在.所以,对于上面的配置,http://myserver/mywebdir/myscript.py 和 http://myserver/mywebdir/montypython.py 执行的结果是一样的。
如果你看不懂这段话,重复多看几遍,直到弄懂为止。
3.4 现在学习一些更复杂的—认证
现在你已经知道了该怎样写一个简单的处理器,让我们来尝试着写一些更复杂的。
假设我们想用密码保护一个目录,用名字:spam,密码:eggs来登陆。
首先,我们需要告诉apache当需要认证的时候去调用我们的认证处理器。我们通过在配置文件中加入pythonAuthenHandler来实现,如下:
<Directory /mywebdir>
AddHandler mod_python .py
PythonHandler myscript
PythonAuthenHandler myscript
PythonDebug On
</Directory>
注意,我们在这里为两个不同的处理器指定了相同的脚本,这是可以的,如果你还记得,mod_python会在同一个脚本中为不同的处理器查找不同的方法,接下来,我们需要告诉apache我们使用的是最基本的HTTP认证,现在我们的配置文件应该是下面这样:
<Directory /mywebdir>
AddHandler mod_python .py
PythonHandler myscript
PythonAuthenHandler myscript
PythonDebug On
AuthType Basic
AuthName “Restricted Area”
require valid-user
</Directory>
现在我们要在myscript.py中写一个认证处理器方法,一个基本的认证处理器象下面这样:
from mod_python import apache
def authenhandler(req):
pw = req.get_basic_auth_pw()
user = req.user
if user == “spam” and pw == “eggs”:
return apache.OK
else:
return apache.HTTP_UNAUTHORIZED
让我们一行一行的看这段代码:
def authenhandler(req):
这是处理器方法的声明。为什么这个方法的名字叫authenhandler呢?这个问题我们在前面已经讲过了,
mod_python把配置文件中的指示的名称(pythonAuthenHandler)去掉”python”,然后把剩下的单词全部变成小写,因此就
是authenhandler了。
pw = req.get_basic_auth_pw()
我们通过这一句代码得到密码。http在传输验证密码的时候一般以base64的编码进行传输,这个方法把它解析成字符串。
user = req.user
然后我们通过这一句得到用户名.
if user == "spam" and pw == "eggs":
验证用户是不是我们在前面约定的用户和密码.
return apache.OK
如果是我们期望的那个用户,那么就返回apache.OK,apache收到以后就会完成这个请求,处理剩下的步骤。
else: return apache.HTTP_UNAUTHORIZED
如果用户和密码不对,我们就返回HTTP UNAUTHORIZED给客户端,这会在浏览器上显示。
第四章 Python API
4.1 多解释器
当使用mod_python写程序的时候,很重要的一点就是必须知道python语言的很多特色在写脚本的时候体现不出来。
Python的C API使python具有创建子解释器的能力。在Py
newInterpreter()方法的文档中有关于子解释器的更详细的描述。每一个子解释器都有它自己的单独的命名空间,不能被其他的子解释器访问到。
子解释器使那些运行在apache服务器中的程序互不干扰。
在启动服务器或初始化mod_python的时候,mod_python会初始化一个叫主解释器的解释器。主解释
器有一个字典。最初,这个字典是空的,当发出请求时,子解释器就会被创建,他们的引用也被存储在这个字典中。这个字典使用字符串做为key,作为解释器的
名字,这个名字可以是任何字符串。主解释器名叫”main interpreter”.其他解释器的命名方法可以通过
pythonInterp*来控制,这意味着运行在同一个虚拟服务器中所有脚本都在同一个子解释器中执行,但是在不同虚拟服务器中的脚本使用完全不同的命
名空间运行在不同的子解释器中。pythonInterpPerDirectory 和 pythonInterpPerDirective
指示改变了一般的命名规则,它们使用绝对路径来访问目录,当然也可以是python*Handler中指定的目录,
pythonInterpreter 可以用来强制解释器的名字为一个特殊的字符串从而舍弃习惯上的命名.
一旦被创建,如果子解释器遇到并发请求,那么它就会被重新生成。而不会被销毁,它将一直存在直到apache进程被杀掉。 你可以通过req.interpreter来找到你正在运行的解释器的名字。
4.2 请求处理器
一个处理器就是处理一个请求中的某一步的方法。Apache分步骤处理请求—读请求,处理头,提供内容等等。
在每一步中,它都会调用处理器,通过apache的核心或是它的一个模块来提供,比如:mod_python把控制权交给用python写的方法。一个用
python写的处理器和一个用c写的处理器完全相同,它有下面的要求: 一个处理器方法必须传递一个request对象的引用。(在本手册中,这个request对象一般都是指req变量) 每一个处理器可以返回:
apache.OK
意思是请求处理的这一步没有错误发生。
apache.DECLINED
意思是请求处理的这一步已经完成,apache需要查看后面的模块中的另一个处理器。
apache.HTTP ERROR
意思是发生了一个http错误,HTTP ERROR可以是下面中的任何一个:
HTTP_CONTINUE = 100 HTTP_SWITCHING_PROTOCOLS = 101 HTTP_PROCESSING = 102 HTTP_OK = 200 HTTP_CreateD = 201 HTTP_ACCEPTED = 202 HTTP_NON_AUTHORITATIVE = 203 HTTP_NO_CONTENT = 204 HTTP_RESET_CONTENT = 205 HTTP_PARTIAL_CONTENT = 206 HTTP_MULTI_STATUS = 207 HTTP_MULTIPLE_CHOICES = 300 HTTP_MOVED_PERMANENTLY = 301 HTTP_MOVED_TEMPORARILY = 302 HTTP_SEE_OTHER = 303 HTTP_NOT_MODIFIED = 304 HTTP_USE_PROXY = 305 HTTP_TEMPORARY_REDIRECT = 307 HTTP_BAD_REQUEST = 400 HTTP_UNAUTHORIZED = 401 HTTP_PAYMENT_REQUIRED = 402 HTTP_FORBIDDEN = 403 HTTP_NOT_FOUND = 404 HTTP_METHOD_NOT_ALLOWED = 405 HTTP_NOT_ACCEPTABLE = 406 HTTP_PROXY_AUTHENTICATION_REQUIRED= 407 HTTP_REQUEST_TIME_OUT = 408 HTTP_CONFLICT = 409 HTTP_GONE = 410 HTTP_LENGTH_REQUIRED = 411 HTTP_PRECONDITION_FAILED = 412 HTTP_REQUEST_ENTITY_TOO_LARGE = 413 HTTP_REQUEST_URI_TOO_LARGE = 414 HTTP_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_RANGE_NOT_SATISFIABLE = 416 HTTP_EXPECTATION_FAILED = 417 HTTP_UNPROCESSABLE_ENTITY = 422 HTTP_LOCKED = 423 HTTP_FAILED_DEPENDENCY = 424 HTTP_INTERNAL_SERVER_ERROR = 500 HTTP_NOT_IMPLEMENTED = 501 HTTP_BAD_GATEWAY = 502 HTTP_SERVICE_UNAVAILABLE = 503 HTTP_GATEWAY_TIME_OUT = 504 HTTP_VERSION_NOT_SUPPORTED = 505 HTTP_VARIANT_ALSO_VARIES = 506 HTTP_INSUFFICIENT_STORAGE = 507 HTTP_NOT_EXTENDED = 510
4.5 apache—–访问apache的内部
python通过一个叫apache的模块来访问apache内部,apache模块放在mod_python包
里。这个模块提供了很多访问apache内部构造的对象,很多方法和很多文档。(request对象也有很多访问apache内部的接口,在稍后的
request对象那一节我们会讲解。)
apache模块只能在mod_python的脚本中使用,这是因为它是基于mod_python提供的内置的_apache模块。 它这样被引入:
from mod_python import apache
mod_python.apache定义了下面这些方法和对象。
4.5.1方法
log_error(message[, level, server ])
一个apache ap_log_error()方法的接口,message是承载错误信息的字符串,level是下面常量中的一个:
APLOG_EMERG APLOG_ALERT APLOG_CRIT APLOG_ERR APLOG_WARNING APLOG_NOTICE APLOG_INFO APLOG_DEBUG APLOG_NOERRNO
Server是req.server对象的引用。如果server没有被指定,那么错误会被记录到默认的错误日志中,另外它也将被写入到虚拟服务器的错误日志中。如果server没有被指定,那么LogLevel的设置就没有作用,httpd在编译的时候会默认的指定LogLevel,一般使用warn等级。
如果你有一个request对象的引用,那么请使用req.log_error来代替它,它会预先使用请求的格式比如说request的IP来登陆。
Import_module(module name[, autoreload=1, log=0, path=None ])
如果一个模块在上次引入以后改动了,那么我可以利用mod_python的内部机制来自动重新引入这个模块,而这个方法就是达到这个目的的。
module
name是一个代表模块名称的字符串,(它可以包含包结构,例如:mypacke.mymoudle);
autoreload指明如果从上次引入以后这个模块改动了,那么是否自动重新引入。如果log设为true的话,那么当模块被重新引入的时候,会有一条
信息自动记载到日志中,path允许重新指定模块所在的路径。 举个例子:
from mod_python import apache
mymodule = apache.import_module(’mymodule’, log=1)
allow_methods([*args ])
一个设置req.allowed的非常方便的方法,req.allowed被用来构造”allow”头,它应该在返回一个HTTP NOT IMPLEMENTED错误以前就设置好。 参数可以是下面列表中的一个或多个:
M_GET M_PUT M_POST M_Delete M_CONNECT M_OPTIONS M_TRACE M_PATCH M_PROPFIND M_PROPPATCH M_MKCOL M_COPY M_MOVE M_LOCK M_UNLOCK M_VERSION_CONTROL M_CHECKOUT M_UNCHECKOUT M_CHECKIN M_Update M_LABEL M_REPORT M_MKWORKSPACE M_MKACTIVITY M_BASELINE_CONTROL M_MERGE M_INVALID
Config_tree()
返回服务器的配置树。这个树不包括来自.htaccess文件的指示。这是一个源文件的拷贝,请注意修改它对源文件没有作用。
Server_root()
返回ServerRoot.的值。
Make_table()
这个方法已经被废弃了,现在一般使用table(在后面)
mpm_query(code)
这个方法允许MPM的查询可以访问各种不同的参数,比如进程或线程的数量。返回值是下面三个常量中的一个:
AP_MPMQ_NOT_SUPPORTED = 0 #这个值指定一个MPM是否有能力执行线程或分线程。 AP_MPMQ_STATIC = 1 # 这个值指定一个MPM是否有能力使用静态线程或后台程序。 AP_MPMQ_DYNAMIC = 2 #这个值指定一个MPM是否有能力使用动态线程或后台程序。
code参数的值必须要是下面常量中的一个:
AP_MPMQ_MAX_DAEMON_USED = 1 # 到目前为止使用的最大数目的线程。 AP_MPMQ_IS_THREADED = 2 # MPM 可以执行线程。 AP_MPMQ_IS_FORKED = 3 # MPM可以做分支。 AP_MPMQ_HARD_LIMIT_DAEMONS = 4 # 已经编译的最多的线程。 AP_MPMQ_HARD_LIMIT_THREADS = 5 # The compiled max # threads AP_MPMQ_MAX_THREADS = 6 # 执行的最大线程数。 AP_MPMQ_MIN_SPARE_DAEMONS = 7 # 最小空闲后台线程数。 AP_MPMQ_MIN_SPARE_THREADS = 8 # 最小空闲线程数。 AP_MPMQ_MAX_SPARE_DAEMONS = 9 # 最大空闲后台线程数。 AP_MPMQ_MAX_SPARE_THREADS = 10 # 最大空闲线程数 AP_MPMQ_MAX_REQUESTS_DAEMON= 11 # 每个进程的最大请求数 AP_MPMQ_MAX_DAEMONS = 12 # 设置后台进程运行的最大数。
举个例子:
if apache.mpm_query(apache.AP_MPMQ_IS_THREADED):
# 一些代码
else:
# 执行另一些代码
4.5.2 table对象(mp_table)
class table([mapping-or-sequence ])
返回一个空的type mp_table.对象。查看4.5.2查看table对象的描述。 序列化映射会为table提供初始值。
Table对象是apache APR table的一个包裹类。Table对象的行为非常象字典。(包括python2.2版本新加的一些特色,比如支持操作者等等),但有下面的不同:
1. 键和值必须都是字符串。
2. 键看起来好象没有状态。
3. 允许相同的键(看下面的add()方法),如果有一个键有好几个值的话,那么它们就被存放在一个列表中。 Apache使用的大多数信息存储在table中,例如,req.headers_in和req.headers_out。
Mod_python提供的所有tables实际上映射了apache的内部构造。所以改变了python的table也就潜在的改变了apache的table。在类似于字典的行为的基础上,table对象还增加了一个方法:
add(key, val)
addl)允许创建重复的键,这对于多头是非常有用的,比如 Set-Cookie: 被请求。 Add()方法是3.0版本中新加的。
4.5.3 request对象
request对象是映射到Apache request_rec结构。当一个处理器被调用的时候,经常只传递一个单独的参数
request对象。
你可以通过动态的给它增加属性来达到在处理器之间传递属性的目的。
Reuqest的方法:
Add_common_vars()
调用 Ap_add_common_vars()方法,在调用这个方法以后,req.subprocess_env 会包括很多CGI的信息。
Add_handler(htype, handler[, dir ])
允许动态的进行处理器的注册。htype
4.8 Session—会话管理
Sessio模块提供给对象在请求之间穿越并保持持久状态的能力。
这个模块包括BaseSession类,它并不是直接来使用的(它对于存储 session来说没有任何作用).还有一个DbmSession类,它使用dbm来储存会话。
BaseSession类也提供了会话锁,它可以跨越进程和线程。
第五章 Apache配置文件指示
5.1 请求指示
5.1.1 Python*Handler指示的语法
所有的请求指示语法都是下面这样:
Python*Handler handler [handler ...] [ | .ext [.ext ...] ]
[Handler]的位置只接受一个参数-request对象,ext的位置是文件的扩展名。
多个处理器可以在一行中指定,在这种情况下他们会从左到右被连续调用。同一个处理器可以被指定好多次,这都遵循一个结果-在列表中的多个处理器会被连续调用,从第一个知道最后一个。如果其中一个处理器没有返回apache.OK,那么它后面的那些处理器就会停止执行。
这个处理器列表后面可以跟着 一个“|”符号和文件扩展名,这样可以限定处理器只处理这个列表中标明的文件。 一个处理器有下面的语法:
module[::object]
module的位置应该是一个模块的完整名称(包的”.”也可以),可选择的参数object代表的是在模块内的
一个对象的名字。Object也可以包含有”.”符号,在这种情况下它被从左到右执行,在解决的过程,如果mod_python发现一个类,那么它会尝试
实例化这个类,并传递一个request对象给它。
如果object没有指定,它会默认的指定为handler的名称,并去掉”python”,举个例子,PythonAuthenHandler的默认的object应该是authenhandler.
例如:
PythonAuthzHandler mypackage.mymodule::checkallowed
查看更多关于处理器的信息,请阅读处理器概览
小提示:选择”::”是出于性能上的考虑