`

Python Paste笔记

阅读更多

1 简介及安装

Paste Deployment是一种机制,通过loadapp函数和一个配置文件或者egg包来载入WSGI应用。安装很简单,如下两种方式:

$ sudo pip install PasteDeploy

或者可以从github上进行源码安装

$ hg clone http://bitbucket.org/ianb/pastedeploy $ cd pastedeploy
$ sudo python setup.py develop

2 配置文件Config Flie

一个配置文件后缀为ini,内容被分为很多段(section),PD只关心带有前缀的段,比如[app:main]或者[filter:errors],总的来说,一个section的标识就是[type:name],不是这种类型的section将会被忽略。

一个section的内容是以键=值来标示的。#是一个注释。在段的定义中,有以下几类:

  • [app:main]:定义WSGI应用,main表示只有一个应用,有多个应用的话main改为应用名字

  • [server:main]:定义WSGI的一个server。

  • [composite:xxx]:表示需要将一个请求调度定向(dispatched)到多个,或者多种应用上。以下是一个简单的例子,例子中,使用了composite,通过urlmap来实现载入多应用。

  • [fliter:]:定义“过滤器”,将应用进行进一步的封装。

  • [DEFAULT]:定义一些默认变量的值。

以下是一个例子:

[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#authnext= 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

下面会进行分段的讲解

2.1 composite

[composite:main]use= egg:Paste#urlmap/= home /blog = blog /wiki = wiki /cms = config:cms.ini

这是一个composite段,表示这将会根据一些条件将web请求调度到不同的应用。use = egg:Paste#urlmap表示我们奖使用Pasteegg包中urlmap来实现composite,这一个段(urlmap)可以算是一个通用的composite程序了。根据web请求的path的前缀进行一个到应用的映射(map)。这些被映射的程序就包括blog,home,wiki,config:cms.ini(映射到了另外一个配置文件,PD再根据这个文件进行载入)

2.2 App type1

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

app是一个callable object,接受的参数(environ,start_response),这是paste系统交给application的,符合WSGI规范的参数. app需要完成的任务是响应envrion中的请求,准备好响应头和消息体,然后交给start_response处理,并返回响应消息体。egg:Paste#static也是Paste包中的一个简单程序,它只处理静态文件。它需要一个配置文件document_root,后面的值可以是一个变量,形式为%(var)s相应的值应该在[DEFAULT]字段指明以便Paste读取。比如:

[app:test]use= egg:Paste#static document_root =%(path)s/htdocs [DEFAULT] path =/etc/test

2.3 fliter

filter是一个callable object,其唯一参数是(app),这是WSGI的application对象,filter需要完成的工作是将application包装成另一个application(“过滤”),并返回这个包装后的application。

[filter-app:blog]use= egg:Authentication#authnext= blogapp
roles = admin
htpasswd =/home/me/users.htpasswd [app:blogapp]use= egg:BlogApp database = sqlite:/home/me/blog.db

[filter-app:blog]fliter-app字段表明你希望对某个应用进行包装,需要包装的应用通过next指明(表明在下一个段中),这个字段的意思就是,在正式调用blogapp之前,我会调用egg:Authentication#auth进行一个用户的验证,随后才会调用blogapp进行处理。后面的[app:blogapp]则是定义了blogapp,并指明了需要的database参数。

2.4 App type2

[app:wiki]use= call:mywiki.main:application
database = sqlite:/home/me/wiki.db

这个段和之前的app段定义类似,不同的是对于wiki这个应用,我们没有使用egg包,而是直接对mywiki.main这个模块中的application对象使用了call方法。python,中一切皆对象,作为WSGI app的可以是一个函数,一个类,或者一个实例,使用call的话,相应的函数,类,实例中必须实现call()方法。此类app的格式用冒号分割: call(表示使用call方法):模块的完成路径名字:应用变量的完整名字

3 基本使用

PD的主要使用就是通过读取配置文件载入WSGI应用。如下:

from paste.deploy import loadapp
wsgi_app = loadapp('config:/path/to/config.ini')

注意,这里需要指明绝对路径。

4 更多关于配置文件

4.1 App

单个配置文件中可以定义多个应用个,每个应用有自己独立的段。应用的定义以[app:name]的格式,[app:main]表示只有一个应用。应用的定义支持以下五种格式:

[app:myapp]use= config:another_config_file.ini#app_name#使用另外一个配置文件[app:myotherapp]use= egg:MyApp#使用egg包中的内容[app:mythirdapp]use= call:my.project:myapplication #使用模块中的callable对象[app:mylastapp]use= myotherapp #使用另外一个section[app:myfacapp] paste.app_factory = myapp.modulename:app_factory #使用工厂函数

其中,最后一种方式,将一个app指向了某些python代码。此模式下,必须执行app协议,以app_factory表示,后面的值需要import的东西,在这个例子中myapp.modulename被载入,并从其中取得了app_factory的实例。

app_factory是一个callable object,其接受的参数是一些关于application的配置信息:(global_conf,**kwargs),global_conf是在ini文件中default section中定义的一系列key-value对,而**kwargs,即一些本地配置,是在ini文件中,app:xxx section中定义的一系列key-value对。app_factory返回值是一个application对象

在app的配置中,use参数以后配置就算结束了。其余的键值参数将会作为参数,传递到factory中,如下:

[app:blog]use= egg:MyBlog database = mysql://localhost/blogdb #这是参数 blogname =ThisIsMyBlog!#这是参数

4.2 全局配置

全局配置主要是用于多个应用共用一些变量,这些变量我们规定放在段[DEFAULT]中,如果需要覆盖,可以在自己的app中重新定义,如下:

[DEFAULT] admin_email = webmaster@example.com [app:main]use=...set admin_email = bob@example.com

4.3 composite app

composite是一个运行着像是app,但是实际上是由多个应用组成的。urlmap就是composite app的一个例子,url不同的path对应了不同的应用。如下:

[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

在loadapp函数的执行中,composite app被实例化,它同时还会访问配置文件中定义的其他应用。

4.4 app定义高级用法

在app段中,你可以定义fliters和servers,通过fliter:和server: PD通过loadserver和loadfilter函数进行调用,工作机制都一样,返回不同的对象。

4.4.1 filter composition

应用filter的方式很多,重要的是看你filter的数量和组织形式。下面会一一介绍应用fliter的几种方式:

1.使用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...

2.使用fliter-app

[fliter-app:printdebug]use= egg:Pastenext= main [app:main]use= egg:MyEgg

3.使用pipeline

当使用多个filter的时候需要使用pipeline的方式,它需要提供一个key参数pipeline,后面的值是一个列表,最后以应用结尾。如下:

[pipeline:main] pipeline = filter1 egg:FilterEgg#filter2 filter3 app[filter:filter1]...

假设在ini文件中, 某条pipeline的顺序是filter1, filter2, filter3,app, 那么,最终运行的app_real是这样组织的: app_real = filter1(filter2(filter3(app)))

在app真正被调用的过程中,filter1.__call__(environ,start_response)被首先调用,若某种检查未通过,filter1做出反应;否则交给filter2.__call__(environ,start_response)进一步处理,若某种检查未通过,filter2做出反应,中断链条,否则交给filter3.__call__(environ,start_response)处理,若filter3的某种检查都通过了,最后交给app.__call__(environ,start_response)进行处理。

4.5 读取配置文件

如果希望在不创建应用的情况下得到配置文件,可以使用appconfig(uri)函数,将会以字典形式返回使用的配置。这个字典包括了全局很本地的配置信息,所以可以通过属性方法获得相应的attributes (.local_conf and .global_conf)

5 其他

5.1 如何引用Egg包

egg是python的一个包,pip easy_install等都是安装egg包的方式。关注egg包要注意: +某一egg包是有标准说明的

python setup.py name +有entry point,不用太在意,这个只是说明调用程序的参数。

5.2 定义factory函数

工厂函数的定义还是遵循之前提到的应用的协议。目前,用于工厂函数的协议有以下:

*paste.app_factory

*paste.composite_factory

*paste.filter_factory

*paste.server_factory

所有的这些都希望有一个含有__call__方法的(函数,方法,类)。

1.paste.app_factory

def app_factory(global_config,**local_conf):return wsgi_app

global_config是一个字典,而local_conf则是关键字参数。返回一个wsgi_app(含有call方法。)

2.paste.composite_factory`

