http://blog.lightcloud.cn/?p=183
Pasted Deployment是一套查找和配置WSGI应用和服务的系统。向WSGI application(WSGI应用,可调用对象)的用户提 供了一个简单的函数(loadapp)来从配置文件或是python的egg包中加载WSGI application。调用WSGI application的程序只要求你的程序提供一个简单的单独的访问入口,所以application发布者并不需要暴露application的内部 的实现细节。
这样的好处是系统管理员可以很容易的安装和管理,并不需要掌握python,或是WSGI Application的细节和它的container。 目前Paste Deployment不需要依赖Paste项目中的其他部分,可以单独做为一个安装包。 Paste Deploy使用MIT协议发布。
Paste Deploy已经发布了1.0版本,是个活跃的开发项目。在1.0版本中,做了很多工作使它向后兼容,会在必要时包含对一些不再推荐使用的功能的警告。
名词解释
- application:应用,符合WSGI规范的可调用对象,接受参数(environ, start_response),调用start_response返回状态和HTTP消息头,返回结果作为消息体。
- filter:过滤器,可调用对象,类似python中的装饰器(decorator),接受一个application对象作为参数,返回一个封装后的application。
- app_factory:可调用对象,接受参数(global_config, **local_conf),返回application对象。
- composite_factory:可调用对象,接受参数(loader, global_config, **local_conf),loader有几个方法, get_app用于获取wsgi_app, get_filter用于加载filter, 返回application对象。
- filter_factory:可调用对象,接受参数(global_config, **local_conf),返回filter对象
Paste Deploy最主要的一方面是它定义的entry points(像paste.app_factory)
paste.deploy用的最多的是通过它的配置文件。
配置文件分为不同的段(section),Paste Deply关心的几个段(section)都有前缀,像 app:main 还有 filter:errors————冒号后面的部分是段的名字(name of section),冒号前的部分给出段的类型(type of section)。其他的段(section)会被忽略。
paste配置文件就是普通的INI文件格式,可以使用缩进的多行来作为一行的延续。开头的”#”井号或是”;”分号后面是注释。 格式像下面这样定义
1
2
3
4
|
[section_name]
key = value
another key = a long value
that extends over multiple lines
|
所有的值都是字符串(不需要用引号括起来),每个段的名字和段中值的名字都是大小写敏感的,可以包含标点符号和空白符,最后键和值两边的空白符都会被去掉。以空白符开始的行被认为是上一行的延续。
通常会添加两个段,一个叫做main的应用段([app:main])和一个服务段([server:main])。[composite:...]表示转发到多个应用的东西。
下面是一个典型的配置文件使用了paste.urlmap来组合多个应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini
[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs
[filter-app:blog]
use = egg:Authentication#auth
next = blogapp
roles = admin
htpasswd = /home/me/users.htpasswd
[app:blogapp]
use = egg:BlogApp
database = sqlite:/home/me/blog.db
[app:wiki]
use = call:mywiki.main:application
database = sqlite:/home/me/wiki.db
|
下面解释详细解释每一段:
1
2
3
4
5
6
|
[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini
|
这是一个composite段,它会转发请求到其他的应用。use = egg:Paste#urlmap 表示使用paste包中的叫做urlmap的 composite application。urlmap 是一个通用的 composite application —— 它把请求转到其他的应用。这里有几个应用,像”home”, “blog”, “wiki”和”config:cms.ini”。最后一个只是指向同一个文件夹下的cms.ini配置文件。
接下来:
1
2
3
|
[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs
|
egg:Paste#static 是另一个简单的应用,处理非动态文件。需要参数 document_root, 指定静态文件根目录。还可以使用变量替换,变量是在[DEFAULT]段中定义的,定义替换变量作用标签 %(var_name)s。上面用到的特殊变量 %(here)s 指这个配置文件所在的文件夹。
接下来,看看
1
2
3
4
5
6
7
8
9
|
[filter-app:blog]
use = egg:Authentication#auth
next = blogapp
roles = admin
htpasswd = /home/me/users.htpasswd
[app:blogapp]
use = egg:BlogApp
database = sqlite:/home/me/blog.db
|
[filter-app:blog]段是用到应用上的过滤器(filter)。next字段指定了这个filter应用到哪个应用上。
第二个段只是指向BlogApp应用,database会做为参数传递到应用中。
最后
1
2
3
|
[app:wiki]
use = call:mywiki.main:application
database = sqlite:/home/me/wiki.db
|
这个段和前一个段很像,一个重要的不同点是它直接指向mysiki.main模块中的应用而不是egg包中的入口点(entry point)。指向哪个应用定义由冒号分隔的两部分组成:冒号左边的模块的名字,右边是从模块中引用的应用。
以上就是最常用的功能。
配置文件中的DEFAULT段的选项会被作为全局变量。在其他段中的选项做为局部变量传到相应的application中,如果局部选项中的键和 DEFAULT中的键一样,局部选项中键会被忽略(global_config和local_config中都没有),如果想覆盖全局变量中的键值,使用set字段,如
1
2
3
4
5
6
7
8
|
[DEFAULT]
name = TEST
version = 1.0
[app:test]
use = call:test:test
version = 2.0
set name = test
|
使用loadapp生成test应用时,会调用test.test,和调用app_factory一样,传入global_config和 local_config,global_config中name=test,被局部配置覆盖了,global_config中version还是 1.0,局部配置中没有version。
使用call时是会转成相应的factory(app转成app_factory,filter转成 filter_factory,composite转成composite_factory)但是调用的方法名,只能是函数,不能是类的方法,虽然在代码 注释中写可以导入如”obj.method”,但是其在引入可调用对象时是直接使用getattr会报错,还是不能有点”.”
示例代码如下:test_paste.ini
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[DEFAULT]
name = TEST
version = 1.0
[app:test1]
paste.app_factory = test_wsgi:TestApp.factory
version = 2.0
set name = test
[app:test2]
use = call:test_wsgi:test2_factory
version = 2.0
set name = test
[composite:test3]
use = call:test_wsgi:composite_factory
/ = test
/name = Tony NIU
|
test_wsgi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import
os
import
eventlet
from
eventlet
import
wsgi, listen
from
paste
import
deploy
_path
=
os.path.dirname(__file__)
#application object
class
TestApp(
object
):
@classmethod
def
factory(
cls
, global_conf,
*
*
local_conf):
return
cls
()
def
__call__(
self
, env, start_response):
#print env
start_response(
"200 OK"
, ())
return
[
"welcome"
]
def
test_app(
self
, env, start_response):
start_response(
"200 OK"
, ())
return
[
"welcome"
]
test2_factory
=
TestApp.factory
def
composite_factory(loader, global_config,
*
*
local_config):
#print env, start_response, args, kwargs
return
TestApp()
def
filter_factory(global_config,
*
*
local_config):
import
time
def
fiter(app):
print
"now is %f"
%
time.time()
return
app
return
filter
def
main():
f
=
"config:%s"
%
os.path.join(_path,
"test_paste.ini"
)
host
=
"0.0.0.0"
server_list
=
[(
"test1"
,
8001
),
(
"test2"
,
8002
),
(
"test3"
,
8003
),]
servers
=
[]
for
app_name, port
in
server_list:
_socket
=
listen((host, port))
app
=
deploy.loadapp(f, app_name)
print
"%s is starting"
%
app_name
servers.append(eventlet.spawn(wsgi.server, _socket, app))
for
server
in
servers:
server.wait()
if
__name__
=
=
'__main__'
:
main()
|
参考:http://pythonpaste.org/deploy/index.html