资讯专栏INFORMATION COLUMN

flask中current_app、g、request、session源码的深究和理解

meteor199 / 2899人阅读

摘要:本文是我在学习中对上下文和几个类似全局变量的思考和研究,也有我自己的理解在内。为了研究中的,我找到定义在的源码可以看到主要由等组成,我先来分析和其实和原理上是一样的,所以将其归为一类,称为请求上下文。

本文是我在学习flask中对上下文和几个类似全局变量的思考和研究,也有我自己的理解在内。

为了研究flask中的current_app、g、request、session,我找到定义在global.py的源码:

# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))

可以看到主要由_lookup_req_object、_lookup_app_object、_find_app等组成,我先来分析request和session
其实request和session原理上是一样的,所以将其归为一类,称为请求上下文。

我们从最里面看起,partial(_lookup_req_object, "request"),最外层是一个偏函数,不过这不是重点,它主要是将"request"传给_lookup_req_object,没有其他含义, 顺着_lookup_req_object找到它的源码

def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
    raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)

从最后的return可以看到,这个函数的主要功能是从top中取出键值为"request"的内容,top是一个字典,top从_request_ctx_stack.top中来,在上面的源码中 _request_ctx_stack = LocalStack(),从名字来看LocalStack应该是一个栈类,应该有pop,push,top方法,我继续找到源码:

def __init__(self):
    self._local = Local()
...
...
def push(self, obj):
    """Pushes a new item to the stack"""
    rv = getattr(self._local, "stack", None)
    if rv is None:
        self._local.stack = rv = []
    rv.append(obj)
    return rv

def pop(self):
    """Removes the topmost item from the stack, will return the
    old value or `None` if the stack was already empty.
    """
    stack = getattr(self._local, "stack", None)
    if stack is None:
        return None
    elif len(stack) == 1:
        release_local(self._local)
        return stack[-1]
    else:
        return stack.pop()

@property
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

可以看到LocalStack()这个类有一个属性self._local = Local(),对应另一个类,继续看源码:

def __init__(self):
    object.__setattr__(self, "__storage__", {})
    object.__setattr__(self, "__ident_func__", get_ident)
...
...
def __getattr__(self, name):
    try:
        return self.__storage__[self.__ident_func__()][name]
    except KeyError:
        raise AttributeError(name)

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

我截取了几个重要的函数,LocalStack()中的push,用到了Local()中的__setattr__();pop用到了__getattr__(),看到push和pop都是对"stack"这个键值进行查询和赋值,我们转到Local()这个类中,这个类有两个实例属性,__storage__和__ident_func__,前者是一个字典,后者是一个函数,我们看一下这个get_ident函数,查看源码:

def get_ident(): # real signature unknown; restored from __doc__
    """
    get_ident() -> integer

    Return a non-zero integer that uniquely identifies the current thread
    amongst other threads that exist simultaneously.
    This may be used to identify per-thread resources.
    Even though on some platforms threads identities may appear to be
    allocated consecutive numbers starting at 1, this behavior should not
    be relied upon, and the number should be seen purely as a magic cookie.
    A thread"s identity may be reused for another thread after it exits.
    """
    return 0

显然这个函数不是python写的,因为它来自_thread.py,是一个底层库,从名字可以猜到和线程有关,根据描述,它返回一个非零整数,代表了当前线程id,我们再看看__setattr__这个方法,它其实是一个字典嵌套列表再嵌套字典的数据,__storage__是一个字典,它里面的键值被赋值为当前线程id,这个键值对应的值是另一个字典:{"stack":["request":r_val,"session":s_val]},这样和前面联系起来就很好理解了,Local()中的__storage__存储的格式为{thread_id:{"stack":["request":r_val,"session":s_val]}},LocalStack()中的top方法,返回了"stack"中最后一个加入的元素,也就是最新的元素,我自己理解为服务器接受的最新的请求,在框架外看起来request和session是一个全局变量,其实内部已经由进程id将其分隔开了,即使同时有多个请求过来,进程间的数据也不会混乱。

