Differences between revisions 4 and 5
Revision 4 as of 2006-08-25 06:56:10
Size: 5982
Editor: HuangYi
Comment:
Revision 5 as of 2006-08-28 06:34:20
Size: 5952
Editor: HuangYi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
本文对 Pylons 中对一个请求的处理过程中内部调用的大致轮廓进行了简单的勾勒. 本文对 Pylons 内部调用过程的大致轮廓进行了简单的勾勒.

TableOfContents

Pylons 请求生命周期

本文对 Pylons 内部调用过程的大致轮廓进行了简单的勾勒.

首先通过命令 paster create --template=pylons yourproject 创建一个项目. 然后 ==>

" yourproject_dir::command "

# 读取配置文件并启动服务器和应用程序
paster serve development.ini  ==>

" yourproject_dir::development.ini "

#PasteScript 的命令会通过配置文件中指定的 egg 的 entry_points 找到相应的对象
[server:main]
# 指定 paster serve 使用的 web 服务器
use = egg:Paste#http  
[app:main]
use = egg:yourproject  ==>

" yourproject_dir/setup.py "

   1 entry_points="""
   2     [paste.app_factory]
   3     main=yourproject:make_app  ==>
   4     ...
   5 """

" yourproject.__init__.py "

   1 from yourproject.config.middleware import make_app  ==>

" yourproject.config.middleware.py "

   1 def make_app(...):
   2     ...
   3     # 核心 app
   4     app = pylons.wsgiapp.PylonsApp(config)  ==>
   5     app = ConfigMiddleware(app, ... )
   6     # YOUR MIDDLEWARE
   7     # 将 HTTPExceptions 转换成 HTTP responses
   8     app = httpexceptions.make_middleware(app, ... )
   9     app = ErrorHandler(app, ... )
  10     # 处理静态文件
  11     static_app = StaticURLParser(...)
  12     # 处理 webhelpers 中使用到的 javascript 文件
  13     javascripts_app = StaticJavascripts()
  14     # 该中间件的作用是: 对列表中的 app 一个一个尝试, 如果 app 产生 404 错误则继续试下一个
  15     app = Cascade([static_app, javascripts_app, app])
  16     app = ErrorDocuments(app, ... )
  17     # 
  18     app = RegistryManager(app)
  19     return app

" pylons.wsgiapp.py "

   1 class PylonsApp(object):
   2     def __init__(self, ...):
   3         self.app = PylonsBaseWSGIApp(... ) ==>
   4     def __call__(self, environ, start_response):
   5         ...
   6         return self.app(environ, start_response)

" pylons.wsgiapp.py "

   1 class PylonsBaseWSGIApp(object):
   2     def __init__(self ... )
   3         ...
   4     def __call__(self, environ, start_response):
   5         #正常情况下大致执行流程:
   6         self.setup_app_env(...)
   7         req = pylons.request._current_obj()
   8         # 通过 url dispatcher (routes) 找到相应 controller
   9         controller = self.resolve(environ, start_response)
  10         # 执行该 controller
  11         response = self.dispatch(controller, environ, start_response)
  12         status, response_headers, content = response.wsgi_response()
  13         start_response(status, response_headers)
  14         return content
  15         ...
  16 
  17     def dispatch(self, controller, environ, start_response):
  18         # 如果是 class, 则实例化之
  19         if not hasattr(controller, '__class__') or \
  20             getattr(controller, '__class__') == type:
  21             controller = controller()
  22         
  23         # 把它当个 wsgi application 调用之
  24         return controller(environ, start_response) ==>

" pylons.controllers.py "

   1 class Controller(object):
   2     ...
   3     def _dispatch_call(self):
   4         """将请求分发到具体函数"""
   5         action = self._req.environ['pylons.routes_dict'].get('action')
   6         action_method = action.replace('-', '_')
   7         func = getattr(self, action_method, None)
   8         if isinstance(func, types.MethodType):
   9             response = self._inspect_call(func)
  10         else:
  11             if asbool(CONFIG['global_conf'].get('debug')):
  12                 raise NotImplementedError('Action %s is not implemented' % action)
  13             else:
  14                 response = pylons.Response(code=404)
  15         return response
  16 
  17 class WSGIController(Controller):
  18     ...
  19     def __call__(self, environ, start_response):
  20         self.start_response = start_response
  21         match = environ['pylons.routes_dict']
  22         self._req = pylons.request._current_obj()
  23         
  24         # 不调用私有方法
  25         if match.get('action').startswith('_'):
  26             return pylons.Response(code=404)
  27         
  28         if hasattr(self, '__before__'):
  29             self._inspect_call(self.__before__)
  30         # 此方法请看上面的 Controller 类
  31         response = self._dispatch_call()
  32         if hasattr(self, '__after__'):
  33             self._inspect_call(self.__after__)
  34         
  35         if hasattr(response, 'wsgi_response'):
  36             # 估计返回的是 paste.wsgiwrappers.WSGIResponse 
  37             status, response_headers, content = response.wsgi_response()
  38             start_response(status, response_headers)
  39         
  40             # 如果是在测试, 将 response 放到测试环境中去
  41             if environ.get('paste.testing'):
  42                 environ['paste.testing_variables']['response'] = response
  43             response = content
  44         
  45         # 看来是个 WSGI应用程序 的 Response
  46         return response
  47 
  48 class RPCController(object):
  49     ...

结论

从中我们大致可以看出 Pylons 扩展的潜力:

  • 首先用户代码中通过命令 paster controller controllername 生成的 Controller 都默认继承自 yourproject.lib.base.BaseController ( 后者继承自 pylons.controllers.WSGIController ), 那么修改 BaseController 可以影响到所有默认生成的 Controller

  • Controller 中有两个特殊的方法 __before____after__, 这个两个方法的行为可以影响到当前 controller 中所有 action

  • yourproject/config/middleware.py 添加其他 wsgi中间件.

  • Controller 中直接调用标准的 WSGI 应用程序 并返回其结果

  • 而且注意到 PylonsAppWSGIController 都是 WSGI应用程序, 不过里面通过 environ 依赖到一些其他的东西, 比如 routes, 单独拿出来用的意义似乎不太大 :-(

  • ...

PyLons/call-cycle (last edited 2009-12-25 07:09:39 by localhost)