zoukankan      html  css  js  c++  java
  • Flask(三)之请求上下文源码分析

    Flask请求上下文源码分析

    from  flask import Flask
    
    app  = Flask(__name__)
    
    if __name__ == '__main__':
        
        app.run()
    

    分析入口app.run(),点入查看源码:

    from werkzeug.serving import run_simple
    
            try:
                run_simple(host, port, self, **options)
                #这里的self是app自己
    

    run_simple是werkzeug内部的方法,在run_simple执行时会将app加括号调用从而执行app的__call__方法,来看__call__源码:

    def __call__(self, environ, start_response):
            """The WSGI server calls the Flask application object as the
            WSGI application. This calls :meth:`wsgi_app` which can be
            wrapped to applying middleware."""
            return self.wsgi_app(environ, start_response)
    

    进入wsgi_app:

    ctx就是RequestContext对象,请求上下文对象ctx中初始化所有请求所有内容,并且其内部封装着Request对象,Request对象把请求过来的信息格式化并且储存起来。

    1 ctx = self.request_context(environ)---》ctx本质是RequestContext的对象
    1.1 RequestContext(self, environ) :self是当前的app,environ是请求相关的
    1.2 RequestContext(self, environ)执行的结果是:RequestContext类的对象,该对象中包含了请求相关和当前的app
    1.3 所以这个ctx就是RequestContext的对象。

    '''
    environ:A WSGI environment
    start_response: A callable accepting a status code,a list of headers, and an optional exception context to start the response.         
    '''
    def wsgi_app(self, environ, start_response):
    
        ctx = self.request_context(environ)
        #在self.request_context(environ)中
    #RequestContext类中的 `__init__`实例化出请求上下文对象ctx
        
            error = None
            try:
                try:
    
                    #把ctx放到了Local对象里面了
                    ctx.push()
                    #请求扩展以及响应函数
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:  # noqa: B001
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                ctx.auto_pop(error)
                
    

    2 ctx.push():当前ctx是RequestContext的对象,那就是执行RequestContext的对象的push方法
    2.1 ctx.push方法中有一个 _request_ctx_stack.push(self);这个self是ctx, 那就是把ctx,传递给_request_ctx_stack
    2.2 _request_ctx_stack是什么?--》 LocalStack的对象
    2.3 _request_ctx_stack.push方法是什么?看源码:
    obj就是_request_ctx_stack.push(self),传过来的self,也就是ctx

            def push(self, obj):
                rv = getattr(self._local, "stack", None)
                if rv is None:
                    #storage["线程或者协程的id"][stack] = []
                    self._local.stack = rv = []
                # storage["线程或者协程的id"][stack] = [ctx,]
                rv.append(obj)
                return rv
    

    ​ 2.3.1 在2.3的代码中self._local.stack做了什么?
    ​ 我们发先self._local是Local对象,所以self._local就执行
    ​ Local对象的__setattr__:代码如下:

    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            #name = stack
            storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}
    

    对2的总结:就是将ctx放到Locl对象中以这样的形式存储storage[ident][stack] = [ctx,]

    response = self.full_dispatch_request() :这里面是所有请求扩展以及真正的响应函数:
    源码如下:

     def full_dispatch_request(self):
                self.try_trigger_before_first_request_functions()
                try:
                    request_started.send(self)
                    rv = self.preprocess_request()
                    #如果请求扩展中的,请求之前的所有函数,如果没有返回值,就会执行下面的,
                    #下面的是:请求路由中的函数
                    if rv is None:
                        rv = self.dispatch_request()
                except Exception as e:
                    rv = self.handle_user_exception(e)
                return self.finalize_request(rv)
    

    3.1 self.try_trigger_before_first_request_functions():这是在执行请求扩展中的befor_first_request
    它是如何判断项目启动后只执行里面方法一次的呢?
    答:它是通过self._got_first_request变量来判断的,初始值为False,一旦执行过该函数,在函数的末尾self._got_first_request设置成True
    源码如下:

    def try_trigger_before_first_request_functions(self):
        if self._got_first_request:
            return
        with self._before_request_lock:
            if self._got_first_request:
                return
            #这里去循环第一次执行的函数,请求扩展的里面
            for func in self.before_first_request_funcs:
                func()
                self._got_first_request = True
    

    3.2 rv = self.preprocess_request() 这里执行的是befor_request的函数,
    源码如下:

    def preprocess_request(self):
        bp = _request_ctx_stack.top.request.blueprint
        funcs = self.url_value_preprocessors.get(None, ())
        if bp is not None and bp in self.url_value_preprocessors:
            funcs = chain(funcs, self.url_value_preprocessors[bp])
            for func in funcs:
                func(request.endpoint, request.view_args)
                #请求之前的要做的事情,请求之前的请求扩展
                funcs = self.before_request_funcs.get(None, ())
                if bp is not None and bp in self.before_request_funcs:
                    funcs = chain(funcs, self.before_request_funcs[bp])
                    for func in funcs:
                        rv = func()
                        if rv is not None:
                            return r
    

    3.2.1 funcs = self.before_request_funcs.get(None, ())这里是获取所有注册进来的befor_request
    3.2.2 下面的代码可以看出:如果befor_before_request函数有一个有返回值,那后面的函数都不执行,并且把返回值给rv = self.preprocess_request()的rv

    for func in funcs:
        rv = func()
        if rv is not None:
            return rv
    

    3.3 if rv is None: 这个rv是3.2中befor_reuqest返回的,如果没有返回值才会执行rv = self.dispatch_request();有返回值不会执行
    rv = self.dispatch_request()这是真正的响应函数
    通过这个代码:我们知道,如果befor_request有返回值,就不会执行真正的响应函数
    3.4 return self.finalize_request(rv):这个个rv是3.2或者3.3的返回值
    源码如下:

    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            #这里执行的是执行完请求视图后,after_request的请求
            response = self.process_response(response)
            request_finished.send(self, response=response)
            except Exception:
                if not from_error_handler:
                    raise
                    self.logger.exception(
                        "Request finalizing failed with an error while handling an error"
                    )
                    return response
    

    3.4.1 response = self.process_response(response):这里做after_request请求扩展的
    源码如下:

    def process_response(self, response):
        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
            if None in self.after_request_funcs:
                funcs = chain(funcs, reversed(self.after_request_funcs[None]))
                for handler in funcs:
                    response = handler(response)
                    if not self.session_interface.is_null_session(ctx.session):
                        self.session_interface.save_session(self, ctx.session, response)
                        return response
    

    在上述代码中有两行代码:
    if None in self.after_request_funcs:
    funcs是所有after_request的函数列表,并用reversed做了反转
    这个我们就知道,在after_request为什么先注册的后执行funcs = chain(funcs, reversed(self.after_request_funcs[None]))
    for handler in funcs:
    在这里知道after_request的函数必须接收这个response,并且要做返回response = handler(response)

    4 我们知道3中就可以调用request属性了,那它怎么做到呢?当我们在3中的任意一个位置,都能调用request或者其他的。比如我们request.methons,它是如果找到当前请求的request呢
    4.1 当我们调用request.methons的时候,我们先要看看rquest是什么?
    看源码我们知道:request = LocalProxy(partial(_lookup_req_object, "request"))也就说是LocalProxy对象

    4.2 当我们调用request.methons就会返回属性methons属性给我们,但是我们在LocalProxy根本就没有methons属性,那我们想到,既然没有这个属性,那一定会走__getattr__
    LocalProxy的__getattr__源码如下:

    def __getattr__(self, name):           
        if name == "__members__":
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name) 
    

    上述代码中name,就是我们要找的methons属性,那它是从self._get_current_object()通过反射那拿去的,从下面的分析中我们知道self._get_current_object() 就是request。

    4.2.1 self._get_current_object()的返回值是什么?通过下面的源码我们可以看到就self.__local()执行结果
    源码:

    def _get_current_object(self):
        if not hasattr(self.__local, "__release_local__"):
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError("no object bound to %s" % self.__name__)
    
       
    

    ​ 4.2.1.1 self.__local()的执行结果是什么?我们先搞清楚self.__local是什么?
    ​ 我们发现self.__local是通过如下初始化得到的:

    def __init__(self, local, name=None):
        object.__setattr__(self, "_LocalProxy__local", local)
    

    ​ 那我们可以知道 self.__local就是4.1 中 LocalProxy(partial(_lookup_req_object, "request"))的参数,
    ​ 也就 partial(_lookup_req_object, "request")偏函数

    ​ 4.2.1.2 我们现在知道self.__local partial(_lookup_req_object, "request")
    ​ 那 self.__local()就是 partial(_lookup_req_object, "request")()执行

    ​ 4.2.1.3 partial(_lookup_req_objec,"request")相当于给_lookup_req_objec函数传递了一个"request"参数
    _lookup_req_objec的源码如下:

    #name 为 "request"
    def _lookup_req_object(name):
        #到Local中取取ctx取出来了
        top = _request_ctx_stack.top
        #top就是ctx
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
            #cxt中取request,把request返回回去了
            return getattr(top, name)
    

    我们从 4.2.1.3.1得知:top就是ctx,getattr(top, name)就是从ctx中找request,并且返回那4.2.1.2中self.__local执行的结果就是request
    4.2.1.3.1 上述代码中_request_ctx_stack.top的源码如下:

    @property
    def top(self):
        try:
            #返回了一开始进去的ctx对象
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None  
    
        self._local也是Local对象.stack就是在执行__getattr__
        代码如下:
        def __getattr__(self, name):
            try:
                return self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
    

    这样_request_ctx_stack.top的到的结果就ctx,那 4.2.1.3 中的 top = _request_ctx_stack.top;top就是ctx

  • 相关阅读:
    UNION ALL
    jquery.validate的使用
    通过匹配绑定select option的文本值 模糊匹配
    .net 时间操作[摘抄]
    判断Table表中是否含有某一列
    将数组,表的某一列转换为string字符串的方法
    选出某一列不重复,某一列作为选择条件,其他列正常输出(摘抄)
    SQLite 解决:Could not load file or assembly 'System.Data.SQLite ... 试图加载格式不正确的程序(转)
    金山毒霸占用80端口
    DropDownList 绑定数据后 插入一条不属于表中的数据
  • 原文地址:https://www.cnblogs.com/ghylpb/p/12391029.html
Copyright © 2011-2022 走看看