flask 中请求上下文reuqest session 和应用上下文current_app g 的实现

我们先看这几个全局变量的定义



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


def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return getattr(top, name)


def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app


# 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'))

我们以g的使用为例,分析原理,当我们使用g.username=’abc’ 时,这是一个属性赋值,我们看一下LocalProxy的代码

   __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            # "local" is a callable that is not an instance of Local or
            # LocalManager: mark it as a wrapped function.
            object.__setattr__(self, '__wrapped__', local)

    def _get_current_object(self):
        """Return the current object.  This is useful if you want the real
        object behind the proxy at a time for performance reasons or because
        you want to pass the object into a different context.
        """
        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__)


    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)

这里只贴出了部分代码,当我们给g属性赋值时,触发了最下面这行匿名函数的代码,里面的_get_current_object()
,在这里返回是getattr(top,g)的结果。这个结果又是什么么,我们继续往下看,top是_app_ctx_stack的一个属性,而_app_ctx_stack = LocalStack() ,我们看LoaclStack的代码

    def __init__(self):
        self._local = Local()

    def __release_local__(self):
        self._local.__release_local__()

    def _get__ident_func__(self):
        return self._local.__ident_func__

    def _set__ident_func__(self, value):
        object.__setattr__(self._local, '__ident_func__', value)
    __ident_func__ = property(_get__ident_func__, _set__ident_func__)
    del _get__ident_func__, _set__ident_func__


    @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 初始化中,实例化了一个Loal(),我们先看Loal()的代码

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    def __call__(self, proxy):
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    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}

在这里我们看到get_ident,这个函数是用来生成线程ID的,可以看到在Local类中,storage是一个字典,setattr(self, name, value)定义了属性设置的方法,在设置属性时,会将当前线程的ID作为KEY,将要设置的属性和值放在字典里作为value。到这里也就能解释为什么flask能保证线程安全了。我们要知道,在web程序运行时,有request进来的时候,程序会自动把包含线程ID的应用上下文和请求上下文推到入栈鼎,即调用的是LocalStack的push方法。所以此时的top是LoaclStack位于栈定的’stack’中的对象,然后找到属性的g, 然后通过属性设置将username设为’abc’。至于为什么会有属性g及g 为什么可以设置属性,这是因为在应用上下文进栈的时候,就已经定义好了它的这种结构,这种结构是在请求进来时就确定的。

猜你喜欢

转载自blog.csdn.net/hehekk/article/details/82319578
今日推荐