flask源码之全局变量

引言:

  flask全局变量,里边包含了几乎常用的双下方法以及对这些方法flask又做了定制化,那么这样的话几乎教会了我们怎么去使用这些双下方法,并且有个生僻的东西叫偏函数(冻结函数)

 

代码:

  入口    import flask.globals

# -*- coding: utf-8 -*-

from functools import partial

from werkzeug.local import LocalProxy
from werkzeug.local import LocalStack


_request_ctx_err_msg = """\
Working outside of request context.

This typically means that you attempted to use functionality that needed
an active HTTP request.  Consult the documentation on testing for
information about how to avoid this problem.\
"""
_app_ctx_err_msg = """\
Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().  See the
documentation for more information.\
"""


def _lookup_req_object(name):
    # name=request
    # _request_ctx_stack 就是globals中的 LocalStack() 对象
    # top方法拿到 requestContext也就是ctx 即 top=ctx
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 这里就相当于 去ctx中取request 
    return getattr(top, name)


def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    # name= request  这就是 ctx.push 中调用 requet_context 的 request=request_class
    return getattr(top, name)


def _find_app():
    # 这里同 request session g 同理
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app

"""
LocalStack LocalProxy Local
"""



# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()

# 在使用 current_app 可以被直接导入使用 _find_app 从栈中拿到了程序的上下文 不过没有使用偏函数
current_app = LocalProxy(_find_app)
# 同 current_app 就可以在 视图函数中导入该 app
# capp=LocalProxy(partial(_lookup_app_object,'app'))

# 以后执行 函数 partial(_lookup_req_object, "request") 时 自动传递 request参数
# 查看源码 先去Local中拿到 RequestContext 对象 也就是ctx 这个对象中有request对象和session对象
# (_lookup_req_object, "request") 偏函数
request = LocalProxy(partial(_lookup_req_object, "request"))
#  同上 去ctx中获取 sesison
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))

  这里核心的代码 设计到 三个类    LocalStack    LocalProxy  Local 关系如下图

      描述:

          1、Local类是一个字典对象里边封装了大量的双下方法,并且做了定制化,且大量使用了lambda函数,模糊的话自己回去看看 lambda,主要目的是利用线程号为key,存储数据,用线程号对数据之间天然形成了隔离。

          2、LocalStack类更像是个统一的入口,在__init__方法中组合Local类,主要作用就是在Local中存取数据,操作的主要是 请求和应用相关的对象

          3、LocalProxy 类也是一个统一的入口,不过利用了偏函数,其实例化传入的local 就是 偏函数,查看源码还是从LocalStack中取到request和session的属性对象

 

  

 

LocalStack代码:

    下方高亮的方法主要在 wsgi_app 里边使用,并且使用的频率相当的高,作用就是去拿值,不过pop比top多做了一步,删除了栈元素

class LocalStack(object):
   
    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__

    def __call__(self):
        def _lookup():
            rv = self.top
            if rv is None:
                raise RuntimeError("object unbound")
            return rv

        return LocalProxy(_lookup)

    def push(self, obj):
  
        # 将 loaclstack
        rv = getattr(self._local, "stack", None)
        # print("_local====>", self._local)
        # print("rv====>",rv)
        # 这里的rv是 None
        # 将 loaclstack 添加到local中去 self._local = Local()
        if rv is None:
            # 这里会执行 Local() 的__setattr__方法 以后在用的时候小心死递归
            # 这里就等于 local() 里的这个 storage[携程号][stack]=[]
            self._local.stack = rv = []
            # 这里的obj 就是 外边传进来的self 也就是 requestContext 对象
        rv.append(obj)
        return rv

    def pop(self):
        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):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None


Local代码:

    这里比较重要的就是 __setattr__ __getattr__方法 以及__init__,特别要注意__init__中 赋值的方式,如果直接通过self.key=key的方式 ,因为该类已经实现了__setattr__方法 中会引起死递归 

    self.__storage__ 对象的最终数据结构 为

             {   stack:   {

                threadnum:[ctx]

                }

             }

    所以最终无论在内部或者外部通过 列表 切片list[:-1] 拿到的都是最新的元素 因为 在  _request_ctx_stack.push 中 rv.append(obj)

  

try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident



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[携程号][stack]=[] storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)

  

LocalProxy代码:

    想要了解LocalProxy这个类,必须要知道什么是偏函数,又叫冻结函数,它可以阻塞函数的运营,在需要的时候执行函数,并且支持预置传参数

    request = LocalProxy(partial(_lookup_req_object, "request"))

     LocalProxy.__init__ 方法 接收的local参数就是 传进来的偏函数 然后在 _get_current_object 方法中调用 