def composite_factory(loader, global_config,**local_conf):return wsgi_app

loader是一个对象,有几个有趣的方法,get_app(name_or_uri, global_conf=None)根据name返回一个wsgi应用,get_filter()和get_server()也是一样。看一个更加复杂的例子,举例一个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

3.paste.filter_factory fliter的工厂函数和app的共产函数类似,除了它返回的是一个filter,fliter是一个仅仅把一个wsgi应用作为唯一参数的callable对象,返回一个被filter了的应用。 以下是一个例子,这个filter会检查CGI中REMOTE_USER变量是否存在,并创建一个简单的认证过滤器。

def auth_filter_factory(global_conf, req_usernames):# space-separated list of usernames: req_usernames = req_usernames.split()def filter(app):returnAuthFilter(app, req_usernames)return filter classAuthFilter(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')inself.req_usernames:returnself.app(environ, start_response) start_response('403 Forbidden',[('Content-type','text/html')])return['You are forbidden to view this resource']

4.paste.filter_app_factory 和paste.filter_factory类似,接受一个wsgi应用参数,返回一个WSGI应用,所以如果改变以上代码的:

classAuthFilter(object):def __init__(self, app, global_conf, req_usernames):....

那么,类 AuthFilter就会作为一个filter_app_factory函数使用。

