python Paste Deployment PasteDeploy模块介绍 中文翻译版
官网地址 https://docs.pylonsproject.org/projects/pastedeploy/en/latest/#
翻译时间:2019年3月
PasteDeploy 2.0.1
介绍
Paste Deployment是一个用来发现和配置WSGI应用和服务的系统。对于一个WSGI应用的使用者,可以使用方法loadapp,从一个配置文件或者python egg中,加载出来一个WSGI应用。对于WSGI应用的开发者,它只需要你的应用有一个简单的入口。因此应用的具体实现细节并不会暴露给应用的使用者。
这个设计的结果是,一些系统管理员,在不了解python,或者不了解WSGI应用的详情或者它的容器,也能进行安装和管理。
Paste Deployment现在不要求Paste模块的其他部分,是作为单独的包进行分发的。
想了解Paste Deploy的更新,可以查看更新文件。
Paste Deploy 是在 MIT license下发行的。
状态
译者注:这段看不看不影响使用Paste Deploy
Paste Deploy已经通过了1.0版本。Paste Deploy是一个积极维护的项目。从1.0开始我们将努力向后保持兼容(实际上从1.0很久之前已经开始了,只不过现在明确的表示一下)。有必要时将会包含一些警告信息。主版本将会有一些函数或者入口的改变。
请注意,Paste Deploy最关键的方面是它定义的入口(例如paste.app_factory)。Paste Deploy不是这些入口的唯一使用者。许多扩展可以更好的利用这些入口,来替代使用Paste Deploy来实现。而入口不需要改变。如果需要改变,需要定义新的入口。
安装
首先确保你已经安装了setuptools或者其他能代替的分发工具。对于python3,setuptools是不能安装的,你可以使用pip去安装Paste Deployment,是没有问题的。
pip install PasteDeploy
译者:如果拉取pypi比较慢,可以使用国内源(下面是用阿里的pypi源)
pip install PasteDeploy -i https://mirrors.aliyun.com/pypi/simple/
如果你想追随开发版,你可以
git clone https://github.com/Pylons/pastedeploy
cd pastedeploy
pip install -e .
你将包安装到本地,你可以很简的安装开发版PasteDeploy。
更多下载或者其他信息,可以查看PyPI PasteDeploy的页面。
一个补充的包是Paste Script,想要安装它,可以使用 pip install PasteScript
(或者pip install PasteScript==dev,译者注:这个不是个可执行的命令,意思是让你去装开发版,就上装pastedeploy的开发版一样)。
从用户的角度
下面的部分中,将给出使用Paste Deploy的一些Python API。这个不是用户要用的(但是它对Python开发者有用,对设置测试装置也是有用的)。
和Paste Deploy交互,主要是通过它的配置文件。你主要要做的事情就是提供配置文件。要了解如何提供配置文件,查看命令“paster serve”(译者注:这个是PasteScript这个python包里的命令不是Paste或者PasteDeploy里的!上面有提如何安装PasteScript),地址。
配置文件
一个配置文件有不同的部分(译者注:英文词叫section,后面就不翻译这个词了)。Paste Deploy只关心那些有前缀的sections,像app:main或者filter:errors——其中冒号(:)后面的是这个section的名称,后面的是这个section的类型。其他的sections(译者:就是那些不符合格式的,没有冒号的分割的)将会被忽略。
配置文件的格式,是简单的INI格式:name=value。你可以通过缩进后续的行来扩展这些值。可以通过#做注释。
通常你有一或两sections,名字叫做main,一个是应用section([app:mian]),一个是服务section([server:main])。[composite:…] 代表着你有多个应用(下面有例子)。
下面是一个典型的配置文件,它将展示如果装载多个应用。
[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
下面我将详细的解释每一个部分
[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini
这是一个composite section。代表着它将分发请求给其他的应用。
use = egg:Paste#urlmap。代表要用Paste这模块里的urlmap去处理分发请求。urlmap是一个比较常用的用于分发请求的应用。它可以根据请求路径的前缀,将请求交给不同的应用去处理。这里有些应用就是home,blog,wike,config:cms.ini。最后一个配置是去找同级目录下的另一个配置文件cms.ini。
译者注:这个功能就是,根据根据不同的URL让不同的应用去处理,和nginx里的location有点像。自己使用的时候,use那部分固定写,其他的根据需求改。这里应用的名字叫做main,在loadapp是如果不指定name,默认就是main。 如果想按照示例写,记得安装Paste模块。
下一步
[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs
use = egg:Paste#static是另一个应用,这个是针对没有动态文件的服务。它需要一个配置项document_root。这里你可以使用变量替换,它将从[DEFAULT](区分大小写!)这部分获取,格式就像%(var_name)s。这有个特殊的变量 %(here)s指的是包含配置文件的目录;你应该用它替代相对的文件名(一些依赖当前目录,可以根据服务的运行而改变)。
然后
[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] 是想实现一个带着过滤器的应用。过滤后将交给next(引用next这个section)指向的应用处理。egg:Authentication#auth实际上是不存在,但是可以想象它可以让人们登录并验证权限。
最后一个section只是对一个应用程序的引用,该应用可能需要pip install BlogApp(译者注:这是一个模块名称,并不真实存在,可以是其他处理的模块名),并传递给它一点配置(database。译者注:需要哪些配置名,是由具体模块定。实例中可能是BlogApp模块定义的需要database这个参数)。
最后
[app:wiki]
use = call:mywiki.main:application
database = sqlite:/home/me/wiki.db
这一个section与前一section相似,但有一个重要区别。它不是egg中的入口点,而是直接引用mywiki.main模块中的application变量。这种引用由两部分组成,用冒号隔开。左边是模块的全名,右边是变量的路径,作为一个python表达式去相对的包含一个模块。
所以,这是你们想要的大部分的功能。
基本用法
你们使用Paste Deploy基本的方式是去加载一个WSGI的应用。很多python的框架现在都支持WSGI,因此这些框架写的应用都是可用的。
主要的方法是paste.deploy.loadapp,这个可以使用一个URI加载一个应用,你可以使用它像:
from paste.deploy import loadapp
wsgi_app = loadapp('config:/path/to/config.ini')
现在有两种格式是支持的:config:和egg:
config URI
URIs that being with config: refer to configuration files. These filenames can be relative if you pass the relative_to keyword argument to loadapp().
URIs需要以config:(译者注:目前是不区分大小写的,CONFIG也可以,最好还是老实的小写为好)开头,加上引用配置文件。如果你设置了loadapp的relative_to,这些配置文件可以是相对路径的。译者注:就是如果loadapp(“config:config.ini”, relative_to="/etc/msg")这么调用的,配置文件的位置就是/etc/msg/config.ini。
Filenames are never considered relative to the current working directory, as that is an unpredictable location. Generally when a URI has a context it will be seen as relative to that context; for example, if you have a config: URI inside another configuration file, the path is considered relative to the directory that contains that configuration file.
特别注意:文件名永远不要认为是相对于当前工作目录的,因为这是一个不可预测的位置。通常当一个URI有上下文史,都是相对于上下文的。例如你有一个config:URI包含了另一个配置文件路径,这个路径可以考虑是相对于配置文件的目录。(译者注:配置文件A里引用了,配置文件B。在引用B的时候,可以使用相对于A所在目录,写相对路径)
Config Format 配置格式
配置文件的格式是INI格式的,一个简单的格式,像下面这样
[section_name]
key = value
another key = a long value
that extends over multiple lines
所有的值都是字符串类型的。键名和每节的名称是大小写敏感的,可能包含标点符号和空格(但键名和值要去掉前面和后面的空格)。多行值可以前面加空格继续。
可以使用#开头,作为注释(推荐),也可以使用分号(;)开头作为注释。
Applications 应用配置
You can define multiple applications in a single file; each application goes in its own section. Even if you have just one application, you must put it in a section.
Each section name defining an application should be prefixed with app:. The “main” section (when just defining one application) would go in [app:main] or just [app].
There’s two ways to indicate the Python code for the application. The first is to refer to another URI or name:
你可以在一个配置文件中定义多个应用。每一个应用要有自己的section。即使只有一个应用,你也必须把它放到一节中。
在定义一个应用时,每一个section的名称应该以app:开头。解析入口(当只定义了应用时)是[app:main]或者[app]。
有两种方式应用程序的python代码,第一种是引用另一个URI或者应用的名称。
[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
起初上面看起来是没有意义的,只是一种方式去指向另一个位置。但是, 除了从该位置加载应用程序外, 你还可以添加或更改配置。
另一种定义应用的方式是精确的指向一段python代码
[app:myapp]
paste.app_factory = myapp.modulename:app_factory
你必须显示的指定协议(示例中为paste.app_factory),值应该是引用一些东西。在这个示例中,模块myapp.modulename将会被加载,然后从模块中检索app_factory 对象。
查看Defining Factories获得更多关于该协议的内容。
配置
配置通过一些keys完成,除了use(或者协议名),其他在那节中的键值对都将作为有关键字的参数传给factory。他们可能像:
[app:blog]
use = egg:MyBlog
database = mysql://localhost/blogdb
blogname = This Is My Blog!
你可以重写他们在另一节,像:
[app:otherblog]
use = blog
blogname = The other face of my blog
这种方式一些设置可以被定义在一般的配置文件中(如果你设置了use = config:other_config_file)或者你可以发布更多的(更专业的)应用通过添加一节。
全局配置
通常很多应用共享相同的配置。虽然你可以通过其他配置节点和覆盖一些值来实现这一点,通常你希望一组不同的值来实现。而且通常应用不能接受特别的配置参数;有了全局配置你可以做一些事就像“如果一个应用想知道管理员的邮箱,就是它”。
应用会被单独传递全局配置信息,因此需要它们明确的从中取值;通常在没有本地配置传入时,全局配置作为默认值。
全局配置将被应用到配置文件中的每一个应用中,全局配置应该是一个特殊的节命名为[DEFAULT]。你可以在本地覆盖全局配置,像:
[DEFAULT]
admin_email = [email protected]
[app:main]
use = ...
set admin_email = [email protected]
也就是说在键值前面使用set。
Composite应用 综合应用
“Composite”应用和普通应用在行为上很相似,但是它是由其他应用组合成的。一个例子就是URL映射,你可以不同应用对应不同的路径。就像:
[composite:main]
use = egg:Paste#urlmap
/ = mainapp
/files = staticapp
[app:mainapp]
use = egg:MyApp
[app:staticapp]
use = egg:Paste#static
document_root = /path/to/docroot
The composite application “main” is just like any other application from the outside (you load it with loadapp for instance), but it has access to other applications defined in the configuration file.
这个composite应用mian像其他外部应用(你可以使用loadapp加载它成为一个实例),但是它访问了定义在其他配置文件中的应用。
其他对象
除了可以添加app:类型的sections,还可以在配置文件中配置filter和server,使用filter:和server:做前缀即可。你可以使用loadserver和loadfilter加载它们。这样的配置同样可以工作。你会获得不同的返回对象。
Filter Composition
有几种方式可以将过滤器应用到应用中。这主要取决于有多少个过滤器,和你想以什么样的顺序应用他们。
第一种方式使用filter-with设置,像:
[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...
此外,有两种特殊的section类型去应用过滤器到你的应用:[filter-app:…]和[pipeline:…]。这两种section定义应用,可以在任何需要一个应用的地方使用。
filter-app 定义了一个过滤器(就像你定义一个filter:的section),然后定义一个特殊的键值next,指向过滤应用到的应用(译者注:就是执行完过滤器后把请求交给哪个应用)。
pipeline: 使用在当你想应用一定数量的过滤器时。它使用一个特殊的配置键pipeline (加上一些你想覆盖的全局配置)。 pipeline对应的值是一个过滤器的列表,列表中最后一个是一个应用,像:
[pipeline:main]
pipeline = filter1 egg:FilterEgg#filter2 filter3 app
[filter:filter1]
# ...
获得配置
如果你想在不创建要给应用的情况下获取配置信息。你可以使用 appconfig(uri) 这个方法,它像loadapp()方法,返回一个可以使用的配置,是一个字典。全局配置和本地配置都会被组合一个字典中,但是你可以单独查看它们其中的一个或者另一个,通过属性.local_conf和.global_conf。
译者:
from paste import deploy
app_config = deploy.appconfig("config:/etc/nova/api-paste.ini", name="osapi_compute") # 我测试时我的配置文件没有一个名字叫main的section,所以需要指定name。
# app_config可以当做一个字典用,里面有全局的配置和本地配置,是合并到一块
app_config.global_conf # 这个可以单独获取出全局的配置,这里至少包括here配置文件所在目录,__file__配置文件的路径。
app_config.local_conf # 这个可以单独取出本地配置
egg:URIs
Python Eggs are a distribution and installation format produced by setuptools and distribute that adds metadata to a normal Python package (among other things).
Python Eggs是由setuptools和distribute生成的一种分发和安装格式,它将元数据添加到一个普通的Python包中(除其他外)。
你使用它们时并不需要完全了解Eggs。如果有<python:distuils>setup.py脚本,只需更改:
from distutils.core import setup
改为
from setuptools import setup
现在当你安装这个包时,就会比当成一个egg安装。
关于Egg最重要的一部分是,它有一个specification。这是由分发的名称(setup())中的name参数),和指定一个特殊的版本号。你可以有一个Egg命名为MyApp,或者使用MyApp==0.1来指定特殊的版本号。
第二个入口点。这些是对你包里具有特殊名称和特殊协议的一个Python对象的引用。“协议”只是一种表达方式,我们将使用某些参数调用它,并期待它返回一个特殊的值。稍后我们会讨论的更多。
一个重要的部分就是我们如何定义入口。你将添加一个参数到setup()像:
setup(
name='MyApp',
# ...
entry_points={
'paste.app_factory': [
'main=myapp.mymodule:app_factory',
'ob2=myapp.mymodule:ob_factory'],
},
)
这里定义了两个应用main和ob2。你可以引用他们像egg:MyApp#main (或者egg:MyApp,因为main是默认值)和gg:MyApp#ob2。
这些值指明了引用的对象。main会加载myapp.mymodule里的一个名字为app_factory的对象。
这里没有方式,添加配置引用Egg里的对象。
Defining Factories 定义工厂
这允许你指向一些工厂(遵循我们提到的特定的协议),但是除了为了你的应用创建工厂,其他是没多大用的。
有几个协议: paste.app_factory, paste.composite_factory, paste.filter_factory, and lastly paste.server_factory. 它们都需要是可调用的 (像函数,方法,或者类)。
paste.app_factory
应用是最常见的,你可以这么定义:
def app_factory(global_config, **local_conf):
return wsgi_app
global_config是一个字典, 本地配置作为带键的参数传入。这个方法返回一个WSGI应用对象。
paste.composite_factory
Composites只是稍微有些复杂
def composite_factory(loader, global_config, **local_conf):
return wsgi_app
loader这个参数是一个对象,它有几个有趣的方法。get_app(name_or_uri, global_conf=None)根据给的名字返回一个WSGI对象。get_filter和get_server是同样的工作方式。
一个更有趣的例子是Composites可以多一些事,例如,考虑一个Pipeline的应用:
def pipeline_factory(loader, global_config, pipeline):
# space-separated list of filter and app names:
pipeline = pipeline.split()
filters = [loader.get_filter(n) for n in pipeline[:-1]]
app = loader.get_app(pipeline[-1])
filters.reverse() # apply in reverse order!
for filter in filters:
app = filter(app)
return app
我们可以这么使用:
[composite:main]
use = <pipeline_factory_uri>
pipeline = egg:Paste#printdebug session myapp
[filter:session]
use = egg:Paste#session
store = memory
[app:myapp]
use = egg:MyApp
paste.filter_factory
filter工厂和应用工厂一样(相同的签名),只不过需要返回一个filter。filter是可以被调用的,而且只能有一个WSGI的应用的参数,返回一个过滤后版本的应用。
下面是一个过滤器的例子,它检查一个CGI的变量REMOTE_USER是否设置,创建一个真正的简单的身份验证过滤器:
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']
paste.filter_app_factory
这个是和paste.filter_factory非常相似的。它也需要一个wsgi_app作为参数,返回一个WSGI的应用。因此你可以更改上面的代码,像:
class AuthFilter(object):
def __init__(self, app, global_conf, req_usernames):
# ...
AuthFilter将作为一个paste.filter_app_factory提供服务(这个例子中,req_usernames是必须存在于本地配置的一个键)。
paste.server_factory
这个和应用,过滤器有相同的签名。但是返回的是一个server。
一个server可以被调用的,传入一个wsgi应用作为参数。它这个应用提供服务。
一个例子可能像:
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
服务的实现留给用户
paste.server_runner
像paste.server_factory,期待第一个参数传入一个wsg_app,然后server应该立刻运行。
上一篇: Python wsgi 简介
下一篇: JavaScript 学习记录(二)