@implements_bool
class LocalProxy(object):


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

    def __init__(self, local, name=None):
        #print("Localproxy---NAME",name)
        # request = LocalProxy(partial(_lookup_req_object, "request"))
        # 这里的local 就是 partial(_lookup_req_object, "request")
        # object.__setattr__(self, "_LocalProxy__local", local) ==== self.__local=local
        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):
        if not hasattr(self.__local, "__release_local__"):
            # 此local 就是传进来的偏函数 _lookup_req_object  其实这里 无论外部对该类做任何操作
            # 都有对应的 双下方法 所有双下方法几乎都调用了方法 等于是个公用的方法 
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError("no object bound to %s" % self.__name__)

    @property
    def __dict__(self):
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError("__dict__")

    def __repr__(self):
        try:
            obj = self._get_current_object()
        except RuntimeError:
            return "<%s unbound>" % self.__class__.__name__
        return repr(obj)

    def __bool__(self):
        try:
            return bool(self._get_current_object())
        except RuntimeError:
            return False

    def __unicode__(self):
        try:
            return unicode(self._get_current_object())  # noqa
        except RuntimeError:
            return repr(self)

    def __dir__(self):
        try:
            return dir(self._get_current_object())
        except RuntimeError:
            return []

    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        # self._get_current_object() 得到的就是 request 对象
        # 这里实际上是从request 反射拿到 method
        return getattr(self._get_current_object(), name)

    def __setitem__(self, key, value):
        """

        获取 session
          执行 偏函数 _get_current_object() 赋值
          obj=_get_current_object() 拿到  session 空字典  SecureCookieSession
          obj[key]=value  执行 SecureCookieSession 的 __setitem__
        :param key:
        :param value:
        :return:
        """
        self._get_current_object()[key] = value

    def __delitem__(self, key):
        del self._get_current_object()[key]

    if PY2:
        __getslice__ = lambda x, i, j: x._get_current_object()[i:j]

        def __setslice__(self, i, j, seq):
            self._get_current_object()[i:j] = seq

        def __delslice__(self, i, j):
            del self._get_current_object()[i:j]

    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    __str__ = lambda x: str(x._get_current_object())
    __lt__ = lambda x, o: x._get_current_object() < o
    __le__ = lambda x, o: x._get_current_object() <= o
    __eq__ = lambda x, o: x._get_current_object() == o
    __ne__ = lambda x, o: x._get_current_object() != o
    __gt__ = lambda x, o: x._get_current_object() > o
    __ge__ = lambda x, o: x._get_current_object() >= o
    __cmp__ = lambda x, o: cmp(x._get_current_object(), o)  # noqa
    __hash__ = lambda x: hash(x._get_current_object())
    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
    __len__ = lambda x: len(x._get_current_object())
    __getitem__ = lambda x, i: x._get_current_object()[i]
    __iter__ = lambda x: iter(x._get_current_object())
    __contains__ = lambda x, i: i in x._get_current_object()
    __add__ = lambda x, o: x._get_current_object() + o
    __sub__ = lambda x, o: x._get_current_object() - o
    __mul__ = lambda x, o: x._get_current_object() * o
    __floordiv__ = lambda x, o: x._get_current_object() // o
    __mod__ = lambda x, o: x._get_current_object() % o
    __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
    __pow__ = lambda x, o: x._get_current_object() ** o
    __lshift__ = lambda x, o: x._get_current_object() << o
    __rshift__ = lambda x, o: x._get_current_object() >> o
    __and__ = lambda x, o: x._get_current_object() & o
    __xor__ = lambda x, o: x._get_current_object() ^ o
    __or__ = lambda x, o: x._get_current_object() | o
    __div__ = lambda x, o: x._get_current_object().__div__(o)
    __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
    __neg__ = lambda x: -(x._get_current_object())
    __pos__ = lambda x: +(x._get_current_object())
    __abs__ = lambda x: abs(x._get_current_object())
    __invert__ = lambda x: ~(x._get_current_object())
    __complex__ = lambda x: complex(x._get_current_object())
    __int__ = lambda x: int(x._get_current_object())
    __long__ = lambda x: long(x._get_current_object())  # noqa
    __float__ = lambda x: float(x._get_current_object())
    __oct__ = lambda x: oct(x._get_current_object())
    __hex__ = lambda x: hex(x._get_current_object())
    __index__ = lambda x: x._get_current_object().__index__()
    __coerce__ = lambda x, o: x._get_current_object().__coerce__(x, o)
    __enter__ = lambda x: x._get_current_object().__enter__()
    __exit__ = lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw)
    __radd__ = lambda x, o: o + x._get_current_object()
    __rsub__ = lambda x, o: o - x._get_current_object()
    __rmul__ = lambda x, o: o * x._get_current_object()
    __rdiv__ = lambda x, o: o / x._get_current_object()
    if PY2:
        __rtruediv__ = lambda x, o: x._get_current_object().__rtruediv__(o)
    else:
        __rtruediv__ = __rdiv__
    __rfloordiv__ = lambda x, o: o // x._get_current_object()
    __rmod__ = lambda x, o: o % x._get_current_object()
    __rdivmod__ = lambda x, o: x._get_current_object().__rdivmod__(o)
    __copy__ = lambda x: copy.copy(x._get_current_object())
    __deepcopy__ = lambda x, memo: copy.deepcopy(x._get_current_object(), memo)

  偏函数代码:

      partial(_lookup_req_object, "request") = _local 明显看到 还是从 _request_ctx_stack中调用的top方法拿到栈顶元素 这个栈顶元素 就是 requestContext对象,忘记的话自己翻一下 r_request_ctx_stack.push 中 rv.append(obj)

      那么这个obj= requestContext,然后通过反射拿值,拿回来的值就是request_class 的值,想想requestContext实例的时候做了什么事情对本身request属性赋值的时候给的是那个类

def _lookup_req_object(name):
    # name=request
    # _request_ctx_stack 就是globals中的 LocalStack() 对象
    # top方法拿到 requestContext也就是ctx 即 top=ctx
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 这里就相当于 去ctx中取request 
    return getattr(top, name)

  

总结与感悟

  1、需要熟悉python的双下方法以及定制化方法,这无疑是提升了简便性,想想这些方法是不是可以写在组件里边特别是数据库

  2、想想这3个类之间的关系,是不是实现了解耦,这玩意是不是可以用在httprunner存储临时数据例如token

  3、偏函数

  4、lambda函数,特别是LocalProxy中几乎全是,只有一个私有方法,使用lambda定制双下方法的用法还是第一次见到,回想一下也是因为lambda也是函数,不过有点猝不及防

  

posted @ 2021-05-29 17:44  Yuan_x  阅读(403)  评论(0编辑  收藏  举报