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

  • 相关阅读:
    CodeForces 785D Anton and School
    CodeForces 785C Anton and Fairy Tale
    CodeForces 785B Anton and Classes
    CodeForces 785A Anton and Polyhedrons
    爱奇艺全国高校算法大赛初赛C
    爱奇艺全国高校算法大赛初赛B
    爱奇艺全国高校算法大赛初赛A
    EOJ 3265 七巧板
    EOJ 3256 拼音魔法
    EOJ 3262 黑心啤酒厂
  • 原文地址:https://www.cnblogs.com/ghylpb/p/12391029.html
Copyright © 2011-2022 走看看