周末看了关于Aaron Swartz的一些东西:一部电影 互联网之子 和一个web框架 webpy,这里写一下webpy的源码的简单分析。
webpy安装
webpy安装使用python的软件包管理工具(easy_install或者pip)安装就可以了。
hello world
官网的一个简单demo, code.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import web
urls = ( '/(.*)', 'hello' )
app = web.application(urls, globals())
calss hello: def GET(self, name): if not name: name = 'World' return 'Hello, ' + name + '!' if __name__ == '__main__': app.run()
|
命令行启动
这时候用浏览器打开 localhost:8080,就看到了Hello World。
源码解读
我们针对上面的code.py过一下源代码。源代码主要是在web文件夹下,包括application.py, browser.py, db.py等。
urls是路由表,表示对于目录’/(.*)’的处理都对应到hello,hello下面可以定义HTTP的方法,包括GET,POST等。另外,细心一点地话会发现 路由表支持正则表达式。
1 2
| app = web.application(urls, globals()) app.run()
|
参数urls为路由表,globals()为python内置函数,主要作用是传递全局变量。我们看一下application类。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class application: def __init__(self, mapping=(), fvars={}, autoreload=None) if autoreload is None: autoreload = web.config.get('debug', False) self.init_mapping(mapping) self.fvars = fvars self.processors = [] self.add_processor(loadhook(self._load)) self.add_processor(unloadhook(self._unload)) if autoreload: ...
|
autoreload表示是否需要重新import程序,每一个wsgi调用application都会检查一下,具体细节先不说了。init_mapping(mapping)这个函数直觉上应该就是将url路由表载入,我们来验证一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def init_mapping(self, mapping): self.mapping = list(utils.group(mappping, 2)) class utils: def group(seq, size): def take(seq, n): for i in xrange(n): yield seq.next() if not hasattr(seq, 'next'): seq = iter(seq) while True: x = list(take(seq, size)) if x: yield x else break
|
显然这里将mapping进行了两两分组,实现代码我看了一会才明白。
processors是干嘛用的呢?其实是为了自定义一些操作。代码里的loadhook()是处理request之前要进行的操作,unloadhook()是处理完request之后要进行的操作,这里不妨说下_load和_unload是干嘛的?_load的作用是载入request的一些状态,比如一些环境变量,实现这里就先不展开了。_unload那么就卸载了。
下面看一下app.run()。这里代码跳转比较多,先全部列出。
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
| class application: def run(self, *middleware): return wsgi.runwsgi(self.wsgifunc(*middleware)) def wsgifunc(self, *middleware): ... for m in middleware: wsgi = m(wsgi) return wsgi wsgi.runwsgi: def runwsgi(func): ... server_addr = validip(listget(sys.argv, 1, '')) if os.environ.hash_key('PORT'): server_addr = ('0.0.0.0', intget(os.environ['PORT'])) return http.server.runsimple(func, server_addr)
httpserver: def runsimple(func, server_address=("0.0.0.0", 8080)): global server func = StaticMiddleware(func) func = LogMiddleware(func) server = WSGIServer(server_address, func) ... try: server.start() except (KeyboadInterrupt, SystemExit): server.stop() server = None def WSGIServer(server_address, wsgi_app): import wsgiserver ... server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")
|
app.run最后,也是最主要的工作是起一个CherryPy WSGI server 监听前面传过来的端口,对应的响应函数为前面传过来的func。CherryPy是一种WSGI server,还有一些别的,所以这个地方是可以重载的。而app.run到httpserver.runsimple中间的代码主要用来生成可以供CherryPy server调用的应用程序,以及生成对应的端口地址。
Webpy号称匕首,小巧而不失锋利。最可惜的是Aaron Swartz离开了,可惜了。之后会继续写一些webpy源码的文章。