display_code()
返回HTML格式的PSP原始代码。如下是PSP的模板机制:
模板文件:
<html>
<!-- This is a simple psp template called template.html-->
<h1>Hello, <%=what%>!</h1>
</html>
处理器代码如下:
from mod_python import apache,psp
def handler(req):
template=psp.PSP(req,filename='template.html')
template.run({'what':'world'})
return apache.OK
class PSPInstance()
PSP代码中全局变量psp对象所属的类。对象由内部负责实例化,所以__init__接口是没有文档的。
set_error_page(filename)
设置一个psp页面来处理抛出的异常。如果用绝对路径,将会添加到文档根目录,或者被假设为文件与当前页面在同一路径下。错误页会收到一个附加的变量exception,是一个三元素元组,由sys.exc_info()返回。
apply_data(object[,**kw])
这个方法调用可执行对象object,传递表单参数data作为关键字参数,并返回结果。
redirect(location[,permanent=0])
这个方法重定向浏览器到位置location。如果permanent是True,则MOVED_PERMANENTLY将会发送(不同于MOVED_TEMPORARILY)到客户端。
注意:重定向仅可在任何数据发送到客户端之前调用,调用它的Python代码必须在整个页面的顶端调用。否则会发生IOError异常。
例子:
<%
# note that the '<' above is the first byte of the page!
psp.redirect('http://www.modpython.org')
%>
另外,psp模块还提供如下函数:
parse(filename[,dir])
此函数打开文件filename,读取和转换后返回Python代码。
如果制定了dir参数,最终(ultimate)用于转换的文件名由dir和filename构成。并且include标志也会被指定为相关路径。注意这只是简单的串连,dir之后不会自动加入路径分隔符。
parsestring(string)
同上,只是转换字符串string中的psp代码到Python代码。
完成...
第五章 apache配置标记
5.1请求处理器
5.1.1Python*Handler标记语法
所有的请求处理器标记具有如下语法:
Python*Handler handler [handler ...] [|.ext [.ext ...]]
handler是一个可接收请求对象的可调用对象,.ext是文件扩展名。
多个处理器可以写在同一行,并按顺序调用,处理器列表中的每个处理器都会执行。如果序列中某个处理器返回apache.OK以外的东西则处理序列会被中止。
在处理器列表后可用"|"分割开其后的文件扩展名列表,这将会严格的按照扩展名来执行。这个功能用于穿越各个阶段的处理器工作。
handler有如下语法:
module[::object]
module是完整的模块名(允许含有句点的包符号(notation)),可选的object是模块内的对象。
对象也可以包含句点,并且从左至右分解。在判别(resolution)过程中,如果mod_python遇到(encounter)了<class>类型的对象,将会试图实例化并且传递一个请求对象作为唯一的参数。
如果没有指定对象,将会使用缺省的将处理器标志全部转换成小写,之后去除开头的"python"单词,成为调用的对象名。比如PythonAuthenHandler将是authenhandler。
例子:
PythonAuthzHandler mypackage.mymodule::checkallowed
更多关于处理器的信息,参考具体的处理器。
细节:选用"::"是为了提高性能。当使用python模块内部对象时,首先导入模块。按照句点'.'简单的分隔,将会复杂的分析每一个可能的对象,包括包、模块和类等等。使用"::"符号(公认的(adminttedly)非Python风格符号),将会直接指定对象来获得较高的性能。
5.1.2PythonPostReadRequestHandler
语法:Python*Handler Syntax
上下文:server config, virtual host
Override:非None
模块:mod_python.c
这个处理器在读取请求但是没有处理任何阶段之前调用。用于判别提交头字段信息。
注意:当处理这个阶段的请求时,URI还没有被翻译成路径名,所以不可以在apache的<Directory>、<Location>、<File>配置标志和.htaccess文件中指定。只可以在主配置文件中指定,代码也是在主解释器中执行的。因为这个阶段发生在判别内容类型之前(比如不区分一个Python程序和.gif图片),所以Python例程将会处理整个服务器上所有的请求,所以应该优先(priority)考虑性能的问题。
下面的处理器被apache按照阶段处理。
5.1.3PythonTransHandler
语法:Python*Handler Syntax
上下文:server config, virtual host
Override:非None
模块:mod_python.c
这个处理器给出从URI翻译到实际(actual)文件名的机会(opportunity),发生在服务器缺省规则(如目录别名等)之前。
注意:此阶段发生时URI还没有转换成路径名,所以不可以用在apache的<Directory>、<Location>、<File>标志和.htaccess文件中。只可以放在主配置文件中,代码也是运行在主解释器中的。
5.1.4PythonHeaderParserHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个处理器在请求头解析时给出一定的处理,早于其他处理序列。
5.1.5PythonInitHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这是在请求处理阶段第一个调用的处理器。在.htaccess和目录中都可以使用。
这个处理器是两个不同的处理器的别名。当写入主配置文件时而不在任何目录标签之中时,它代指PostReadRequestHandler。当在某个目录中时(不允许使用PostReadRequestHandler的地方),它代指PythonHeaderParserHandler。
这个主意是从mod_perl中借用的。
5.1.6PythonAccessHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个例程用于检查特定模块的限制(module-specific restriction)对某些资源的访问。
比如,可以用于严格的限制某些IP地址的访问,可以返回HTTP_FORBIDDEN或者其他禁止访问。
5.1.7PythonAuthenHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
用于检查用户请求的认证信息,可以查询数据库中的信息决定认证结果。
使用req.user获取(obtain)用户名。用req.get_basic_auth_pw()获取密码。
返回apache.OK代表认证通过。返回apache.HTTP_UNAUTHORIZED将会使大多数浏览器弹出密码提示框。返回apache.HTTP_FORBIDDEN将会告知浏览器无需再显示密码框并显示验证失败。HTTP_FORBIDDEN也可用于验证成功时,但是这个用户无权访问特定URL。
一个验证处理器的例子:
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
注意:req.get_basic_auth_pw()的调用必须优先于req.user值。因为在调用req.get_basic_auth.pw()之前apache不会解码验证信息。
5.1.8PythonAuthzHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个模块在PythonAuthenHandler之后调用,用于检查特定用户是否有权访问特定资源。在PythonAuthenHandler之前执行会出错。(But more often than not it is done right in the PythonAuthenHandler)。
5.1.9PythonTypeHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个例程用于确定(determine)和设置各种(various)文档类型信息,有如Content-Type(经过req->content_type),语言,及其他等(et cetera)。
5.1.10PythonFixupHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个例程用于修正头字段。在所有内容处理器(content-handler)之前调用。
5.1.11PythonHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
主要的请求处理器。很多应用只提供这一个处理器即可。
5.1.12PythonLogHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
这个例程在特定模块写入日志时调用。
5.1.13PythonCleanupHandler
语法:Python*Handler Syntax
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
最后一个处理器,在apache销毁请求对象时调用。
不像其他处理器,这个处理器的返回值会被忽略。任何错误都被直接记录到日志,并且在即使PythonDebug为On时,也不会把错误发送到客户端。
这个处理器不能作为req.add_handler()函数的有效参数。动态注册清理行为使用req.register_cleanup()。
一旦清理开始了,就没有必要注册更多。在这个处理器中调用req.register_cleanup()是没有用的。
通过这个标志注册的清理行为运行在通过req.register_cleanup()注册的清理之后。
5.2过滤器
5.2.1PythonInputFilter
语法:PythonInputFilter handler name
上下文:server config
模块:mod_python.c
注册一个名为name的输入过滤器handler。handler是模块名,并可以使用"::"加上可调用对象。如果可调用对象省略(omit)了,则默认为"inputfilter"。name是过滤器的注册名,按照惯例(by convention)过滤器名应该在所有的cap(?)中。
激活过滤器需要用AddOutputFilter标志。
5.3连接处理器
5.3.1PythonConnectionHandler
语法:PythonConnectionHandler handler
上下文:server config
模块:mod_python.c
用handler来处理连接。handler将会传递唯一的连接对象作为参数。
handler是一个模块并允许使用"::"指定可调用对象。如果可调用对象省略则缺省为"connectionhandler"。
5.4其他标志
5.4.1PythonEnablePdb
语法:PythonEnablePdb {On,Off}
缺省:PythonEnablePdb Off
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
当为On时,mod_python将会在Python调试器pdb中运行,使用pdb.runcall()函数。
因为PDB是交互式工具,所以从命令行启动httpd加上参数-DONE_PROCESS来使用这个标志。当进入某个处理器时,将会从pdb提示符看到代码和测试变量。
5.4.2PythonDebug
语法:PythonDebug {On,Off}
缺省:PythonDebug Off
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
一般时,Python无法处理的错误将会写入日志。当指定PythonDebug On标志时,输出会发送到客户端(同日志一样),除非错误是写入时的IOError,那种情况还是写入到错误日志。
这个标志在开发时非常有用。但是不要在最终运行环境中使用,会暴露一些信息影响安全。
5.4.3PythonImport
语法:PythonImport module interpreter_name
上下文:server config
模块:mod_python.c
告知服务器给特定解释器导入特定模块。用于初始化任务,而非在请求处理时的事情,比如初始化数据库连接。
导入发生在子进程初始化(initialization)时,所以模块是在每个子进程产生(spawn)时真正的导入。
注意:在导入时,配置还没有被完全读取,所以其他标志包括PythonInterpreter等因为还没有导入而不会产生效果(effect)。出于这个限制(limitation),解释器必须知道,并必须对比名字(name)来确保并发的请求依赖于(rely)这个操作的执行。如果不确定所在解释器的名字,可以从请求对象的interpreter成员获得。
参考多解释器。
5.4.4PythonInterpPerDirectory
语法:PythonInterpPerDirectory {On,Off}
缺省:PythonInterpPerDirectory Off
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
指示(instruct)mod_python使用请求文件名的路径作为子解释器的名字,而不是使用服务器名字。这意味着不同目录将会执行不同的解释器,而不是象缺省的规则(policy)那样所有的脚本都在同一个虚拟服务器上的相同子解释器上运行。
例如,假设(assume)有个目录'/dir/subdir','dir'含有.htaccess文件并且指定了PythonHandler标记。'dir/subdir'并没有.htaccess文件。缺省时,在'/dir'和'dir/subdir'中执行的解释器相同,通过同一个虚拟服务器。含有PythonInterpPerDirectory标志时,将会启动两个不同的解释器,对应每个目录。
注意:在在请求处理的早期阶段,如PostReadRequestHandler和TransHandler,由于URI还没有被解析出来。所以即便是有PythonInterpPerDirectory On标志,所有的代码也都是在主解释器中执行。这也许并不是你想的,但是很不幸没有其他办法。
参考:
4.1节,多解释器
5.4.5PythonInterpPerDirective
语法:PythonInterpPerDirective {On,Off}
缺省:PythonInterpPerDirective Off
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
指定mod_python按照正在作用的Python*Handler标志来命名子解释器。
例如,假设目录'dir/subdir'。'dir'含有.htaccess文件并有PythonHandler标志。'dir/subdir'含有另外的.htaccess文件,并有另外的PythonHandler。缺省时,在'/dir'和'/dir/subdir'中的脚本使用相同的解释器来执行,并在相同的虚拟服务器中。打开了PythonInterpPerDirective标签后,每个标签使用不同的解释器。
参考:4.1节,多解释器
5.4.6PythonInterpreter
语法:PythonInterpreter name
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
强制mod_python给解释器命名为name,覆盖缺省的行为和PythonInterpPerDirectory和PythonInterpPerDirective标签的行为。
这个标签用于强制指定使用不同的子解释器。当用在DocumentRoot时,强制整个服务器使用同一个子解释器。
参考:4.1节,多解释器
5.4.7PythonHandlerModule
语法:PythonHandlerModule module
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
可用于在Python*Handler标签中任选其一(alternative)。指定的模块将会被搜索缺省的函数名,如果找到了则执行。
例如:
PythonAuthenHandler mymodule
PythonHandler mymodule
PythonLogHandler mymodule
可以被简写为:
PythonHandlerModule mymodule
5.4.8PythonAutoReload
语法:PythonAutoReload {On,Off}
缺省:PythonAutoReload On
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
如果设为Off,指定mod_python不需要检查模块文件的修改时间。
缺省时,mod_python检查模块的时间签并决定是否需要重新载入模块,当模块文件的时间签比导入或重新导入的时间签要新的时候会重新导入。这个确保更新的模块会重新导入,这样在每次更改的时候无需重启服务器。
禁用这个功能适用于发布环境,会节省很多时间并有微小的性能提升。
5.4.9PythonOptimize
语法:PythonOptimize {On,Off}
缺省:PythonOptimize Off
上下文:server config
模块:mod_python.c
允许Python优化,同Python -O选项。
5.4.10PythonOption
语法:PythonOption key [value]
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
指定键值对用于在req.get_options()中获得。用来传递配置信息非常有效。如果值被省略,则是用于从配置中移出对应键。
5.4.11PythonPath
语法:PythonPath path
上下文:server config, virtual host, directory, htaccess
Override:非None
模块:mod_python.c
用于设置PythonPath。这个路径必须使用Python的列表符号(notation),比如:
PythonPath "['/usr/local/lib/python2.0','/somedir/dir']"
这里指定的路径会替换路径(path),而不是添加。如果需要只是添加,则用如下:
PythonPath "sys.path+['/mydir']"
mod_python会尝试最小化PythonPath标签指定的目录数量,因为这是非常影响性能的,尤其是在'.htaccess'文件中指定时,每次解析都需要重新导入。mod_python将会记住PythonPath的参数,每次只是比对是否有更改,只是在不同的时候才重新导入。因为这样,在代码改变时不需要依赖于这种方法来重新装入PythonPath的值。
注意:这个标志不能用于安全的量度,因为Python Path的值可以很容易的在脚本中更改。
完成...
第六章 标准处理器
6.1发布处理器
发布处理器是一种快速开发应用程序,避免(avoid)直接书写处理器的好方法。收到Zope中ZPublisher的启发。
6.1.1简介
使用处理器需要书写如下配置:
<Directory /some/path>
SetHandler mod_python
PythonHandler mod_python.publisher
</Directory>
这个处理器允许通过URL在模块中存储函数和变量。例如,如果有如下模块,叫做'hello.py':
""" Publisher example """
def say(req,what="NOTHING"):
return "I am saying %s" % what
则如下路径显示"I am saying NOTHING"
http://www.mysite.com/hello.py/say
如下路径显示"I am saying hello"
http://www.mysite.com/hello.py/say?what=hello
6.1.2发布处理器规则(algorithm)
发布处理器从URI映射到Python变量或可执行对象,返回调用的返回值。
Traversal(?)
发布处理器定位和导入URI指定的模块。模块的位置按照req.filename属性指定。在到如之前,如果有扩展名则丢弃。
如果req.filename为空,则模块名缺省为index。
一旦模块成功导入,则URI剩余的部分成为query字段的开始(a.k.a also known as也叫做PATH_INFO),来在模块里查找对象。发布处理器切断路径,从左到右,每个元素一次的映射到Python对象。
如果URL里未指定path_info,则发布处理器使用缺省的'index'。如果最后一个元素是模块里的一个对象,且它领先与一个目录(也就是没有给出模块名),模块名也会默认到'index'。
当如下情况发生时,转换结束,并返回HTTP_NOT_FOUND到客户端:
任何一个横断(traverse?)对象名以下划线开头。使用下划线保护对象不被web存取。
模块冲突(encounter),发布的对象因为安全原因不可以成为模块。
如果路径中找不到对象,则HTTP_NOT_FOUND也会发送到客户端。
例如,如下的配置:
DocumentRoot /some/dir
<Directory /some/dir>
SetHandler mod_python
PythonHandler mod_python.publisher
</Directory>
如下是脚本'/some/dir/index.py':
def index(req):
return "We are in index()"
def hello(req):
return "We are in hello()"
然后:
http://www.mysite.com/index/index 显示"We are in index()"
http://www.mysite.com/index/ 显示"We are in index()"
http://www.mysite.com/index/hello 显示"We are in hello()"
http://www.mysite.com/hello 显示"We are in hello()"
http://www.mysite.com/spam 显示'404 Not Found'
如果找到了目标对象,且它是可调用对象而不是一个类,则发布处理器会提取一个对象预期的列表。这个列表包含从HTML表单提交数据中的字段。字段值和字段名以字符串匹配并传入可调用对象的参数。任何没有匹配的名字都被默认丢弃,除非目标对象有一个**kwargs风格的参数。未匹配参数传入这个词典中。
如果目标对象不是可调用的或者是一个类,则会返回它创建的字符串到客户端。
发布处理器提供了对模块和函数简单的存取控制。
在每个处理阶段,发布处理器按顺序检查__auth__和__access__属性,还有__auth_realm__属性。
如果__auth__是可调用的,将会被传入三个参数:一个请求对象,字符串包含用户名和密码。如果返回值是False,则将发送HTTP_UNAUTHORIZED到客户端,一般来说还会弹出一个输入密码的对话框。
如果__auth__是一个词典,则用户名和密码按照键值对在词典中尝试匹配。如果不匹配则返回HTTP_UNAUTHORIZED。注意,如果明文将密码存入源代码中是不安全的。
__auth__也可以是常量。在这种情况下,如果为False(也同None、0、""、等等)则返回HTTP_UNAUTHORIZED。
如果存在__auth_realm__字符串,它将会被发送到客户端作为认证区域的提示,通常还显示在密码提示框中。
如果__access__存在并可调用,则传入两个参数:请求对象和用户名字符串。如果返回值为False则发送HTTP_FORBIDDEN到客户端。
如果__access__是一个列表,则用户名将会被尝试匹配。如果用户名不再列表中,则返回HTTP_FORBIDDEN。
类似的__auth__、__access__也可以是常量。
在下例中,仅用户'eggs'使用密码'spam'可以访问函数hello:
__auth_realm__="Members only"
def __auth__(req,user,passwd):
if user=="eggs" and passwd=="spam":
return 1
else:
return 0
def __access__(req,user):
if user=="eggs":
return 1
else:
return 0
def hello(req):
return "hello"
这里还有一个具有相同功能,但使用类似技术:
__auth__realm__="Members only"
__auth__={"eggs":"spam"}
__access__=["eggs"]
def hello(req):
return "hello"
如果函数无法被指定属性,为了保护函数,也可以将__auth__和__access__函数放入这个函数中定义,如:
def sensitive(req): #受保护函数
def __auth__(req,user,password):
if user=="spam" and password=="eggs":
return 1
else:
return 0
#受保护函数的函数体
return 'sensitive information'
注意,这种技术也可用于__auth__或__access__是常量的情况,但是不可以是词典或列表。
__auth__和__access__机制独立于PythonAuthenHandler而存在。它可能使用,例如认证处理器。然后__access__列表用于已经验证过的用户访问具体函数。
注意:为了让mod_python可以访问__auth__,包含它的模块必须首先导入。因此,任何模块级(module-level)的代码在导入__auth__之前,即时__auth__为False,也是可以执行的。为了真正的保护模块,使用另外一种认证技术,比如apache的mod_auth模块或者用mod_python的PythonAuthenHandler处理器。
6.1.3表单数据
在处理参数结束后,发布处理器将会创建一个FieldStorage类的实例。实例的引用存入请求对象的form属性中。
每次请求只能拥有一个FieldStorage类的实例,在使用发布处理器的时候不可以自己实例化FieldStorage,只能使用Request.form代替。
6.2PSP处理器
PSP处理器是处理含有mod_python.psp模块中psp类的。要使用它,只需简单的在httpd的配置中加入:
AddHandler mod_python .psp
PythonHandler mod_python.psp
更多的PSP语法参见4.9节。
如果打开了服务器选项PythonDebug为On,则在URL的末尾添加下划线"_"会显示响应的PSP源代码和对应的Python代码。这对调试是非常有用的。
注意:开启调试选项时,远程用户同样有权查看PSP源码。
6.3CGI处理器
CGI处理器用来在mod_python中模拟CGI环境。
但是注意,在Python级别并不能真正的模拟出一个完整的CGI环境。stdin和stdout是由sys.stdin和sys.stdout来替换(substitute)的。环境变量也被替换为词典。一点类似(implication)是外部程序通过环境变量os.system交互,等。不要指望外部可以看到Python的环境变量,也不要指望读写标准输入输出象真正的CGI环境那样。
这个处理器是为了逐步(step stone)移植(migration)CGI遗留(legacy)下来的代码。不建议大量使用这种方式。因为CGI程序没有打算(intend)过在线程环境中使用(比如请求更换当前目录,就不是线程安全的。这就需要在多线程服务器程序中使用线程锁)。他的实现方式也远不如mod_python中的其他方式。
需要使用它,只需在.htaccess文件中加入如下内容:
SetHandler mod_python
PythonHandler mod_python.cgihandler
在版本2.7中,cgihandler会完全的重新载入来间接(indirectly)的重新导入模块。这是为了保护装入模块列表(sys.modules)来优先(prior)执行CGI脚本,然后在脚本执行后对比导入模块。除了一些__file__属性指向标准Python库的模块外,将会被在sys.modules中删除来强制Python在下次CGI脚本导入时再次装入他们。
如果你不希望如上的行为,可以编辑'cgihandler.py'文件注释掉###代码。
测试表明,cgihandler在多次文件上传时会发生内存泄漏(leak)。而且至今还没有完全解决这个问题。暂时的解决办法是设置apache的MaxRequestsPerChild到一个非零的值。