web.py(0.61)源码剖析

application.py

__all__ = [
    "application",
    "auto_application",
    "subdir_application",
    "subdomain_application",
    "loadhook",
    "unloadhook",
    "autodelegate",
]

__all__是一个字符串列表,里面包含要对外暴露的接口。即可以利用模块名直接调用且只能调用__all__中包含的类或者方法。

application类
下面是类的构造方法头

def __init__(self, mapping=(), fvars={}, autoreload=None):

一般会这样使用:

app = web.application(urls, global())

先看一下构造器的最后一个参数autoreload(自动加载),默认值为None,

if autoreload is None:
        autoreload = web.config.get("debug", False)

这段代码说明,当没有设置autoreload参数时,autoreload = web.config.debug,可能为True,也可能为False。如果没有设置web.config.debug,那autoreload = False,即不启动自动加载。
何为自动加载?
也就是在项目启动后,如果修改了代码,无需重新启动项目,而是程序内部自动加载,从而直接达到预期效果。这在调试代码时很有用。

构造器的第二个参数mapping=()代表urls路由元组,通过init_mapping()函数来初始化url路由映射关系,这个函数内部调用了utils工具类的group()函数

self.init_mapping(mapping)
...
...
def init_mapping(self, mapping):
    self.mapping = list(utils.group(mapping, 2))
...
...
def group(seq, size):
    return (seq[i : i + size] for i in range(0, len(seq), size))

看看group()是怎么实现的,再看看下面的例子就明白了:

urls = (
    '/', 'toindex',
    '/index', 'index',
    '/favicon.ico', 'ico',
)
=>
self.mapping = [
    ['/', 'toindex'],
    ['/index', 'index'],
    ['/favicon.ico', 'ico'],
]

后面框架在进行url路由时,就会遍历这个二维列表。

构造器的第三个参数fvars={}代表一个字典,比如globals(),locals(),分别提供当前范围的全局变量或局部变量。

构造器接着初始化了一个processors空列表,在add_processor()中每个处理器都会被添加到该列表中

self.processors = []
...
def add_processor(self, processor):
    self.processors.append(processor)

构造器里还通过add_processor()添加了两个处理器self._loadself._unload,分别在HTTP请求处理前后进行处理,并非真正用来处理HTTP请求,而是做一些额外的工作,官方对于处理器也做了详细解读,可自行查阅。

self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))

看看其它的类方法:
add_mapping()用于添加url路由映射。

def add_mapping(self, pattern, classname):
    self.mapping.append((pattern, classname))

request()通过指定路径和方法向应用发出请求,返回值reponse将是一个带有data, status和headers、header_items的storage对象。一个storage对象类似于一个字典,但是有两种调用方式:obj.fooobj['foo']

def request(
    self,
    localpart="/",
    method="GET",
    data=None,
    host="0.0.0.0:8080",
    headers=None,
    https=False,
    **kw
):
...
...
    response = web.storage()
	  
    def start_response(status, headers):
	response.status = status
	response.headers = dict(headers)
	response.header_items = headers
	
    data = self.wsgifunc()(env, start_response)  
    response.data = b"".join(data)  
    return response

可以看到data是调用了wsgifunc()产生的,其执行结果是返回一个WSGI兼容的函数,并且该函数内部实现了url路由等功能。其中还包含peepwsgi两个内置函数,如果没有实现任何中间件(即*middleware为空),那就直接返回内置的wsgi函数,否则要将wsgi转成中间件类型再返回。很明显这里是直接返回内置wsgi并调用。

def wsgifunc(self, *middleware):
    def peep(iterator):
    ...
    def wsgi(env, start_resp):
    ...
    for m in middleware:
        wsgi = m(wsgi)

    return wsgi

peep()函数通过进行一次迭代来窥视一个迭代器,并返回一个等效的迭代器。返回itertools.chain([firstchunk], iterator),chain对象的__next__()方法会按顺序直到消耗完所有迭代器的可迭代对象。
wsgi()函数实现了wsgi兼容接口,同时也实现了url路由等功能,其中:
self._cleanup()内部调用了utils.ThreadedDict.clear_all(),表示清除所有的threadlocal数据,避免内存泄漏(web.py框架的很多数据都保存在threadlocal变量中)
self.load(env)使用env中的参数初始化了web.ctx变量,我们在应用中可能会用到,比如web.ctx.fullpath
handle_with_processors ()会对请求的url进行路由,找到合适的类或子应用来处理该请求,对于处理的返回结果,可能有三种方式:

  1. 返回一个可迭代对象,则通过peep()进行安全迭代处理。
  2. 返回其他值,则创建一个列表对象来存放
  3. 如果抛出了一个HTTPError异常(比如我们使用raise web.OK(“hello, world”)这种方式来返回结果时),则将异常中的数据e.data封装成一个列表

build_result(result)会将result中的元素都转为bytes类型。
接下来,根据WSGI规范,调用start_resp函数,并将result结果转换成一个迭代器。application = app.wsgifunc()就是将wsgi函数赋值给application变量,这样应用服务器就可以采用WSGI标准和我们的应用对接了。

def wsgi(env, start_resp):
    # clear threadlocal to avoid inteference of previous requests
    self._cleanup()

    self.load(env)
    try:
        # 请求方法字母必须是大写
        if web.ctx.method.upper() != web.ctx.method:
            raise web.nomethod()

        result = self.handle_with_processors()
        if result and hasattr(result, "__next__"):
            result = peep(result)
        else:
            result = [result]
    except web.HTTPError as e:
        result = [e.data]

    def build_result(result):
        for r in result:
            if isinstance(r, bytes):
                yield r
            else:
                yield str(r).encode("utf-8")

    result = build_result(result)

    status, headers = web.ctx.status, web.ctx.headers
    start_resp(status, headers)

    def cleanup():
        self._cleanup()
        yield b""  # force this function to be a generator

    return itertools.chain(result, cleanup())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值