同理current_app和g也一样,唯一不同的是,current_app、g和request、session是两个不同的实例,注意前面 _request_ctx_stack = LocalStack()、_app_ctx_stack = LocalStack(),所以"stack"中存的数据也不一样,current_app和g称为应用上下文,两者还是有区别的。
LocalProxy 则是一个典型的代理模式实现,它在构造时接受一个 callable 的参数(比如一个函数),这个参数被调用后的返回值本身应该是一个 Thread Local 对象。对一个 LocalProxy 对象的所有操作,包括属性访问、方法调用(当然方法调用就是属性访问)甚至是二元操作,都会转发到那个 callable 参数返回的 Thread Local 对象上。
LocalProxy 的一个使用场景是 LocalStack 的 call 方法。比如 my_local_stack 是一个 LocalStack 实例,那么 my_local_stack() 能返回一个 LocalProxy 对象,这个对象始终指向 my_local_stack 的栈顶元素。如果栈顶元素不存在,访问这个 LocalProxy 的时候会抛出 RuntimeError。
需要注意的是,如果需要离线编程,尤其在写测试代码时,需要将应用上下文push到栈中去,不然current_app会指向空的_app_ctx_stack栈顶,自然也就无法工作了。
我们可以通过current_app的值来判断是否进入应用上下文中,可以用app.app_context().push()来进入应用上下文。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44734.html

相关文章

  • 深入 Flask 源码理解 Context

    摘要:主要的作用是将维护的字典中键为对应的值定义为。如果没有,则会将当前到中,同时将加入列表中否则添加。注意清理之后的动作。上述代码涉及到,它强调必须是一个可调用对象。后期的工作之一是了解。这仅仅是我的个人理解。实际上这是解决多个实例运行的问题。 Flask 中的上下文对象 知乎问题 编程中什么是「Context(上下文)」 已经能够简单地说明什么是 Context,它是一个程序需要的外部对...

    ninefive 评论0 收藏0
  • flask 上下文实现

    摘要:值得注意的是这个对象在创建时,将实例的本身作为实参传入自身,因此,。到这里想必已经很清楚了就是指对象就是对应每次请求创建的对象通过将与关联起来总结创建了,这个对应的上下文,就是每响应一个请求,就会创建一个对象,这个对象对应的上下文,就是 引言 本文主要梳理了flask的current_app, request, session, g的实现原理 源码说明 本文使用flask 0.5 版本...

    lncwwn 评论0 收藏0
  • 关于Flask-SQLAlchemy事务提交有趣探讨

    摘要:最近在开发的时候遇到这样一个问题我就好奇了这样还报不在中的错没有显示调用啊加一行测试无奈,一个一个翻到之间调用的每一个函数,终于在找到可疑点但是这里也没有显式提交。为什么接下来总结下大神们的探讨。 最近在开发mdwiki的时候遇到这样一个问题.Post is unbond to session.我就好奇了 post=Post.query.filter_by(location=locat...

    BlackHole1 评论0 收藏0
  • flask 核心 之 应用上下文 及 请求上下文

    摘要:的上下文对象有两种上下文,分别是请求上下文请求的对象,封装了请求的内容,生命周期请求处理完就结束了根据请求中的,重新载入该访问者相关的会话信息应用上下文处理请求时用作临时存储的对象。 Werkzeugs 是 Flask 的底层WSGI库。 什么是WSGI? showImg(https://s1.ax1x.com/2018/11/13/iOqdKS.jpg); 一段简单的app: def...

    tinna 评论0 收藏0
  • flask 源码解析:上下文

    摘要:但是这些对象和全局变量不同的是它们必须是动态的,因为在多线程或者多协程的情况下,每个线程或者协程获取的都是自己独特的对象,不会互相干扰。中有两种上下文和。就是实现了类似的效果多线程或者多协程情况下全局变量的隔离效果。 这是 flask 源码解析系列文章的其中一篇,本系列所有文章列表: flask 源码解析:简介 flask 源码解析:应用启动流程 flask 源码解析:路由 flas...

    Labradors 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<