本文发自 http://www.binss.me/blog/Source-code-reading-notes-for-bottle-framework/,转载请注明出处。
?bottle是最轻量级的Python Web框架,代码含注释一共只有4000行,存放在单个文件(bottle.py)中。它实现了Web应用常用的功能,如路由、模版渲染、cookie设置等,无需依赖于其他库,可独立运行。
组成
Router
路由的有序集合,URL映射器。
对WSGI的请求(environ)进行解析,返回第一个匹配的target。主要函数有:
-
def add(self, rule, method, target, name=None)
向Router中添加/替换rule,rule维护了匹配信息(method, url)到target的映射
-
def match(self, environ)
对WSGI的请求(environ)进行解析,返回第一个匹配的target
先匹配匹配静态rule(如 /contact ),再匹配动态rule(带有通配符,如 /wiki/ )
如果没有匹配项,判断是否是由于HTTP Method不匹配造成的(可以匹配URL),如是,返回405 Method Not Allowed,如否,返回404 Not Found。
Route
保存一条route信息,对应于Router里的一条rule。
Request
对WSGI environ的封装。以属性的方式提供了许多常用函数,如get_header、get_cookie、files、headers等。
一般属性使用@property来定义。部分属性使用了DictProperty来定义(一般为只读属性)。使用attr和key两级索引。
以ThreadLocal方式存储,每一个线程都有独立的副本。
Response
封装了HTTP Response,包括body、status code、header、cookie等信息。
Template
对模版进行渲染,将模版中的参数替换为用户设置的值。
实现了SimpleTemplate类,利用StplParser来进行模版渲染,也支持使用第三方渲染器如mako、Cheetah、Jinja2
通过调用template函数,传入参数,即可得到渲染后的内容。主要函数如下:
-
def search(cls, name, lookup=None)
在指定目录下查找模版文件,返回模版文件的完整路径。
由基类BaseTemplate实现。
-
def prepare(self, **options)
进行准备工作,如引入第三方渲染器模块,设置编码类型、转义函数,初始化缓存等。
子类必须实现。
-
def render(self, args, *kwargs)
通过传入的参数渲染模版,返回渲染后的字符串。
对于SimpleTemplate来说,将用户传入的参数和辅助函数加入到字典中,并通过eval执行编译后的模版代码。在此过程中,通过将SimpleTemplate.co、SimpleTemplate.code函数设置为cached_property使其只执行一次,实现模版代码的复用。
子类必须实现。
ServerAdapter
bottle实现的是一个Web App,可以通过WSGI协议将其挂到Web Server上使用。
默认对许多Server提供了支持,列表如下:
server_names = {
'cgi': CGIServer,
'flup': FlupFCGIServer,
'wsgiref': WSGIRefServer,
'waitress': WaitressServer,
'cherrypy': CherryPyServer,
'paste': PasteServer,
'fapws3': FapwsServer,
'tornado': TornadoServer,
'gae': AppEngineServer,
'twisted': TwistedServer,
'diesel': DieselServer,
'meinheld': MeinheldServer,
'gunicorn': GunicornServer,
'eventlet': EventletServer,
'gevent': GeventServer,
'geventSocketIO': GeventSocketIOServer,
'rocket': RocketServer,
'bjoern': BjoernServer,
'aiohttp': AiohttpServer,
'auto': AutoServer, # 从几个默认的WSGI Server中尝试进行启动,直到第一个成功的为止
}
如果列表中没有,用户也可以对支持WSGI的Web Server进行自定义。只需继承自ServerAdapter,然后实现def run(self, handler),在里面启动Web Server即可。
Bottle
主类,一个实例为一个app。
-
def route(self, path=None, method='GET', callback=None, name=None, apply=None, kip=None, **config)
将传入的参数包装成Route对象,加入到bottle.routes列表中。同时在Router中新增rule。
-
def match(self, environ):
通过Router进行匹配,获取对应的处理函数。
-
def wsgi(self, environ, start_response)
标准wsgi接口。处理请求environ,调用start_response返回响应。
-
def mount(self, prefix, app, **options)
可将其他bottle实例或WSGI应用挂载到当前bottle实例上。
如
app.mount('/photo/', photo_app)
这样当用户访问 /photo/ 时,请求就会交给photo_app来处理,实现了逻辑上的解耦。
-
def add_hook(self, name, func)
添加钩子。允许用户在特定时机执行自定义函数。
name参数为钩子类型,指定了钩子func运行的时机。
before_request: 收到请求,route匹配前执行
after_request: handler处理请求后执行
app_reset: Bottle.reset调用时执行
-
def install(self, plugin)
安装插件以扩展bottle的功能。常用的有:
- Bottle-Beaker 实现session
- Bottle-Flash 支持flash
- Bottle-Sqlite sqlite集成
- Bottle-Mongo MongoDB集成
- Bottle-Redis Redis集成
语法糖
bottle将很多常用函数包装成装饰器,以语法糖的形式调用可以使代码更加简洁。
-
@view 在执行函数后,将结果作为参数调用template函数进行模版渲染。
原来需要在返回时调用template:
def hello(name='World'): return template('hello_template', name=name)
如今配合语法糖使用view,代码更美观:
@view('hello_template') def hello(name='World'): return {'name': name}
-
@route
设置路由路径。
@route('/') def hello(): return 'Hello World'
-
@error
返回特定的HTTP error。
@error(404) def error404(error): return 'Nothing here, sorry'
-
@hook
调用add_hook函数添加hook。
@hook('before_request') def callback(): print 'callback'
其他
麻雀虽小,五脏俱全。bottle还实现了以下贴心的功能。
-
自动重启
设置run的reloader参数为True可以开启自动重启。
-
默认app
通过make_default_app_wrapper,当前bottle app内的接口将暴露到全局命名空间。如@app.get可以写成@get。
流程
下面展示bottle处理的流程。
-
用户调用全局run启动Web Server,默认为wsgiref(Python内置WSGI Server)
-
Web Server收到请求,以WSGI标准的形式调用bottle(__call__),然后wsgi函数被调用。根据WSGI标准,environ为请求内容,start_response为回调函数
-
(bottle._handle)调用before_hook
-
通过router.match获取匹配的route,调用处理函数,返回处理结果
-
调用after_request
-
(bottle._cast)将4得到的处理结果转换为符合WSGI标准的结果
-
调用start_response设置Header,然后返回6得到的结果
-
Web app执行完毕,等待下一个请求
扩展性
bottle令人眼前一亮之处不仅在于小巧,还在于强大的可扩展性。
对于模版渲染,除了使用bottle自带的SimpleTemplate外,还能使用mako、Cheetah、Jinja2。
对于Web Server,除了python自带的wsgiref外,还能使用tornado、twisted等等。还可选用eventlet、gevent进行Monkey patch。
相比其他主流Web应用框架,bottle的功能还是比较简单,没有实现如session、数据库集成等功能。而这些都可以通过插件解决。在安装插件后,通过install添加到bottle中即可使用。
总结
短短4000行(其中还包含了注释),实现了一个小而美的Web框架,同时不失可扩展性,这就是bottle几年来倍受欢迎的原因。通过读4000行代码,来理解一个Web框架的实现方式,还是非常值得的。
1F Zhikun 8 years ago 回复
强!?