轉自:http://bingotree.cn/?p=100html
1.Paste Deploy的一個組件,可是並不依賴於Paste的其它組件。其能夠當作是一個獨立的包。其主要用於經過一個配置文件完成WSGI應用和服務器的構建。對於一個不怎麼了解Python的人來講,只要知道了這個配置文件如何編寫,那麼也能寫出一個符合WSGI標準的應用。這樣說可能仍是有點抽象,下面看了例子就清楚了。python
2.安裝PasteDeployweb
1
2
3
|
[root@OS_DEV ~]# pip install PasteDeploy
Requirement already satisfied (use --upgrade to upgrade): PasteDeploy in /usr/lib/python2.6/site-packages
Cleaning up...
|
3.配置文件
這個是官網的一個配置文件:sql
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
|
首先,配置文件分爲多個section,每一個section的名字的格式是TYPE:NAME,每一個section中參數的格式通常是KEY = VALUE。咱們分別來看看各類TYPE:
3.1 TYPE = composite服務器
1
2
3
4
5
6
|
[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini
|
composite這個類型的section會的把具體的URL請求分配到VALUE對應的section中的APP上去。use代表具體的分配方 法,換句話說這裏的KEY = VALUE是egg:Paste#urlmap這個Python模塊的參數,我的猜想egg:Paste#urlmap的實現應該相似於:app
1
2
3
|
if
(URL
=
=
"/"
) call(home_app)
if
(URL
=
=
"/blog"
) call(wiki)
if
(URL
=
=
"/cms"
) call(config:cms.ini)
|
3.2 TYPE = appui
1
2
3
|
[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs
|
一個app就是一個具體的WSGI的應用。具體調用那個python module中的app則由use來指定。use有不少類型,好比:this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[app:myapp]
use = config:another_config_file.ini#app_name
# or any URI:
[app:myotherapp]
use = egg:MyApp
# or a callable from a module:
[app:mythirdapp]
use = call:my.project:myapplication
# or even another section:
[app:mylastapp]
use = myotherapp
|
其實也不難,大概看看就知道每種的含義了。config:another_config_file.ini#app_name表示從另一個 config.ini文件中找app。egg:MyApp是從python蛋中找。call:my.project:myapplication是直接調 用某個模塊中的myapplication。use = myotherapp則是在其它section找app。url
另外還有一種調用方法:spa
1
2
|
[app:myapp]
paste.app_factory = myapp.modulename:app_factory
|
這裏直接指定了使用myapp.modulename中的app_factory做爲咱們的app。paste.app_factory代表代表了咱們的app_factory所使用的格式,app_factory的相關格式、參數、返回值咱們下面會講到。
另外在section中的其它KEY = VALUE對則是會的被當成參數傳到咱們的app中。
3.3 TYPE = filter-app
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就是一個過濾,也就是說一個請求過來後,會的先走filter-app中的use指定的app,若是那個app過濾了這個 request,那麼這個request就不會發送到next指定的app中去進行下一步處理了。若是沒有過濾,則會發送給next指定的app。這個 filter-app雖然有過濾的名字,但其實也不必定要作過濾這檔子事情,能夠用來記錄些日誌啥的,好比每次來個請求就log些東西,而後再轉給後面的 app去處理。fiter-app必需要有next,這個和filter不同
3.4 TYPE = filter
1
2
3
4
5
6
7
|
[app:main]
use = egg:MyEgg
filter-with = printdebug
[filter:printdebug]
use = egg:Paste#printdebug
# and you could have another filter-with here, and so on...
|
和filter-app差很少,可是沒有next
3.5 TYPE = pipeline
1
2
3
4
5
|
[pipeline:main]
pipeline = filter1 egg:FilterEgg#filter2 filter3 app
[filter:filter1]
...
|
pipeline就是簡化了filter-app,否則你想,若是我有十個filter,那不是要寫十個filter-app,而後用next連起 來?因此經過pipeline,我就能夠把這些filter都連起來寫在一行,很方便。但要注意的是這些filter須要有一個app做爲結尾。
4.基本用法
如何使用呢?很簡單。咱們都說了,這個Paste Deploy就是爲了從配置文件生成一個WSGI的APP,因此只要這樣調用就好了:
1
2
|
from
paste.deploy
import
loadapp
wsgi_app
=
loadapp(
'config:/path/to/config.ini'
)
|
5.全局section
這個很好理解,看個例子:
1
2
3
4
5
6
|
[DEFAULT]
admin_email = webmaster@example.com
[app:main]
use = ...
set admin_email = bob@example.com
|
main這個app裏會有一個參數admin_email傳遞進去,默認就是DEFAULT中的那個,固然能夠經過set來覆蓋。
6.具體的factory格式
PasteDeploy自身有不少的factory,這些factory對普通的WSGI標準作了個封裝,讓用的時候好用一些。咱們來看看對應的格式:
6.1 paste.app_factory
1
2
|
def
app_factory(global_config,
*
*
local_conf):
return
wsgi_app
|
這個比較簡單,寫的時候就是隻要寫一個咱們須要的WSGI_APP就好了。
6.2 paste.composite_factory
1
2
|
def
composite_factory(loader, global_config,
*
*
local_conf):
return
wsgi_app
|
這個其實就是比上面多了個loader方法,loader有get_app之類的方法。
6.3 paste.filter_factory
相似於paste.app_factory。和app_factory的區別在於paste.filter_factory返回的是一個filter。filter到底是個啥?其實很簡單,無非就是個if-else,好比:
1
2
|
if ("一切ok,沒啥能夠過濾的") return NEXT_WSGI_APP(XXX)
else return "直接返回WSGI的標準返回報文,request請求鏈在這裏就斷了"
|
這個是個形象的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def
auth_filter_factory(global_conf, req_usernames):
# space-separated list of usernames:
req_usernames
=
req_usernames.split()
def
filter
(app):
return
AuthFilter(app, req_usernames)
return
filter
class
AuthFilter(
object
):
def
__init__(
self
, app, req_usernames):
self
.app
=
app
self
.req_usernames
=
req_usernames
def
__call__(
self
, environ, start_response):
if
environ.get(
'REMOTE_USER'
)
in
self
.req_usernames:
return
self
.app(environ, start_response)
start_response(
'403 Forbidden'
, [(
'Content-type'
,
'text/html'
)])
return
[
'You are forbidden to view this resource'
]
|
換句話說,auth_filter_factory須要有一個filter的方法,假設有兩個filter,那麼Paste Deploy可能會生成這樣的代碼:
1
2
|
APP = filter01(WSGI_APP) = AuthFilter01
APP02 = filter02(AuthFilter01) = AuthFilter02
|
實際上調用的時候變成:
APP02(XXX,XXX),也就是AuthFilter02(XXX,XXX),而AuthFilter02會的先作過濾,若是過濾成功,那麼直接由 他返回咱們的HTTP報文,不然調用AuthFilter01,AuthFilter01也會作個過濾,若是過濾成功,則返回HTTP報文,不然調用最後 的WSGI_APP。
所以若是寫在pipeline中,那麼順序就應該是:filter02 filter01 WSGI_APP
6.4 paste.filter_app_factory
這個的話就是把paste.filter_factory從function變成了class,原理是同樣的。
6.5 paste.server_factory
生成一個WSGI標準的SERVER:
1
2
3
4
5
6
|
def
server_factory(global_conf, host, port):
port
=
int
(port)
def
serve(app):
s
=
Server(app, host
=
host, port
=
port)
s.serve_forever()
return
serve
|
這裏的Server能夠自由選擇。
7.總結
說了這麼多,來個總結吧。若是說我有一個pipeline是這個樣子滴:
1
|
pipeline = filter01 filter02 app
|
filter01對應的是TEST:filter01_factory
filter02對應的是TEST:filter02_factory
app對應的是TEST:app_factory
相關的代碼能夠是:
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
|
def
filter01_factory(global_conf, XXX):
def
filter
(app):
return
Filter01(app)
return
filter
class
Filter01(
object
):
def
__init__(
self
, app,):
self
.app
=
app
def
__call__(
self
, environ, start_response):
if
"知足某個條件"
:
return
self
.app(environ, start_response)
start_response(
'403 Forbidden'
, [(
'Content-type'
,
'text/html'
)])
return
[
'You are forbidden to view this resource'
]
def
filter02_factory(global_conf, XXX):
def
filter
(app):
return
Filter02(app)
return
filter
class
Filter02(
object
):
def
__init__(
self
, app,):
self
.app
=
app
def
__call__(
self
, environ, start_response):
if
"知足某個條件"
:
return
self
.app(environ, start_response)
start_response(
'403 Forbidden'
, [(
'Content-type'
,
'text/html'
)])
return
[
'You are forbidden to view this resource'
]
def
app_factory(global_config,
*
*
local_conf):
return
WSGI_APP
#一個真正幹活的的WSGI APP,符合WSGI的標準
|
那麼根據上面的總結,paste會的生成以下的代碼:
1
2
3
4
5
6
7
8
|
WSGI_APP
=
app_factory(XXX)
FILTER01
=
filter01_factory(XXX)
FILTER02
=
filter02_factory(XXX)
CALLABLE_WSGI_APP
=
FILTER02(FILTER01(WSGI_APP))
#實際的請求格式會的是CALLABLE_WSGI_APP(XXX,XXX),這裏的CALLABLE_WSGI_APP實際上變成了Filter02
s
=
Server(CALLABLE_WSGI_APP, host
=
host, port
=
port)
s.serve_forever()
|