zoukankan      html  css  js  c++  java
  • 如何使用Paste.Deploy

    转自:http://bingotree.cn/?p=100

    1.Paste Deploy的一个组件,但是并不依赖于Paste的其它组件。其可以看成是一个独立的包。其主要用于通过一个配置文件完成WSGI应用和服务器的构建。对于一个不怎么了解Python的人来说,只要知道了这个配置文件如何编写,那么也能写出一个符合WSGI标准的应用。这样说可能还是有点抽象,下面看了例子就清楚了。

    2.安装PasteDeploy

    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.配置文件
    这个是官网的一个配置文件:

    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的实现应该类似于:

    1
    2
    3
    if (URL == "/") call(home_app)
    if (URL == "/blog") call(wiki)
    if (URL == "/cms") call(config:cms.ini)

    3.2 TYPE = app

    1
    2
    3
    [app:home]
    use = egg:Paste#static
    document_root = %(here)s/htdocs

    一个app就是一个具体的WSGI的应用。具体调用那个python module中的app则由use来指定。use有很多类型,比如:

    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。

    另外还有一种调用方法:

    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()
  • 相关阅读:
    APP-Android:APK
    软件-版本控制:VCS(版本控制系统)
    协议-网络-安全协议-SSH(安全外壳协议):百科
    un-解决方案-BIM:百科
    MySQL:常用语句
    rsync+inotify-tools文件实时同步
    Java实现 Leetcode 169 求众数
    Java实现 Leetcode 169 求众数
    Java实现 LeetCode 137 只出现一次的数字
    Java实现 LeetCode 137 只出现一次的数字
  • 原文地址:https://www.cnblogs.com/zmlctt/p/4208924.html
Copyright © 2011-2022 走看看