• Flask上下文管理源码分析 ——(3)


    引出的问题

    Flask如何使用上下文临时把某些对象变为全局可访问

    首先我们做如下的几种情况的假设

    情况一:单进程单线程

    这种情况可以基于全局变量存储临时的对象

    情况二:单进程多线程

    这种情况会出现多个线程共享全局的变量,为了每个线程中的数据不被其他线程修改,可以借助hreading.local对象,为每个线程做唯一的表示用来做键,请求的对象作为值来实现

    多线程共享数据的问题

    import threading
    class Foo(object):
        def __init__(self):
            self.name = 0
    
    local_values = Foo()
    
    def func(num):
        local_values.name = num
        import time
        time.sleep(1)
        print(local_values.name, threading.current_thread().name)
    
    
    for i in range(20):
        th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
        th.start()

    我们可以看到最后把每个线程中对象中name值都变为了19,不能保证每个线程中对象中的值唯一

    使用hreading.local对象可以对每个线程做唯一的表示可以解决上述的问题

    import threading
    
    local_values = threading.local()
    
    def func(num):
        local_values.name = num
        import time
        time.sleep(1)
        print(local_values.name, threading.current_thread().name)
    
    
    for i in range(20):
        th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
        th.start()

    可以看到每个线程中的值唯一

    - 情况三:单进程单线程(多个协程)Flask 的上下文管理就是基于这种情况做的

     在这种情况下使用上面的方法可以保证线程中的数据唯一,但是使用其内部创建多个协程后,hreading.local只能对线程作唯一的标示,协程是在单线程下切换的,所以多个协程还会出现共享数据的问题

    解决的思路:为每个程做唯一的标示,我们可以通过python自带的greenlet模块中的getcurrent来实现

    只需对上面的代码做简单的修改即可

    import threading
    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):
        def __init__(self):
            self.storage = {}
            self.get_ident = get_ident
    
        def set(self,k,v):
            ident = self.get_ident()
            origin = self.storage.get(ident)
            if not origin:
                origin = {k:v}
            else:
                origin[k] = v
            self.storage[ident] = origin
    
        def get(self,k):
            ident = self.get_ident()
            origin = self.storage.get(ident)
            if not origin:
                return None
            return origin.get(k,None)
    
    local_values = Local()
    
    
    def task(num):
        local_values.set('name',num)
        import time
        time.sleep(1)
        print(local_values.get('name'), threading.current_thread().name)
    
    
    for i in range(20):
        th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
        th.start()

    测试的结果如下

    使用面向对象中方法对其进行简单的优化

    在初始化的时候设置属性的时候,为了避免循环引用,我们可以这样做  object.__setattr__(self, 'storage', {})

    class Foo(object):
    
        def __init__(self):
            object.__setattr__(self, 'storage', {})
            # self.storage = {}
    
        def __setattr__(self, key, value):
            self.storage = {'k1':'v1'}
            print(key,value)
    
        def __getattr__(self, item):
            print(item)
            return 'df'
    
    
    obj = Foo()
    
    # obj.x = 123
    # 对象.xx

    修改后的代码如下所示 

    import threading
    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):
    
        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}
    
        def __delattr__(self, name):
            try:
                del self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
    
    
    local_values = Local()
    
    
    def task(num):
        local_values.name = num
        import time
        time.sleep(1)
        print(local_values.name, threading.current_thread().name)
    
    
    for i in range(20):
        th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
        th.start()

    偏函数 (帮助我们传递参数)

    import functools
    
    def func(a1):
        print(a1)
    
    
    new_func = functools.partial(func,666)
    
    new_func() 

    运行结果如下

    面向对象中的魔法方法的简单使用

    import flask.globals
    class Foo(object):
    
        def __init__(self,num):
            self.num = num
    
        def __add__(self, other):
            data = self.num + other.num
            return Foo(data)
    
    obj1 = Foo(11)
    obj2 = Foo(22)
    
    v = obj1 + obj2
    print(v.num)  

    运行结果如下

    chain 帮助我们拼接列表中的值

    from itertools import chain
    
    # def f1(x):
    #     return x + 1
    #
    # func1_list = [f1,lambda x:x-1]
    #
    # def f2(x):
    #     return x + 10
    #
    #
    # new_fun_list = chain([f2],func1_list)
    # for func in new_fun_list:
    #     print(func)
    
    
    v1 = [11,22,33]
    v2 = [44,55,66]
    
    new = chain(v1,v2)
    for item in new:
        print(item)  

    测试结果如下

    Flask上下文源码分析

    Flask中有两种上下文,请求上下文和应用上下文。

    Flask上下文大致可以分为3个步奏

      1 请求到来的时候(为每个线程/协程开辟独立的空间,存入statck中)   

        - ctx = 封装RequestContext(request,session) 

          - ctx放到Local中

      2 执行视图函数的时候(调用每个线程自己的数据)

        - 导入request
        - 调用 _lookup_req_object函数:去local中将requestContext想获取到,再去requestContext中获取request或session

      3- 请求结束(把数据从stack中删除)

        - ctx.auto_pop()    

        - ctx从local中移除。

    当程序启动时执行run方法中的 run_simple方法

    请求上下文  (request,session)

    封装请求相关的数据

        def run(self, host=None, port=None, debug=None,
                load_dotenv=True, **options):
            
            _host = '127.0.0.1'
            _port = 5000
            server_name = self.config.get('SERVER_NAME')
            sn_host, sn_port = None, None
    
            if server_name:
                sn_host, _, sn_port = server_name.partition(':')
    
            host = host or sn_host or _host
            port = int(port or sn_port or _port)
    
            options.setdefault('use_reloader', self.debug)
            options.setdefault('use_debugger', self.debug)
            options.setdefault('threaded', True)
    
            cli.show_server_banner(self.env, self.debug, self.name, False)
    
            from werkzeug.serving import run_simple
    
            try:
                run_simple(host, port, self, **options)
            finally:
                # reset the first request information if the development server
                # reset normally.  This makes it possible to restart the server
                # without reloader and that stuff from an interactive shell.
                self._got_first_request = False

    当请求来的时候会执行 run_simple中的self对象,也就是app.__call__方法,代码如下

        def __call__(self, environ, start_response):
            return self.wsgi_app(environ, start_response)
    

    查看源码中的wsgi_app方法,参数environ表示所有请求的数据,start_response表示响应

        def wsgi_app(self, environ, start_response):
            # 将请求相关的数据environ 封装到request_context 对象中
            ctx = self.request_context(environ) # 生成一个类
            error = None
            try:
                try:
                    # 把请求的对象封装到local中,每个线程 / 协程都是独立的空间存储
                    ctx.push()
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                # 最后把请求在local中的数据删掉
                ctx.auto_pop(error)

    在上面的源码中我们可以看到把所有请求相关的数据封装到了,self.request_context(environ)中,其返回一个RequestContext赋值给ctx我们继续追踪其内部的代码如下所示

        def request_context(self, environ):
         # self指的是app对象
            return RequestContext(self, environ)

    我们可以看到RequestContext类对environ进行了封装,在这里我们可以看到session的数据为None其初始化的方法如下:

    class RequestContext(object):
        def __init__(self, app, environ, request=None):
            self.app = app
            if request is None:
                request = app.request_class(environ)
            self.request = request
            self.url_adapter = app.create_url_adapter(self.request)
            self.flashes = None
            self.session = None
            self._after_request_functions = []
            self.match_request()

    把请求相关的数据添加到Local对象的storage中

     我们继续追踪wsgi_app中的ctx.push的代码如下

        def push(self):
            top = _request_ctx_stack.top
            if top is not None and top.preserved:
                top.pop(top._preserved_exc)
    
            app_ctx = _app_ctx_stack.top
            if app_ctx is None or app_ctx.app != self.app:
                app_ctx = self.app.app_context()
                app_ctx.push()
                self._implicit_app_ctx_stack.append(app_ctx)
            else:
                self._implicit_app_ctx_stack.append(None)
    
            if hasattr(sys, 'exc_clear'):
                sys.exc_clear()
            # self 是request_contenx的对象,其中包含了请求相关的所有数据
            #  _request_ctx_stack==>LocalStack
            _request_ctx_stack.push(self)
    
            if self.session is None:
                session_interface = self.app.session_interface
                self.session = session_interface.open_session(
                    self.app, self.request
                )
    
                if self.session is None:
                    self.session = session_interface.make_null_session(self.app)

    在最下面我们看到了给session中的数据重新赋了值 

    我们查看_request_ctx_stack类和其内部的push方法,把请求封装后的数据_request_ctx当做参数传递进去

    _request_ctx_stack = LocalStack()
    

      

    继续追踪LocalStack类中的push方法代码如下

        def push(self, obj):
            """Pushes a new item to the stack"""
            rv = getattr(self._local, 'stack', None)
            if rv is None:
                # 执行local 对象的__setatr__方法
                self._local.stack = rv = []
            # 把requestContext 对象添加到列表中 self._local.stack = rv = [把requestContext]
            rv.append(obj)
            return rv

    在上面的源码中有一个赋值的操作self._local.stack=rv=[],self.local=local()会触发local()对象中的__setatr__方法参数key=stack,value=[],其__setatr__方法代码如下所示

        def __setattr__(self, name, value):
            # name = stack value = []
            # {"唯一的表示":
            # {stack:[requestContext(ctx)]}
            ident = self.__ident_func__()
            storage = self.__storage__
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}

    ident = self.__ident_func__() 表示的是为线程/协程做唯一的标示,也就以为者当前的请求的上下文添加到了这样的一个字典中

    继续追踪LocalStack类中的push方法中的下rv.append(obj),把当前请求相关的数据添加到stoage中,obj是请求相关的数据RequestCotent, 

    {
        "线程/协助的唯一表示" : {"stack":["当前请求相关的数据"]}
        
    }
    

      

    当请求结束的时候删除storage中的,当前请求的数据  

    我们回去继续追踪wsgi_app中 ctx.auto_pop(error)方法删除请求结束后的数据

    request和session使用内部调用源码分析

    from flask import Flask,request  

    其代码如下

    from functools import partial
    from werkzeug.local import LocalStack, LocalProxy
    
    def _lookup_req_object(name):
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        # 去requestContext中获取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)
        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)
    # partial 偏函数
    request = LocalProxy(partial(_lookup_req_object, 'request'))
    session = LocalProxy(partial(_lookup_req_object, 'session'))
    g = LocalProxy(partial(_lookup_app_object, 'g'))

     在上面的源码中,我们可以看到request是一个LocalProxy对象,其内部的参数通过偏函数partial调用_lookup_req_object函数传的参数默认为'request",_lookup_req_object代码如下

    def _lookup_req_object(name):
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        # 去requestContext中获取request的值
        return getattr(top, name)
    

    top调用的是_app_ctx_stack= LocalStack()类中的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

    其返回的是存储在storage{"stack":{"当前请求":[RequestContent(当前请求的数据)]}},中当前请求的数据,也就是RequestContent对象,所以上面的_llokup_req_object.函数返回的是RequestContent中的g

    class RequestContext(object):
    
        def __init__(self, app, environ, request=None):
            self.app = app
            if request is None:
                request = app.request_class(environ)
            self.request = request
            self.url_adapter = app.create_url_adapter(self.request)
            self.flashes = None
            self.session = None

    所以request = LocalProxy(RequestContent.request)和session= LocalProxy(RequestContent.session)创建一个类,其初始化的方法如下

    class LocalProxy(object):
    
        __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
    
        def __init__(self, local, name=None):
            # self.__loacl = local  local 指的是request
            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)

    通过上面的赋值我们可以知道,最终把RequestContent.reques赋值给self.local = RequestContent.reques

    补充知识:面向对象的通过通过私有字段的取值

    class Foo(object):
    
        def __init__(self):
            self.name = 'alex'
            self.__age = 18
    
        def get_age(self):
            return self.__age
    
    obj = Foo()
    # 强制获取私有字段
    print(obj._Foo__age)

    当我们使用request中的方法的时候,会执行其内部的魔法方法如:

    - print(request)   -->  LocalProxy对象的__str__
    - request.method   -->  LocalProxy对象的__getattr__
    - request + 1      -->  LocalProxy对象的__add__

    通过以上Flask源码的解读,我们可以试着传递一些值做一些简单的修改

    from flask.globals import _request_ctx_stack
    from functools import partial
    
    def _lookup_req_object(name):
        # name = request
        # top= ctx
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError('不存在')
        # return ctx.request
        return getattr(top, name)
    
    class Foo(object):
        def __init__(self):
            self.xxx = 123
            self.ooo = 888
    
    req = partial(_lookup_req_object,'xxx')
    xxx = partial(_lookup_req_object,'ooo')
    
    # 当前求刚进来时
    _request_ctx_stack.push(Foo())
    
    # 使用
    # obj = _request_ctx_stack.top
    # obj.xxx
    v1 = req()
    print(v1)
    v2 = xxx()
    print(v2)
    
    # 请求终止,将local中的值移除
    _request_ctx_stack.pop()

    后台打印的结果如下

    应用上下文(current__app,g)

    源码wsgi_app

        def wsgi_app(self, environ, start_response):
            # 将请求相关的数据environ 封装到request_context 对象中
            # ctx.app = app
            # ctx.request = app.request_class(environ)
            ctx = self.request_context(environ) # 生成一个类
            error = None
            try:
                try:
                    # 把请求的对象封装到local中,每个线程 / 协程都是独立的空间存储
                    ctx.push()
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                # 最后把请求在local中的数据删掉
                ctx.auto_pop(error)

    追踪ctx.push代码如下

        def push(self):
            top = _request_ctx_stack.top
            if top is not None and top.preserved:
                top.pop(top._preserved_exc)
    
            app_ctx = _app_ctx_stack.top
            if app_ctx is None or app_ctx.app != self.app:
                # 应用上下文 创建一个对象  app_ctx =  AppContext(object)  app_ctx.g   app_ctx.app
                app_ctx = self.app.app_context()
                app_ctx.push()
                self._implicit_app_ctx_stack.append(app_ctx)
            else:
                self._implicit_app_ctx_stack.append(None)
    
            if hasattr(sys, 'exc_clear'):
                sys.exc_clear()
            # self 是request_contenx的对象,其中包含了请求相关的所有数据
            #  _request_ctx_stack==>LocalStack
            _request_ctx_stack.push(self)
    
            if self.session is None:
             session_interface
    = self.app.session_interface self.session = session_interface.open_session( self.app, self.request ) if self.session is None: self.session = session_interface.make_null_session(self.app)

    app_cxt = self.app.app_context() 的源码了解到其返回的是一个AppContext对象

        def app_context(self):
            return AppContext(self)

    AppContext源码如下

    class AppContext(object):
        def __init__(self, app):
            self.app = app
            self.url_adapter = app.create_url_adapter(None)
            self.g = app.app_ctx_globals_class()
    
            # Like request context, app contexts can be pushed multiple times
            # but there a basic "refcount" is enough to track them.
            self._refcnt = 0

     我们追踪g变量的源码发现其用法类似字典

    class _AppCtxGlobals(object):
    
        def get(self, name, default=None):
    
            return self.__dict__.get(name, default)
    
        def pop(self, name, default=_sentinel):
    
            if default is _sentinel:
                return self.__dict__.pop(name)
            else:
                return self.__dict__.pop(name, default)
    
        def setdefault(self, name, default=None):
    
            return self.__dict__.setdefault(name, default)

     回到上面的代码  app_ctx对象,从中我们可以拿到app_ctx和app_ctx.app这就是我们要找的应用上下文了

    继续追踪 app_ctx.push()源码如下

        def push(self):
            self._refcnt += 1
            if hasattr(sys, 'exc_clear'):
                sys.exc_clear()
            _app_ctx_stack.push(self)
            appcontext_pushed.send(self.app)
    

    追踪 _app_ctx_stack = LocalStack()中的push方法

        def push(self, obj):
            """Pushes a new item to the stack"""
            rv = getattr(self._local, 'stack', None)
            if rv is None:
                # 执行local 对象的__setatr__方法
                self._local.stack = rv = []
            # 把requestContext 对象添加到列表中 self._local.stack = rv = [把requestContext]
            rv.append(obj)
            return rv

     

    在上面的源码中有一个赋值的操作self._local.stack=rv=[],self.local=local()会触发local()对象中的__setatr__方法参数key=stack,value=[],其__setatr__方法代码如下所示

        def __setattr__(self, name, value):
            # name = stack value = []
            # {"唯一的表示":
            # {stack:[requestContext(ctx)]}
            ident = self.__ident_func__()
            storage = self.__storage__
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}
    

      

    继续追踪LocalStack类中的push方法中的下rv.append(obj),把当前请求相关的数据添加到stoage中,obj是请求相关的数据RequestCotent, 

    {
        "stack" : {"线程/协助的唯一表示":["当前请求相关的数据"]}
        
    }

    g变量和 current_app

    from flask import Flask,g,current_app  
    其代码如下
    from functools import partial
    from werkzeug.local import LocalStack, LocalProxy
    
    def _lookup_req_object(name):
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        # 去requestContext中获取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)
        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)
    # partial 偏函数
    request = LocalProxy(partial(_lookup_req_object, 'request'))
    session = LocalProxy(partial(_lookup_req_object, 'session'))
    g = LocalProxy(partial(_lookup_app_object, 'g'))

     在上面的源码中,我们可以看到g是一个LocalProxy对象,其内部的参数通过偏函数partial调用_lookup_app_object函数传的参数默认为'g",_lookup_app_object代码如下

    def _lookup_req_object(name):
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        # 去requestContext中获取request的值
        return getattr(top, name)

    top调用的是app_cxt =  LocalStack() 类中的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

    其返回的是存储在storage{"stack":{"当前请求":[RequestContent(当前请求的数据)]}},中当前请求的数据,也就是RequestContent对象,所以上面的_llokup_req_object.函数返回的是AppContext中的g

    class AppContext(object):
    
        def __init__(self, app):
            self.app = app
            self.url_adapter = app.create_url_adapter(None)
            self.g = app.app_ctx_globals_class()
    
            # Like request context, app contexts can be pushed multiple times
            # but there a basic "refcount" is enough to track them.
            self._refcnt = 0

    所以g= LocalProxy(AppContext.g) 和   current_app = LocalProxy(AppContext.app)创建一个类,其初始化的方法如下

    class LocalProxy(object):
    
        __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
    
        def __init__(self, local, name=None):
            # self.__loacl = local  local 指的是request
            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)

    当我们使用g和current_app 中的方法的时候,会执行其内部的魔法方法如:

    - print(g)   -->  LocalProxy对象的__str__
    - g.get(')-->  LocalProxy对象的__getattr__

    多app应用

    from werkzeug.wsgi import DispatcherMiddleware
    from werkzeug.serving import run_simple
    from flask import Flask, current_app
    
    app1 = Flask('app01')
    
    app2 = Flask('app02')
    
    
    
    @app1.route('/index')
    def index():
        return "app01"
    
    
    @app2.route('/index2')
    def index2():
        return "app2"
    
    # http://www.oldboyedu.com/index
    # http://www.oldboyedu.com/sec/index2
    dm = DispatcherMiddleware(app1, {
        '/sec': app2,
    })
    
    if __name__ == "__main__":
        app2.__call__
        run_simple('localhost', 5000, dm)

    with在类中的使用

    class SQLHelper(object):
    
        def open(self):
            pass
    
        def fetch(self,sql):
            pass
    
        def close(self):
            pass
    
        def __enter__(self):
            self.open()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.close()
    
    with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值
        obj.fetch('xxxx')
        # 当执行完毕后,自动调用类 __exit__ 方法

    flask的local中保存数据时,使用列表创建出来的栈。为什么用栈?

    在写脚本的时候一个线程中执行多个app他们的关系还是嵌套的

            - 如果写web程序,web运行环境;栈中永远保存1条数据(可以不用栈)。

            - 写脚本获取app信息时,可能存在app上下文嵌套关系。

    from flask import Flask,current_app,globals,_app_ctx_stack
    
    app1 = Flask('app01')
    app1.debug = False # 用户/密码/邮箱
    # app_ctx = AppContext(self):
    # app_ctx.app
    # app_ctx.g
    
    app2 = Flask('app02')
    app2.debug = True # 用户/密码/邮箱
    # app_ctx = AppContext(self):
    # app_ctx.app
    # app_ctx.g
    
    
    
    with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
        # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
        print(_app_ctx_stack._local.__storage__)
        print(current_app.config['DEBUG'])
    
        with app2.app_context():
            # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
            print(_app_ctx_stack._local.__storage__)
            print(current_app.config['DEBUG'])
    
        print(current_app.config['DEBUG'])

    打印的数据如下

     关于g变量简单的使用

     我们可以在请求到来的时候,给用户赋予一些权限,在视图函数中使用

    from flask import Flask,request,g
    
    app = Flask(__name__)
    
    @app.before_request
    def before():
        g.permission_code_list = ['list','add']
    
    
    @app.route('/',methods=['GET',"POST"])
    def index():
        print(g.permission_code_list)
        return "index"
    
    
    if __name__ == '__main__':
        app.run()
  • 相关阅读:
    TCP Three-Way handshake
    java 连接mysql 和sql server2008代码
    关于dispatch_sync死锁问题
    MacBook Pro使用初体验之Mac快捷键汇总(持续更新中)
    Java对象的内存布局
    Android之adb
    ADT开发中的一些优化设置:代码背景色、代码字体大小、代码自动补全
    实用的eclipse adt 快捷键
    python操作Excel读--使用xlrd
    解决ImportError: cannot import name HTTPConnection的方法
  • 原文地址:https://www.cnblogs.com/crazymagic/p/9589351.html
Copyright © 2020-2023  润新知