今天将分析 Python 里的一个开源项目:Paste Deployment(后面简写为 PasteDeploy),它是一套框架,可以用于发现并配置 WSGI 应用程序和 WSGI Servers。对于 WSGI 应用程序的使用者而言,它可以提供单一的入口(loadpp)来从配置文件或者 Python Egg 中加载 WSGI 应用程序,而对于 WSGI 应用程序的开发者而言,只需要按照 PasteDeploy 的 协议,为应用程序提供相应的 entry_points,PasteDeploy 即可发现这些 WSGI 应用程序,WSGI 应用程序的实现细节里也无需感知应用程序的用户。
PasteDeploy 最核心的一点,就是它预先定义好了接口(可调用的 Python 对象),只要应用程序开发者提供了这些接口(即遵守 PasteDeploy的协议),就能被 PasteDeploy 识别。
PasteDeploy 目前并不依赖 Paste,它可以作为一个独立的包进行发布。
基本使用方法
PasteDeploy 最主要的用途就是加载 WSGI 应用程序,可以通过如下方式加载应用程序:
1 | from paste.deploy import loadapp |
loadapp 函数从指定的 URI 中加载应用程序,目前支持两种 URI 格式:config: 以及 egg:。如果配置文件中定义了多个 app,可以通过 name 关键字参数来指定要加载的 app。如果没有指定 app 的 name,同时 loadapp 中的 URI 参数不包含也 #,则默认加载的 app 为 main。
PasteDeploy 中共有 3 种类型的对象,分别是 application、filter、和 server。因此除了使用 loadapp 来加载app 外,还可以使用 loadfilter 来加载 filter、使用 loadserver 来加载 server。
PasteDeploy 配置文件
可以通过配置文件来使用 PasteDeploy。PasteDeploy 的配置文件采用 INI 文件格式,包含不同的 sections,每一个 section 都有一个前缀,例如 app:main、filter:errors。前缀的格式为:type:name。
典型的配置文件如下所示:
1 | [composite:main] |
application object
可以在配置文件中定义多个 application 对象,每个应用程序都在自己的 section 中。application 也可以根据他们的功能,划分为不同的种类。
Applications
最基本的 application 对象,section 前缀以 app: 为开头,后面是该应用程序的名称(如果没有指定则为 main)。有两种方式来指定该应用程序的 Python 代码:
- 通过 URI 或名称指定
1 | [app:myapp] |
- 显示指定 Python 代码,此时配置项采用
协议作为键
1 | [app:myapp] |
section 下除了 use 关键字之外的其他配置,都会作为关键字参数传给工厂代码。另外 [DEFAULT] section 下的配置是全局配置,全局配置会传递给所有 application,而且可以在 section 下通过 set key=value 来覆盖全局配置。
Composite Applications
Composite Applications 组合应用程序由一系列应用程序构成,最典型的例子是 URL mapper,这样根据 URL 挂载不同的应用程序:
1 | [composite:main] |
这里就是定义了一个名为的 main 的 composite 应用,它使用的是 Paste 包中的 urlmap 应用。urlmap 是 Paste 提供的一套通用 composite 应用,根据根据 URL 将请求分发到不同的 WSGI 应用,这里访问 / 将被映射到 mainapp,访问 /files 将映射到 staticapp。
同样也可以通过协议来指定 Composite Applications,协议名称为 paste.composite_factory。
Filter Composition
Filter Composition 可以对于 application 可以应用 filter,然后再执行 application。有多种方式来创建 Filter Composition application。
- 使用
filter-with
1 | [app:main] |
- 使用
[filter-app:...]section:在该 section 中通过use定义了一个 filter,同时通过next指定该filter应用的app
1 | [filter-app:blog] |
- 使用
[pipeline:...]section:通过 pipeline 可以定义一系列的filter,以及这些filter最终应用的app
1 | [pipeline:main] |
可以通过协议 paste.filter_app_factory 来指定 Filter Composition。
filter object
接下来介绍 filter,filter 是过滤器,它接受应用程序作为参数,然后返回一个新的应用程序,新的应用程序包含了 filter 的过滤逻辑。使用 [filter:...] section 可以创建 filter:
1 | [filter:printdebug] |
也可以通过协议 paste.filter_factory 来指定 filter。
server object
server 是指 WSGI 服务器,可以为 WSGI 应用程序提供服务。使用 [server:...] 可以指定 server 对象。另外通过协议 paste.server_factory 和 paste.server_runner 也可以指定 server。
加载对象
如上所述,有两种方式来加载对象,一种是通过 egg URI 的方式,另外一种就是通过 protocol 的方式。
egg: URIs
通过 setuptools 安装的软件包,都可以指定 entry_points。如果我们编写的 WSGI 软件包,想被 PasteDeploy 识别,可以按照如下方式编写 setup():
1 | setup( |
这里通过 paste.app_factory entry_points 组定义了 2 个 app_factory,这样就可以被 PasteDeploy 识别,然后可以在 PasteDeploy 的配置文件中可以通过 egg:MyApp#main 和 egg:MyApp#ob2 来加载这两个 application。如果想提供其他的 object,只需要将 entry_points 的 group 设置为相应的 protocol 名称即可。
Defining Factories
除了 entry_points 的方式,还可以直接指定应用程序工厂的代码,即采用类似于 paste.app_factory = myapp.modulename:app_factory 的形式。应用程序工厂的代码需要遵循 PasteDeploy 的协议,即工厂代码需要是可调用对象(参数和返回值需要符合预期)。PasteDeploy 定义了如下协议:
- paste.app_factory:它接受全局配置、本地配置作为参数,返回一个 WSGI 应用
例如:
1 | def app_factory(global_config, **local_conf): |
- paste.composite_factory:接受三个参数:loader、全局配置、本地配置。loader 对象提供一系列方法:get_app(name_or_uri, global_conf=None) 根据名称返回 WSGI 应用程序、get_filter 和 get_server 也是类似。
例如:
1 | def composite_factory(loader, global_config, **local_conf): |
如下是一个更复杂的例子,它实现了一个 pipeline application:
1 | def pipeline_factory(loader, global_config, pipeline): |
通过如下方式来使用这个 pipeline:
1 | [composite:main] |
- paste.filter_factory:类似于
app_facotry,但是返回的是 filters。Filter 也是可调用对象,但是接受一个 WSGI 应用程序作为参数,返回一个包含过滤逻辑的 app。
例如:
1 | def auth_filter_factory(global_conf, req_usernames): |
-
paste.filter_app_factory:类似于
paste.filter_factory,但是还接受wsgi_app作为参数,并返回一个 WSGI 应用程序 -
paste.server_factory:接受的参数和
app_factory相同,但是返回一个 server。该 server 接受一个 WSGI application 作为参数,提供相应的服务
例如:
1 | def server_factory(global_conf, host, port): |
- paste.server_runner:类似于 paste.server_factory,除了第一个参数是
wsgi_app,同时 server 也会立即运行