zoukankan      html  css  js  c++  java
  • Flask:上下文管理

    1. werkzurg

    from werkzeug.serving import run_simple 

    def run(environ,start_response):
    reuturn [b
    'hello world']

    if __name__ == "__main__":
    run_simple(
    'localhost',4000,run) # run_simple --> 启动监听接收 socket(一个死循环); run 会加上 () 去执行

    2. 所有请求的入口

    def __call__(self, environ, start_response):        # 当请求来的时候,才会执行 __call__ 方法
        """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)        # wsgi_app()  真正去执行源码
        
    # app.run()  这行代码执行的时候,并没有执行 __call__ 方法

    3. Local()  ---> 把不同线程的数据隔离

    import time
    import threading
    
    # 获取 线程或者协程的唯一id
    try:
        import greenlet
        get_ident = greenlet.getcurrent
    except Exception:
        get_ident = threading.get_ident
        
        
    class Local(object):
        DIC = {}    # 数据都放到这个大字典中
        
        def __getattr__(self,item):
            get_ident = get_ident()
            if get_ident in self.DIC:
                return self.DIC[get_ident].get(item)
                
            return None
            
            
        def __setattr__(self,key,value):
            get_ident = get_ident()
            
            if get_ident in self.DIC:
                self.DIC[get_ident][key] = value
                
            else:
                self.DIC[get_ident] = {key:value}
            
        
    # __getattr__ 和  __setattr__ 的用处  ; 同一个对象的同一个属性,根据线程和协程号这个唯一标识,来获取不同的值 

    4. 上下文管理(第一次)

    请求到来时:
        # ctx = RequestContext(self,environ)  # self 是 app对象,environ表示请求相关的原始数据
        # ctx.request = Request(environ)  
        # ctx.session = None
        
        # 将包含了 request和session 的ctx对象打包放到某个地方(相当于一个大字典;根据线程或协程加个唯一标识,放进去的,所以数据相互隔离)
            {
                1232:{ctx:ctx对象},
                ...
            }
    视图函数:
        from flask import request,session
        # 上述代码背后的过程:根据当前线程或者协程的唯一标识,取到 ctx对象,然后取到 request和session
    请求结束:
        根据当前线程的唯一标识,将 大字典 中该线程对应的数据移除

    5. 偏函数

    import functools
    
    def func(a,b):
        return a+b
        
    new_func = functools.partial(func,6)    # 第一个参数是函数名;第二个参数 6 会当作 func 的第一个参数自动传入 func 中
    
    ret = new_func(2)        # new_func(2) 执行时, 就是 func 函数在执行,6作为第一个参数自动传入 func(),2作为第二个参数传入func
    print(ret)
    
    # 偏函数 ---> 帮助开发人员自动传递参数

    6. 基于列表维护一个栈:

    class Stack(object):
        """
        基于列表维护一个栈
        """
        def __init__(self):
            self.stack = []
    
        def push(self,item):
            self.stack.append(item)
    
        def pop(self):
            return self.stack.pop()
    
        def top(self):
            """
            读取最后一个值
            """
            return self.stack[-1]

    7. flask 中的  Local 类 和 LocalStack 类

    # 自己写的 Local:
    try:
        from greenlet import getcurrent as get_ident
    except Exception as e:
        from threading import get_ident
    
    class Local(object):
        """docstring for Local"""
        def __init__(self, arg):
            object.__setattr__(self,"storage",{})        # Local 实例化的时候会在对象中存一个 "storage" 的空字典;注意这种设置对象属性值点语法的方法
            # 这是不能使用 self.storage = {}  ,因为 self.storage 会调用下面的 __setattr__ ,__setattr__ 中也有 self.storage
    
        def __setattr__(self,key,value):
            get_ident = get_ident()        # 线程号
    
            if get_ident not in self.storage:
                self.storage[get_ident] = {key,value}
            else:
                self.storage[get_ident][key] = value
    
    
        def __getattr__(self,item):
            get_ident = get_ident()
    
            if get_ident in self.storage:
                return self.storage[get_ident].get(item)
    
            return None
    
    
    # 实现的效果: 一个对象, .同一个属性的时候 可获取到不同的值(根据当前线程号等唯一标识)---> 为每个线程开辟一块独立的空间
    
    
    # flask 中的 Local 和 LocalStack 类:
    
    # flask 源码中的 Local 部分源码:
    # 查询“路径”: flask 中的 globals  ---> LocalStack  ---> Local 
    
    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__() 的作用:只允许该类的实例添加 __slots__ () 中的属性
        __slots__ = ('__storage__', '__ident_func__')
    
        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)
    
    
    obj = Local()
    obj.stack = []        
    """
    此时 obj. 的时候, 并不是给 obj 对象新添加了一个属性,而是把 "stack" 作为 key,[] 作为 value 放到了 __storage__ 这个大字典中,如下:
    {
        "线程号":{"stack":[]}
    }
    """
    obj.stack.append("abc")
    obj.stack.append("123")
    obj.stack.pop()
    # 通过重写 __setattr__ 方法,Local对象所有通过 . 方法设置属性值时,都把 属性名和其对应的值放到了 __storage__ 这个大字典当前线程号对应的字典中;
    # 通过重写 __getattr__ 方法,Local对象通过 . 方法获取属性值时,也是从 __storage__ 这个大字典中 当前线程号对应的字典中 获取该属性key 对应的值
    
    """
    LocalStack 存的数据格式:
    __storage__ = {
        1231:{"stack":[]},
        ...
    }
    """
    
    # 每次 obj.stack 这个栈 append 或者 pop 时都要加上 .stack ,下面做简化: 用一个代理 LocalStack
    
    class LocalStack(object):
        def __init__(self):
            self._local = Local()        # 实例化 Local ---> 得到一个 __storage__ 属性对应的空字典 :  Local()对象.__storage__ = {}
    
        def push(self,item):
            """Pushes a new item to the stack"""
            rv = getattr(self._local, 'stack', None)        # self._local.stack  ---> 触发 Local 的 __getattr__ 方法
    
            if rv is None:
                self._local.stack = rv = []            # self._local.stack 和 rv 同时指向同一个引用; 此处执行 __setattr__ 方法
            rv.append(item)
            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:
                return stack[-1]        # 读取栈中的最后一个值
            else:
                return stack.pop()
    
    ls = LocalStack()
    ls.push("abc")
    ls.push("123")
    ls.pop()
    
    """
    __storage__ = {
        "线程号":{"stack":[]}
    }
    """
    
    # LocalStack 对象相当于一个代理,帮助我们在Local中维护一个列表,并把这个列表维护成一个栈
    
    
    """
    小结:
    Local 作用: 为每个线程开辟一个独立的空间 (内部维护了一个 __storage__ 的大字典,该字典中的key是当前线程号,字典的value是该线程独立的数据)
    LocalStack 作用: 帮助我们把 Local 中的一个列表维护成一个栈
    """

     8. 以上综合应用

    # LocalStack的对象 ls push() 时不再传字符串,而是传一个字典,如: ls.push({"request":"xxx","session":"yyy"})
    # 全局变量只有在程序启动时会加载执行

    上下文管理:

    1. request 上下文管理  (请求上下文管理)

    app.__call__  --->  app.wsgi_app  
    wsgi_app 中 :
        1. ctx = self.request_context(environ) ---> 封装请求数据; environ --> 原始的请求数据
            ctx = RequestContext(self, environ)
            ctx.request = Request(environ)
            ctx.session = None
    
        2. ctx.push()
            ---> _request_ctx_stack.push(self)        
                1.    # self 即是 ctx 对象, ctx 又封装了 request 和 session
                    # self = ctx = (request,session)
    
                2. _request_ctx_stack = LocalStack()    ---> 这行代码执行完后(主要是 LocalStack()),
                                                             会在内存中创建一个名字为 __storage__ 的空字典
                    
                    ---> _request_ctx_stack 帮助我们维护一个 __storage__ = {}
            
            ---> ctx.push() 执行完后 ---> __storage__ = {
                                                            "当前线程号":{"stack":[封装了 request 和 session 的ctx对象]}
                                                        }
    
    wsgi(帮助我们初步处理请求)  ---> 然后执行 __call__ 方法  ---> __call__ 方法 执行 wsgi_app 方法
    ---> wsgi_app 方法中: 
            1. 创建 ctx 对象 :  ctx = RequestContext(self, environ)        # environ 中有 request 和 session
            2. 执行 push 方法      ctx.push()     ---> LocalStack , 把 ctx对象添加到 Local 对象中,
                                                                ---> Local 中的数据格式:
                                                                        __storage__ = {
                                                                            "当前线程号":{"stack":[封装了request和session的ctx对象]}
                                                                        }
    
    # 走到视图函数 取数据 :
    # 视图函数取数据也是通过 LocalStack 去 Local 中取数据(ctx对象),而不能直接去 Local 中取
    原理: ctx = _request_ctx_stack.top
           ctx.request  # 取 request
           ctx.session  # 取 session
           
        @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
            
    """
    第一阶段:请求到来
        将 request 和 session 相关数据封装到 ctx=RequestContext() 对象中。
        再通过 LocalStack 将 ctx 对象添加到 Local 中
        __storage__ = {
            1231:{"stack":[ctx(request,session)]}
        }
    第二阶段: 视图函数中获取 request 或 session
        方式一: 直接找 LocalStack 获取
            _request_ctx_stack.top.request
        方式二: 通过代理 LocalProxy 获取
            from flask import request,session
            
    上面两种方式都是通过 LocalStack 去 Local 中取值
    """
            
    # 通过代理 LocalProxy 获取 request 或 session
    request = LocalProxy(partial(_lookup_req_object, 'request'))    ---> 获取 ctx 中的 request
    session = LocalProxy(partial(_lookup_req_object, 'session'))    ---> 获取 ctx 中的 session
    
    # request 是一个 LocalProxy 的对象,以 request.method 为例, .mothod 会执行 LocalProxy 的 __getattr__ 方法:
    @implements_bool
    class LocalProxy(object):
        def __init__(self, local, name=None):
            object.__setattr__(self, '_LocalProxy__local', local)        # __local 是 LocalProxy 在实例化的时候传入的参数
            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 __getattr__(self, name):        
            if name == '__members__':
                return dir(self._get_current_object())
                
            # 以 request.method 为例, 此时 name == "method"
            return getattr(self._get_current_object(), name)
            # 上面的 return 等同于下面的两句代码:
            """
            obj = self._get_current_object()    --> 从ctx中获取 request
            return getattr(obj,name)            --> 从 request 中获取 method
            """
    
    
        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()            # 执行偏函数
            # __local 是 LocalProxy 在实例化的时候传入的参数,即偏函数 partial(_lookup_req_object, 'request')--> 获取到ctx中的request
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)
    
    
    def _lookup_req_object(name):        # name 为 "request" 或者 " session"
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        return getattr(top, name)
  • 相关阅读:
    二叉树
    树的存储表示
    Jarvis OJ | WEB
    xctf | pwn进阶
    《C++Primer Plus》 | 处理数据
    xctf---stack2 | gdb&IDA 调试
    IDA | Windows主机与Ubuntu 16.04远程调试
    ROP | 中级
    IDA | 窗口
    epub reading
  • 原文地址:https://www.cnblogs.com/neozheng/p/11610727.html
Copyright © 2011-2022 走看看