5.paste.server_factory

与以上不同的是,函数返回的是一个server,一个server也是一个callable对象,以一个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

Server的实现用户可以自定义,可以参考python包wsgiref

6.paste.server_runner 与 paste.server_factory类似,不同的是参数格式。

给个例子:

pastedeploylab.ini:

 

[python] view plaincopy
 
  1. [DEFAULT]  
  2. key1=value1  
  3. key2=value2  
  4. key3=values  
  5. [composite:pdl]  
  6. use=egg:Paste#urlmap  
  7. /:root  
  8. /calc:calc  
  9. [pipeline:root]  
  10. pipeline = logrequest showversion  
  11. [pipeline:calc]  
  12. pipeline = logrequest calculator  
  13. [filter:logrequest]  
  14. username = root  
  15. password = root123  
  16. paste.filter_factory = pastedeploylab:LogFilter.factory  
  17. [app:showversion]  
  18. version = 1.0.0  
  19. paste.app_factory = pastedeploylab:ShowVersion.factory  
  20. [app:calculator]  
  21. description = This is an "+-*/" Calculator   
  22. paste.app_factory = pastedeploylab:Calculator.factory  

 

 

pastedeploylab.py

 

[python] view plaincopy
 
  1. ''''' 
  2. Created on 2011-6-12 
  3. @author: Sonic 
  4. '''  
  5. import os  
  6. import webob  
  7. from webob import Request  
  8. from webob import Response  
  9. from paste.deploy import loadapp  
  10. from wsgiref.simple_server import make_server  
  11. #Filter  
  12. class LogFilter():  
  13.     def __init__(self,app):  
  14.         self.app = app  
  15.         pass  
  16.     def __call__(self,environ,start_response):  
  17.         print "filter:LogFilter is called."  
  18.         return self.app(environ,start_response)  
  19.     @classmethod  
  20.     def factory(cls, global_conf, **kwargs):  
  21.         print "in LogFilter.factory", global_conf, kwargs  
  22.         return LogFilter  
  23. class ShowVersion():  
  24.     def __init__(self):  
  25.         pass  
  26.     def __call__(self,environ,start_response):  
  27.         start_response("200 OK",[("Content-type""text/plain")])  
  28.         return ["Paste Deploy LAB: Version = 1.0.0",]  
  29.     @classmethod  
  30.     def factory(cls,global_conf,**kwargs):  
  31.         print "in ShowVersion.factory", global_conf, kwargs  
  32.         return ShowVersion()  
  33. class Calculator():  
  34.     def __init__(self):  
  35.         pass  
  36.       
  37.     def __call__(self,environ,start_response):  
  38.         req = Request(environ)  
  39.         res = Response()  
  40.         res.status = "200 OK"  
  41.         res.content_type = "text/plain"  
  42.         # get operands  
  43.         operator = req.GET.get("operator"None)  
  44.         operand1 = req.GET.get("operand1"None)  
  45.         operand2 = req.GET.get("operand2"None)  
  46.         print req.GET  
  47.         opnd1 = int(operand1)  
  48.         opnd2 = int(operand2)  
  49.         if operator == u'plus':  
  50.             opnd1 = opnd1 + opnd2  
  51.         elif operator == u'minus':  
  52.             opnd1 = opnd1 - opnd2  
  53.         elif operator == u'star':  
  54.             opnd1 = opnd1 * opnd2  
  55.         elif operator == u'slash':  
  56.             opnd1 = opnd1 / opnd2  
  57.         res.body = "%s /nRESULT= %d" % (str(req.GET) , opnd1)  
  58.         return res(environ,start_response)  
  59.     @classmethod  
  60.     def factory(cls,global_conf,**kwargs):  
  61.         print "in Calculator.factory", global_conf, kwargs  
  62.         return Calculator()  
  63. if __name__ == '__main__':  
  64.     configfile="pastedeploylab.ini"  
  65.     appname="pdl"  
  66.     wsgi_app = loadapp("config:%s" % os.path.abspath(configfile), appname)  
  67.     server = make_server('localhost',8080,wsgi_app)  
  68.     server.serve_forever()  
  69.     pass  

 

 

 

 

