bottle源码分析——深入理解web开发

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的开发者对方法就是可调用的属性理解得比较好,代码里多次出现把方法当做属性使用的场景

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值