飞道的博客

Flask开发高级:【上】下文管理源码解析

245人阅读  评论(0)

入口

if __name__ == "__main__":
    app.run()

进入run中:

def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
# self = app = FLask对象

往下翻:

from werkzeug.serving import run_simple

try:
    run_simple(host, port, self, **options)
# self = app = FLask对象

run实际执行的就是run_simple,就是app加括号(对象加括号),最终执行__call__方法,回到入口,app应该有一个__call__方法:

if __name__ == "__main__":
    app.run()
    app.__call__()

现在我们正式进入Flask的请求上下文环节,首先点击进入__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)
 # self = app = FLask对象
 # environ 请求的原始信息 里面包括请求头、请求体等
 # start_response response对象

run本质执行的j就是wsgi_app,回到入口,app应该有一个wsgi_app方法:

if __name__ == "__main__":
    app.run()
    app.__call__()
    app.wsgi_app()

点击进入wsgi_app方法中:

def wsgi_app(self, environ, start_response): # self = app = FLask对象
    """The actual WSGI application. This is not implemented in
    :meth:`__call__` so that middlewares can be applied without
    losing a reference to the app object. Instead of doing this::

        app = MyMiddleware(app)

往下翻,找到:

 ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()

点击进入到request_context中:

def request_context(self, environ): # self = app = FLask对象
    """Create a :class:`~flask.ctx.RequestContext` representing a
    WSGI environment. Use a ``with`` block to push the context,
    which will make :data:`request` point at this request.
    ...... 
    """
    return RequestContext(self, environ) 
    # self = app = FLask对象
    # environ 请求的原始信息 里面包括请求头、请求体等

点击进入到RequestContext中:

#app = FLask对象=self 
#那还有一个self是什么呢,往上翻,这个self是RequestContext
# environ 请求的原始信息 里面包括请求头、请求体等
def __init__(self, app, environ, request=None, session=None): 
    self.app = app
    if request is None:
        request = app.request_class(environ) # 序列化请求信息,变成Flask可用的格式
    self.request = request # self=RequestContext=request_context
    self.url_adapter = None
    try:
        self.url_adapter = app.create_url_adapter(self.request)
    except HTTPException as e:
        self.request.routing_exception = e
    self.flashes = None
    self.session = session

所有完整的request的值都在这里进行序列化,主要记着这里有了request与session就行,其它不用管,再回到ctx

ctx = self.request_context(environ)
# ctx = request_context,其中包含上面的request与session
    error = None
    try:
        try:
            ctx.push() # request_context.push()
            response = self.full_dispatch_request()

进入到push中:

def push(self): # self = ctx = request_context=(request,session)
    """Binds the request context to the current context."""
    # If an exception occurs in debug mode or if context preservation is
    # activated under exception situations exactly one context stays

往下翻,找到:

top = _request_ctx_stack.top
    if top is not None and top.preserved:
        top.pop(top._preserved_exc)

进入到_request_ctx_stack中:

# context locals
_request_ctx_stack = LocalStack()
# _request_ctx_stack 请求上下文栈,实例化LocalStack(),执行__init__方法

进入到LocalStack中:

def __init__(self):
    self._local = Local() # 实例化Local,执行__init__方法

进入到Local中:

class Local(object):
    __slots__ = ("__storage__", "__ident_func__") # 节省内存,实例只能使用这些属性

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)
        
#由于使用了__slots__优化内存 ,实例是通过数组来构建的,不是字典的形式
#Local大概是[{},get_ident],属性名在内部会映射到数组小标中

再回到上面,就是说:

def __init__(self):
    self._local = Local() # 实例化Local,执行__init__方法
# self._local = Local=[{},get_ident]

再回到这里:

# context locals
_request_ctx_stack = LocalStack()
# _request_ctx_stack 请求上下文栈,实例化LocalStack(),执行__init__方法
# _request_ctx_stack.._local = Local=[{},get_ident]

再往回翻,翻到push这里:

def push(self): # self = ctx = request_context=(request,session)
    """Binds the request context to the current context."""
    # If an exception occurs in debug mode or if context preservation is
    # activated under exception situations exactly one context stays

往下走,找到top

 top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

进入top中:

def top(self):
    """The topmost item on the stack.  If the stack is empty,
    `None` is returned.
    """
    try:
        return self._local.stack[-1]
    except (AttributeError, IndexError):
        return None
# __slots__ = ("__storage__", "__ident_func__") # 节省内存,实例只能使用这些属性
# 由上面知道,_local只能有两个属性,这里报错,最后返回None

再回到上面:

 top = _request_ctx_stack.top # None
 # _request_ctx_stack._local=[{},get_ident]
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

往下翻,找到这句:

_request_ctx_stack.push(self)
# self = ctx = request_context=(request,session)

进入到push中:

 def push(self, obj): # obj= ctx = request_context=(request,session)
 
    """Pushes a new item to the stack"""
# 这个self往上找,self._local = Local=[{},get_ident]
    rv = getattr(self._local, "stack", None)
    if rv is None:
        self._local.stack = rv = []
# _local对象.stack执行__setattr__方法
    rv.append(obj)
    return rv

进入到_local中找到__setattr__方法:

def __setattr__(self, name, value):
    ident = self.__ident_func__() # 获取线程或协程id 假设为9528
    storage = self.__storage__  # storage={}
    try:
        storage[ident][name] = value # 不成立 没有name
    except KeyError:
        storage[ident] = {name: value} # storage={9528:{name:value}}

核心

再回到上面的push中:

 def push(self, obj): # obj= ctx = request_context=(request,session)
 
    """Pushes a new item to the stack"""
# 这个self往上找,self._local = Local=[{},get_ident]
    rv = getattr(self._local, "stack", None)
    if rv is None:
        self._local.stack = rv = [] # {9528:{stack :[]}}
# _local对象.stack执行__setattr__方法
    rv.append(obj)
# {9528:{stack :[ctx(request,session)]}}
    return rv

到这里,一个存放请求信息的栈就形成了。你是9528,你进来后,生成一个stack,stack存放的就是9528的请求信息。你是9529,、9531等依次对应,所有用户的请求信息都是这样存储。


转载:https://blog.csdn.net/qq_35289736/article/details/106112256
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场