使用:

http://127.0.0.1:8080/

输出:

Paste Deploy LAB: Version = 1.0.0

http://127.0.0.1:8080/calc?operator=plus&operand1=12&operand2=23

输出:

UnicodeMultiDict([('operator', u'plus'), ('operand1', u'12'), ('operand2', u'23')])

RESULT= 35
====================================================
进一步猜测filter的使用过程:在paste deploy库中应该有类似这样的一段代码对application进行重组包装:
#
# 假设在ini文件中, 某条pipeline的顺序是filter1, filter2, filter3
# app, 那么,最终运行的app_real是这样组织的:
#

app_real = filter1(filter2(filter3(app)))

# 在app真正被调用的过程中,filter1.__call__(environ,start_response)被首先调用,若某种检查未通过,filter1做出反应;否则交给filter2__call__(environ,start_response)进一步处理,若某种检查未通过,filter2做出反应,中断链条,否则交给filter3.__call__(environ,start_response)处理,若filter3的某种检查都通过了,最后交给app.__call__(environ,start_response)进行处理。

 

 

 

 

分享到:
评论

相关推荐

    isl-notebooks:R和Python笔记本编码为“ R中的应用程序进行统计学习简介”

    该存储库包含R和Python笔记本代码,用于。 这是一个练习书的学习场所,包括以Docker化Jupyter笔记本的形式合并和调整其他存储库中的代码(请参阅 ),因此更容易快速运行和试验。 笔记本电脑 统计学习2.3实验-[ ] ...

    python框架bottle使用文档

    Bottle的文档结构清晰,涵盖了用户指南、API参考、教程、配置、请求路由、模板引擎、插件列表、知识库、开发指南、贡献者信息、开发者笔记、FAQ、许可证、Python模块索引等多个部分。对于想要深入了解Bottle框架的...

    OpenCV-Laser-Tracker:在 Python 中使用 OpenCV 跟踪激光指示器

    OpenCV 2.4.5 (Paste cv2.pyd into Python27\Lib\site-packages\) NumPy 1.6.1 (Use installer package for Python 2.7) pySerial 2.7 (Use installer package for Python 2.7) - 可选 快速开始 python track_...

    python图片指定区域替换img.paste函数的使用

    今天用到了img.paste函数,就写篇笔记记录一下,方便回顾。 做人脸检测,产生负样本的时候想把图片中人连部分用背景的某一部分替换掉,然后再随机裁剪产生负样本,这样比随机裁剪的时候避开人脸区域应该实现起来更...

    paste-markdown-main.rar

    这种语言广泛应用于笔记、文档编写、博客和GitHub等平台,因为它允许用户专注于内容而不是排版。 标签为"源码",这提示我们压缩包中可能包含了编程源代码。源码是程序员用高级编程语言编写的未编译程序,可以被...

    bottle-docs

    对于有兴趣为 Bottle 发展做出贡献的开发者来说,可以查看项目的发行说明、贡献者列表和开发者笔记等文档,了解如何参与开发和贡献代码。 ### 总结 Bottle 作为一款微型 Web 框架,以其简洁、高效的特点受到了许多...

    使用python3.5仿微软记事本notepad

    ### 使用Python 3.5 仿制微软记事本Notepad 的关键技术点解析 #### 一、项目背景与概述 本文旨在详细介绍如何使用 Python 3.5 来模拟微软的经典应用——记事本(Notepad)。通过这个项目,读者不仅能够学习到 ...

    PyPI 官网下载 | jupyter_console-6.3.0.tar.gz

    3. **魔法命令**: Jupyter Console继承了IPython的魔法命令,这些命令提供了额外的功能,如 `%timeit` 用于测量代码执行时间, `%prun` 进行代码性能分析,以及 `%paste` 直接粘贴代码块等。 4. **对象信息**: 用户...

    markdown-img-paste:一个可以快速添加附件里的照片到markdown的插件,并且可以设置使用七牛存储的照片

    Markdown是一种轻量级的标记语言,广泛应用于写作、文档编辑、笔记等领域。它通过简单的符号规则,如`#`、`*`、`&gt;`等,使得文本格式化变得简单直观。Markdown-img-paste插件的出现,进一步增强了Markdown处理图片的...

    timmyPaste:基于烧瓶的糊状材料

    笔记 在使用它之前,您需要创建数据库。 您只需要打开python shell并输入以下内容: import lib . db lib . db . Database (). create () 结构 路线: / Show index page. 路线:/新 Create new paste. 路线...

    bottle文档PDF

    - **开发者笔记**:分享开发过程中的一些心得和技术细节。 - **插件开发指南**:为希望开发 Bottle 插件的开发者提供指导。 - **可用插件列表**:再次列出可供使用的插件集合,便于查找。 #### 七、许可证 - **许可...

    SPY课程_spy_

    虽然提供的资料仅是部分笔记,但足以让我们窥见SPY的强大功能和使用技巧。下面我们将深入探讨SPY在IT领域的核心知识点。 一、SPY工具介绍 SPY通常指的是Spyder IDE(Integrated Development Environment),这是一...

    用QRcode生成二维码1

    img.paste(icon, offset, icon) plt.imshow(img) plt.axis('off') plt.show() ``` 这段代码首先创建一个二维码,然后读取Logo图像,调整其大小以适应二维码,并将其放在二维码的中心位置。 总的来说,QRcode库...

    bottle文档

    ##### 3.3 开发者笔记 提供了开发者应该注意的事项和建议。 - **代码风格**: 推荐使用的代码风格和格式。 - **最佳实践**: 在开发过程中遵循的最佳实践。 ##### 3.4 插件开发指南 指导如何为 **Bottle** 开发...

    org-rich-yank:for用于org-mode的RTF文本剪贴板:粘贴到正确模式的#+ BEGIN_SRC块中,并链接到其来源

    例如,如果粘贴的是 Python 代码,它会创建一个 `#+BEGIN_SRC python` 块;如果是 SQL 代码,则会创建 `#+BEGIN_SRC sql` 块。不仅如此,Org-Rich-Yank 还能保留代码的原始格式,包括缩进、颜色和字体样式。 此外,...

Global site tag (gtag.js) - Google Analytics