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()
  • 相关阅读:
    Leetcode Binary Tree Preorder Traversal
    Leetcode Minimum Depth of Binary Tree
    Leetcode 148. Sort List
    Leetcode 61. Rotate List
    Leetcode 86. Partition List
    Leetcode 21. Merge Two Sorted Lists
    Leetcode 143. Reorder List
    J2EE项目应用开发过程中的易错点
    JNDI初认识
    奔腾的代码
  • 原文地址:https://www.cnblogs.com/zmlctt/p/4208924.html
Copyright © 2011-2022 走看看