首页 > programming, python > python paste.deploy 探索

python paste.deploy 探索

简介

Paste Deployment 是一个用来查找和配置 WSGI 应用的系统. 只需要利用它提供的一个 简单的入口 loadapp, 就可以从配置文件或者 python EGG 里加载 WSGI 应用. 用户 所要做的, 仅仅是调用 loadapp 接口, 不需要暴露程序内部的实现细节.

配置文件说明

配置文件是 INI 格式 的, 被分为不同的段, 每个段的段名由 [类型:名字] 组成, 类型包括以下几种:

app

app 接受的参数(environ, start_response), app 需要完成的任务是响应 envrion 中的请求, 准备好响应头和消息体, 然后交给 start_response 处理, 并返回响应消息体. 这个比较好理解, 就是 WSGI 直接调用 app 指定的对象.

filter

filter 是过滤器, 和 python 中的装饰器是一个路子, 它接受一个 app 对象作为参数, 返回一个封装后的 app. 在一般的应用中, 可能在运行到最后一个 app 时候, 前面需要先 处理其他 filter, 如果在处理某一个 filter 的时候, 某些判断条件未通过, filter 有权直接返回, 不交由下面的模块继续处理, 比如认证未通过等.

filter-app

也是一个过滤器, 在某个应用只需要一个过滤器的时候, 一般用该类型, 它需要一个 next 字段指定这个 filter 应用到哪个应用上.

pipeline

如果需要使用多个 filter 过滤一个应用, 需要使用 pipeline 的方式, 他的配置就是一个 名为 pipeline 的 key, value 是以多个 filter和最后的一个应用结尾的列表: 如

[pipeline:main]
pipeline = filter1 filter2 filter3 app

composite

用来完成将将一个请求调度定向(dispatched)到多个(多种)应用上. 比如应用有 v1, v2 的版本, 就可以用 composite 来做调度. composite 其实像是 app, 但是实际上是由 多个应用组成. 比如下面是 openstack glance 的使用

[composite:rootapp]
paste.composite_factory = glance.api:root_app_factory
/: apiversions
/v1: apiv1app
/v2: apiv2app

使用示例

app

下面的例子展示了一个只有名为 blog 的app, 配置文件指定了 app_factory 为 Blog.factory, 这个工厂函数会创建一个 Blog 的实例, 当然你需要实现该类的 __call__ 方法, 这样 wsgi 就会在适当的使用调用你完成你想要做的.

# file: app.ini
[app:blog]
paste.app_factory = app:Blog.factory
# use = call:app:blog_app
#!/usr/bin/python
#encoding: utf-8
 
# file: app.py
 
import os
import eventlet
from eventlet import wsgi, listen
from paste import deploy
 
cfg_file = 'app.ini'
 
 
class Blog(object):
    def __init__(self):
        pass
 
    # 工厂函数, ini 配置文件中指定的值会找到这个函数来创建该 app 的实例
    @classmethod
    def factory(cls, global_conf, **local_conf):
        return cls()
 
    # python 的对象调用机制, 简单的来讲就是 python 中对象分为可调用的和不可调用的
    # 有 __call__ 方法的可以调用, 这样 ini 中指定的配置项的值调用的时候就直接调用到
    # 这个函数了
    def __call__(self, environ, start_response):
        # start_response 把用户传递的 HTTP status 和 headers 记录然后返回
        start_response('200 OK', {("Content-type", "text/plain")})
        return 'welcome to my blog\n'
blog_app = Blog.factory
 
if __name__ == '__main__':
    socket = listen(('0.0.0.0', 8000))
    # paste 提供的入口函数
    app = deploy.loadapp('config:%s' % os.path.abspath(cfg_file), 'blog')
    server = eventlet.spawn(wsgi.server, socket, app)
    server.wait()

一个复杂的例子

下面是一个复杂的例子, 先来看他的 ini 文件:

[composite:wiki]
use = egg:Paste#urlmap
/: home
/v1: wikiv1
 
[filter-app:home]
paste.filter_factory = app:WikiFilter.factory
next = homeapp
 
[app:homeapp]
paste.app_factory = app:Wiki.factory
 
 
[pipeline:wikiv1]
pipeline = logip logmethod v1
 
[filter:logip]
paste.filter_factory = app:LogIPFilter.factory
 
[filter:logmethod]
paste.filter_factory = app:LogMethodFilter.factory
 
[app:v1]
paste.app_factory = app:V1.factory

app 名为 wiki, 首先它是一个 composite 的类型, 根据前面说的(egg.Paste 这个是一个 app), 它会根据 url 对应不同的应用, 比如如果是 http://localhost/ 它就到 home, http://localhost/v1, 就到 wikiv1

home 是只有一个 filter 过滤的 app, filter 对应的工厂函数为 Wiki.factory, 它过滤后 在调用 homeapp, 这个 homeapp 就和前面的 blog一样只是一个简单的 app

wikiv1 是一个 pipeline, 它经过 logip(记录对方 IP), logmethod(记录 HTTP medhod) 这两个 filter, 然后到 v1 的 app

相关代码如下:

class Wiki(object):
    def __init__(self):
        pass
 
    @classmethod
    def factory(cls, global_conf, **local_conf):
        return cls()
 
    def __call__(self, environ, start_response):
        start_response('200 OK', {("Content-type", "text/plain")})
        return 'welcome to my wiki\n'
 
 
class Middleware(object):
    def __init__(self, app):
        self.app = app
 
    @classmethod
    def factory(cls, global_conf, **kwargs):
        def filter(app):
            return cls(app)
        return filter
 
 
class WikiFilter(Middleware):
    def __init__(self, app):
        super(WikiFilter, self).__init__(app)
 
    def __call__(self, environ, start_response):
        req = Request(environ)
        if req.method == 'PUT':
            start_response('200 OK', {("Content-type", "text/plain")})
            return 'Bad request\n'
        else:
            return self.app(environ, start_response)
 
 
class LogIPFilter(Middleware):
    def __init__(self, app):
        super(LogIPFilter, self).__init__(app)
 
    def __call__(self, environ, start_response):
        print 'request IP is: %s' % environ['REMOTE_ADDR']
        return self.app(environ, start_response)
 
 
class LogMethodFilter(Middleware):
    def __init__(self, app):
        super(LogMethodFilter, self).__init__(app)
 
    def __call__(self, environ, start_response):
        print 'request method is: %s' % environ['REQUEST_METHOD']
        return self.app(environ, start_response)
 
 
class V1(object):
    @classmethod
    def factory(cls, global_conf, **local_conf):
        return cls()
 
    def __call__(self, environ, start_response):
        start_response('200 OK', {("Content-type", "text/plain")})
        return 'welcome to my V1 wiki\n'

Resources

完整的代码点击 这里, 配置文件请点击 这里

分类: programming, python 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.