wsgi的实现
web服务器实现wsgi协议
这是python标准库里的web服务器的实现方式,从tcp包中解析出http请求的相关字段存入environ
,然后传给web应用程序application
,最后返回web应用程序的处理结果
def run(self, application):
self.setup_environ()
result = application(self.environ, self.start_response)
return result
web程序实现wsgi协议
在bottle框架下开发的所有代码都会被整合进Bottle对象,Bottle对象实现的__call__方法使Bottle对象符合了wsgi接口协议
class Bottle:
def __call__(self, environ, start_response):
""""""
路由
路由添加
bottle的路由装饰器有点小惊喜,视图函数callback传进去后又原封不动传出来
def route(self, path=None, method='GET'):
def decorator(callback):
# 添加路由的代码
...
return callback
return decorator
数据结构
保存路由的数据结构肯定是哈希表
- 静态路由
{
'GET': {
'/path1': 视图函数1,
'/path2': 视图函数2
},
'POST': {}
}
- 动态路由
动态路由需要对方法下的路径逐个进行匹配,bottle将每个路径的正则匹配并成了一个路径匹配函数,根据匹配到的位置去寻找相应位置的视图函数
{
'GET': [
路径匹配函数,
[(Route('/path3', 视图函数3), 参数获取函数3),
(Route('/path4', 视图函数4), 参数获取函数4),
(Route('/path5', 视图函数5), 参数获取函数5)]
],
'POST': {}
}
插件
插件就是一个装饰器,应用于所有视图函数,比如JsonPlugin将响应体转为json字符
插件生效位置
生效位置位于路由后视图函数执行前。首先对请求进行路由匹配,未匹配到直接返回,匹配到后对视图函数使用插件进行装饰,然后执行视图函数
for plugin in self.all_plugins():
callback = plugin(callback)
或
callback = plugin.apply(callback, context)
插件更新后视图函数的重载
插件更新时,视图函数的重载是通过Route类的描述符属性call
实现的。
描述符call
通过类装饰器cached_property生成,call
也是Route对象调用视图函数的方法,这个方法一开始是没有的,Route对象第一次调用call
会调用描述符的__get__方法对视图函数进行插件装饰,然后将装饰好的视图函数存入实例属性__dict__中,下次再调用call
时调用的就是视图函数而非描述符
class cached_property(object):
def __init__(self, func):
self.__doc__ = getattr(func, '__doc__')
self.func = func
def __get__(self, obj, cls):
if obj is None: return self
value = obj.__dict__[self.func.__name__] = self.func(obj)
return value
class Route:
@cached_property
def call(self):
return self._make_callback()
插件更新时,只需删除__dict__里的call
方法
class Route:
def reset(self):
self.__dict__.pop('call', None)
全局变量request线程独立性的实现
request是个全局变量,视图函数里对request的访问其实访问的都是request.environ,request.environ由property生成,对其的访问其实访问的是threading.local()生成的对象,这个对象是线程独立的
def local_property(name=None):
ls = threading.local()
def fget(self):
try:
return ls.var
except AttributeError:
raise RuntimeError("Request context not initialized.")
def fset(self, value):
ls.var = value
def fdel(self):
del ls.var
return property(fget, fset, fdel, 'Thread-local property')
class BaseRequest(object):
def __init__(self, environ=None):
self.environ = {} if environ is None else environ
# 循环引用?
self.environ['bottle.request'] = self
@property
def path(self):
return '/' + self.environ.get('PATH_INFO','').lstrip('/')
class LocalRequest(BaseRequest):
# 少见的骚操作,新增一个可读性好的初始化方法bind
bind = BaseRequest.__init__
environ = local_property()
request = LocalRequest()
结语
bottle的开发者对方法就是可调用的属性
理解得比较好,代码里多次出现把方法当做属性使用的场景