##language:zh #pragma section-numbers off ##含有章节索引导航的 ZPyUG 文章通用模板 <> ## 默许导航,请保留 <> = 深思WSGI = SEE: '''[[http://groups.google.com/group/python-cn/browse_thread/thread/550ab24904afaada|WSGI的问题集]]''' - python-cn`CPyUG`华蟒用户组 ##startInc == Limodou 问题 == {{{ limodou reply-to python-cn@googlegroups.com to "Python.cn@google" date Thu, Dec 25, 2008 at 10:59 subject [CPyUG:74762] WSGI的问题集 }}} 希望对wsgi了解的介绍下: 1. WSGI的app之间如何传递消息?在environ中添加吗? 1. 多个app之间通过wsgi方式来工作,一般是怎么被调用的?它们的调用如何实现配置化? 1. 对于request, response这样的东西如何在wsgi中处理,如何传递?是每次通过environ直接生成吗?如: {{{ request = Request(environ) }}} 这样做,会不会有效率问题。另外如何处理request中动态添加的属性,丢失吗? == 讨论集锦 == === WSGI的app之间如何传递消息 === Limodou:: {{{ def configure(app): return ErrorHandlerMiddleware( SessionMiddleware( IdentificationMiddleware( AuthenticationMiddleware( UrlParserMiddleware(app)))))) }}} 因此出现了paster,在每个app程序中添加: {{{ def app_factory(global_config, **local_config): return application }}} * 然后通过paster来管理这些app的执行顺序。这样每个app还可以有自已的配置参数。这样就可以理解为什么tg和pylons要使用paster了。不过对我来说的确是理解复杂。甚至象pkg_resources的使用也是最近才一点点理解并开始使用的。 Qiangning Hong:: WSGI的处理模式为 `WSGI Server -> (WSGI Middleware)* -> WSGI Application` * 一次请求只有一个app。middleware和app之间通过environ交换消息。 YoungKing(Zopen.cn):: 每个wsgi app是一个callable对象,传递两个参数,一个是environment ,一个是start_response 函数, * start_response这个函数用来response, * 你在这里设置status和header,最后返回一个iterator(通常是字符串list) 潘俊勇 >> class_name_from_controller_name('with-dashes') 'WithDashes' >>> class_name_from_controller_name('with_underscores') 'WithUnderscores' >>> class_name_from_controller_name('oneword') 'Oneword' """ words = controller_name.replace('-', '_').split('_') return ''.join(w.title() for w in words) class Controller(object): def __call__(self, environ, start_response): c.__dict__.clear() self.environ = environ match = environ['wsgiorg.routing_args'][1] action_name = match['action'].replace('-', '_') action = getattr(self, action_name) kwargs = match.copy() del kwargs['controller'] del kwargs['action'] self.request = webob.Request(environ) self.response = webob.Response(request=self.request) retval = action(**kwargs) if retval: self.response.write(retval) return self.response(environ, start_response) def render(self, template, **kwargs): return self.environ['meilanweb.render'](template, c=c, h=h, **kwargs) def default_template_dir(filepath): here = os.path.dirname(os.path.abspath(filepath)) return os.path.join(here, 'templates') class ErrorDocumentMiddleware(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): try: return self.app(environ, start_response) except webob.exc.WSGIHTTPException, e: e.environ = environ return e(environ, start_response) class AuthenticateMiddleware(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): user = users.get_current_user() if user: environ['REMOTE_USER'] = user try: return self.app(environ, start_response) except webob.exc.HTTPUnauthorized, e: req = webob.Request(environ) url = users.create_login_url(req.url) raise webob.exc.HTTPTemporaryRedirect(location=url) h.create_logout_url = users.create_logout_url }}} == 整体 == Gu Yingbo :: 再啰嗦几句,web框架要统一就要标准化组件之间的接口,但这些已经不是wsgi关注的领域了。 * environ字典对 app 来说只代表最原始的 web请求,它不应该是框架内部组件之间交换数据的场所。 * 框架应该在入口处统一解析一次 environ,并构造一个 request 对象,其后的事情都应该建立在这个request对象的基础之上,而不用再去直接处理environ了, * url dispatcher解析URL的结果也应该存放在这个 request 对象中,而不是 environ 中。 * 像 url dispatcher、auth 这些都应该是框架内的组件,比如说可以有一个框架内的middleware协议,就像 django 的middleware那样,不需要wsgi那么复杂,它们之间就只需要传递 request和response 对象即可,environ、start_response对它们来说都是太底层的东西。 * 而 wsgi middleware 一定要对所有wsgi application透明,这是我理解的 wsgi。 * 标准化web框架应该是在 web框架内部做的事情,而不应该一味地去扩展wsgi,试图把 wsgi 变成一个框架。 * 标准化web框架也不准确,顶多是标准化一个MVC的web框架。 * uliweb没怎么看过,不过我认为limudou完全不必把controller变成wsgi application,应该把框架暴露成一个wsgi app,并且制定一些框架内部组件之间的标准化接口,比如框架内的middleware之间的接口,存取配置的接口,调用模板的接口,使用ORM的接口等。 >>> `Limodou`:好的建议。一个框架中有许多的处理,有些是与wsgi相关的,有些不是。所以只有wsgi统一化也只解决了一部分问题。从看到的一些wsgimiddleware的互访来说,它们其实也约定了许多标准的组件,比如sqlalchemy,webob等。而这些只是组件的选择上的一致性,还没有达到接口的规范统一。不过这是很难的,而且也不知道是否真有意义。 * 现在uliweb基本上就是你说的样子。middleware的标准基本上和django是一样的,不过我增加了一个`__init__`的定义,这样是可以进行初始化的,做一些特殊的事情。 === WSGI不是框架 === {{{ 黄毅 reply-to python-cn@googlegroups.com to python-cn@googlegroups.com date Thu, Dec 25, 2008 at 14:29 subject [CPyUG:74822] Re: WSGI的问题集 }}} * `Limodou`:到是可以考虑把uliweb下的expose改造一下,一方面进行url的注册,另一方面将一个view函数封装成一个wsgi的application的东西。 * 这样的话,哪些东西做成wsgi方式的会有意义呢?因为一个功能,依赖越少可能越好。而象view这样的东西,多数与你的后台,逻辑相关,这些东西比较困难。直接把框架的部分功能做成wsgi方式的有难度,因为框架最具特色的可能就是组件的搭配,配置管理,组件管理和开发方式,思想,而这些东西好象也很难wsgi化。而且有些根本与wsgi的处理无关的,象组件的管理,配置管理之类的。现在想不出来uliweb有哪些可能是独持的可以做成wsgi的东西,因为uliweb能实现的功能,别人基本上都有。而且涉及到wsgi的别人的比uliweb的好象还要更多。 * `黄毅`:我想框架对wsgi友好的含义,一是把自己暴露为一个符合标准wsgi app,二是尽可能抽象出并重用对框架app透明的wsgi middleware,就够了。框架肯定会有很多自己的东西,wsgi并不是一个框架。 Qiangning Hong:: * 可重用的部分做成wsgi middleware的方式就是有意义的。把框架内部的各组件都做成WSGI化的,有助于从中提炼出来可重用的部分。 * 对于Pylons而言,这个框架的`终极思想就是没有框架`,只作为一个WSGI组件的粘合剂存在,每个人用的Pylons都是不一样的。所以他尽可能的把各个组件都WSGI化,用户根据自己的偏好去搭建。 TurboGears 2.0挑选了自己喜欢的一些组件(比如Genshi之类),作为默认组件,就可以在Pylons上建立起一个有特点的框架。 * <<< `Limodou` ~ 其实没有框架就意味着按照自已的方式来组织框架,就是我说的定制化。只不过只有wsgi还无法完成web的处理,我们还需要象template,orm之类的通用模块,而这些与wsgi没什么关系也是一个问题。所以全部wsgi首先是不可能,完全利用wsgi搭建的也只是处理这一块,还有许多的工作要处理。因此我想定制化不一定要在wsgi之上,可以是在一个初步定制化的基础之上的二次定制。 * >>> `Qiangning Hong`~ template的WSGI化: http://pypi.python.org/pypi/wsgiview/ * <<< `Limodou` ~ 这个是把模板的处理进行了包装,可以返回一个wsgi的application。虽然是一种办法,但是我并不认为到处都是wsgi就可以简化web开发,还不如自动对应模板来得简单。应该把这种处理封装到框架内部,如果让一般的web用户来使用,太麻烦,我感觉不好。 * >>> `Qiangning Hong`~ 我也觉得这个没什么大用,因为基本上到模板的时候,app把应该干的事情都已经干完了,渲染模板仅仅是一个函数调用,所以看不出来把渲染这件事情做成wsgi app有什么实质的好处。贴出来只是说一下有人在做这样的事情。 * ORM的WSGI化: http://pypi.python.org/pypi/Alchemyware2.0 * <<< `Limodou` ~ 好象会共享出一个session对象,起到了一个全局化的作用。这也算ORM的wsgi?好象与使用全局模块不是一样吗? * `Qiangning Hong`~ 这个的作用很大,可以打破不同的middleware对SQLAlchemy全局变量调用可能存在的冲突。如果一个middleware对SQLAlchemy的使用有特殊性,可以放在自己的session里,不和其他的发生冲突。 * <<< `黄毅 ` ~我觉得上面这两个路子不对,还是认为不是什么东西都可以通过扩展wsgi来做,template已经turbogears已经有一个叫做 buffet 的标准化接口了吧,我觉得那个已经够了 * Uliweb如果也要走"不可知"的道路,那最后也是这样的模型。所以我提议uliweb多看看pylons,如果能在pylons之上构架框架,会少走很多弯路,产生出来的结果也会更有意义(正如TurboGears项目贡献出来很多WSGI组件一样)。 * <<<`Limodou` ~ uliweb的不可知只是核心部分,它可以被其他人用来构建定制化的东西。 * 所以一般用户使用的应该是可知的。并且我希望uliweb在工作时,会有一个缺省选择,用户可以更换,但不配置时就是缺省的。 * 不过要完全做到不可知很难,因为你的调用接口总要的统一的,但这些可能只是一个局部的标准,别人不一定能遵守或做到,所以要么采用和别人一样的接口,但是仍然可能有不满足的情况。要么自已来定义接口,由用户来针对不同的组件进行适配。 * >>>`Qiangning Hong`~ 这个就是TurboGears的路线。TurboGears原来就是完全自己做,选择自己喜欢的第三方组件搭建框架,和RoR一样。等发展到一定程度了,用的人多了,就开始有替换组件的需求。弄来弄去,最后发现直接在Pylons上搭建,选择一套默认组件,由Pylons去负责组件的可替换性,TurboGears只需要专注在用户的易用性和功能增强上面就行了,这样的方案对整个社区都有利。所以才有了TurboGears * 比如我很看好的uliweb的可重用app模型,如果能够独立成一个wsgi app,就可以直接在Pylons里重用了。这样uliweb就成为一个可重用的wsgi的app的集合,而非一个巨型的大wsgi app。 * <<<`Limodou` ~ 这个想法到是很有意思。不过uliweb的app其实就是一个python的包,它包含了静态信息,配置信息,翻译信息等。uliweb在启动时可以根据配置或动态发现它们,然后合并它们的配置信息,提取url等,而这些处理都是由Dispatcher来完成的。所以这些app并不是一个wsgi的应用。不太明白你希望的重用是什么样子,是Dispatcher还是那些个app? * >>> ``Qiangning Hong`` ~ 如果uliweb能变成一个wsgi middleware,把它往其他的wsgi app上一套,那些app就能像在uliweb里一样变得可重用了,这个世界就很美妙了 :) 不过我对uliweb理解不深,不知道能不能实现。 * <<<`Limodou` ~ UliWeb的app'''是一个包,不是一个真正的对象'''啊,怎么套啊。不是一样的东西 * >>> ``Qiangning Hong`` ~ 给这个包加上 __call__(start_response, environ) 试试? * `Controller本身是wsgi app有一个好处:` * 即在框架内部也可以直接使用wsgi middlewares来进行定制。 * 比如不同的controller使用不同的authentication机制,我就可以用不同的auth middleware去decorate相应的controller。 * 如果只在整个框架外部才暴露一个wsgi app接口,那内部的这些东西都只能用私有的模式实现,重用度就大大降低了 * <<< `黄毅 ` ~ 这样就有一个问题,authentication 组件做成 wsgi middleware 的话需要自己去解析请求,但作为一个 app 内部的组件的话,可以由app对请求解析一次,authentication 直接取解析后的数据即可。 除非说要把 request、response对象也标准化,那按照这条路走下去的话,就是要搞一个标准统一所有框架,变成一个大一统的框架而已。 * <<<`Limodou`~ 的确是这样,对于django的auth middleware我很理解,并且它在处理后会将用户对象赋组request上。而authkit是一个wsgi middleware,它如何关心后台的用户机制是数据库,还是ldap,还是什么的,又如何与request相关联的呢?因为用户认证的情况在后面的应用处理也要使用的。不知道它怎么做的? * >>> `Qiangning Hong`~ 如果不希望在authentication组件里面自己解析request的话,可以在authentication middleware前面套一个解析request的middleware,比如webob。 * 这样这个authentication middleware只依赖于这个request parser middleware,而不是依赖于整个框架。 * 所有其他使用那个request parser middleware,也就是webob的wsgi框架都可以使用这些authentication middleware,可重用性显然要好很多。 * ''BTW,我是希望看到request对象标准化的,'''webob'''很有这方面的潜质。'' * 所以我说Pylons社区`推进routing标准化`是一个创举。 * 可以想象,如果request对象也标准化了,比如都使用和webob相同的接口,那各个框架之间的鸿沟又可以再大大缩小一步,wsgi组件在不同框架间可重用的程度也可以大大增加。 === for UliWeb === Limodou:: 谢谢,这下就清楚许多了。 * 按上面的回答,uliweb中的是Dispatcher(类名都是这么起的),所以它本身是wsgi的。 * 在uliweb的前段时间,还可以通过config.py来添加在执行Dispatcher前的wsgi序列,不过现在去了,因为修改了许多地方,加回来也很容易。 * 因此我想uliweb会同时支持wsgi middleware和django-like middleware。 * 因为django-like middleware更多是在针对request, response的处理,目标比较统一。 * 对于wsgi的互用,我提第4个问题: * `不同的wsgi middleware在不同的项目中是如何互用的?` * 是不是目前都是需要基于paster才可以?有共它的方式,还是根本不存在问题。 * 因为我看到paster是可以在创建wsgi middleware时引入一些local_conf的,这个倒是可以继续用,自已写一个简单的装入器。其它的有没有类似要依赖于paster的东西呢? ==== 复用vs易用 ==== UliWeb 对策:: * 现在想一想wsgi就是提供了一个标准的接口,可以在不同的组件之间进行互相调用。不过把view函数写成app还是感觉麻烦,一个hello在uliweb中只要: {{{#!python @expose('/myapp/hello/') def hello(name): return 'hello, %s!' % name }}} * 就可以了。按你的做法,say_hello其实是需要知道前面使用了selector的。这种我认为是一种假定的方式,可能会有问题。主要还是显得麻烦,对于开发者来说需要了解的东西太多。做为框架完全可以再简化。 * 有时追求复用与代码简化是冲突的,而我是希望用户用起来简单,在一定程度上的复用。而且与业务相关的处理复用本来难度就很大,倒不如考虑将与业务无关的东西复杂,并且减少对外部环境的假定或依赖更好一些。当然,具体还要看到底是什么东西是可以复用,应该如何被复用,也不是件容易的事。 Qiangning Hong:: 那个仅仅是selector的一个演示而已,一般情况下view里面是不需要使用selector.vars的。 * `框架要干的事情就是把问题简化`,比如用selector的作者写的另一个工具yaro的话,上面的例子就变成这样: {{{ from yaro import Yaro @Yaro def hello_world(req): return "Hello World!" from selector import Selector s = Selector() s.add('/myapp/hello/{name}', GET=say_hello) from flup.server.scgi import WSGIServer WSGIServer(s).run() }}} * 完全还可以更简单。比如我前面贴的meilanweb.py里`Controller.__call__`里面那一堆就是为了让写应用的时候变简单的,写代码时只要操作webob的对象就可以了,不用涉及WSGI的细节。 ##endInc ---- '''反馈''' 创建 by -- ZoomQuiet [<>]