flask请求上下文

先看一个例子:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading

# local_values = threading.local()


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(5):
    th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
    th.start()
4 线程0
4 线程1
4 线程3
4 线程2
4 线程4

  

上述结果不是我们想要的,local_values.name的值被最后一个覆盖了.............................

flask的request和session设置方式比较新颖,如果没有这种方式,那么就只能通过参数的传递。

flask是如何做的呢?

1. 本地线程,保证即使是多个线程,自己的值也是互相隔离。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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()
from _thread import  get_ident
import threading



def task(num):
    print(get_ident())

for i in range(10):
    th = threading.Thread(target=task,args=(i,),name='线程%s' % i)
    th.start()
View Code

如上图所示,每个线程都有一个唯一标识

__setattr__、__getattr__的用法
class Foo(object):
    def __init__(self):
        pass

    def __setattr__(self, key, value):
        print(key,value)


    def __getattr__(self, item):
        print(item)

obj = Foo()

obj.xxx=123
obj.xxx
__setattr__ __getattr__

自定义Local

 1 import threading
 2 try:
 3     from greenlet import getcurrent as get_ident # 协程
 4 except ImportError:
 5     try:
 6         from thread import get_ident
 7     except ImportError:
 8         from _thread import get_ident # 线程
 9 
10 
11 class Local(object):
12 
13     def __init__(self):
14         #递归执行__setattr__
15         # self.__storage__={}
16         # self.__ident_func__=get_ident
17         object.__setattr__(self, '__storage__', {})
18         object.__setattr__(self, '__ident_func__', get_ident)
19 
20 
21     def __getattr__(self, name):
22         try:
23             return self.__storage__[self.__ident_func__()][name]
24         except KeyError:
25             raise AttributeError(name)
26 
27     def __setattr__(self, name, value):
28         ident = self.__ident_func__()
29         storage = self.__storage__
30         try:
31             storage[ident][name] = value
32         except KeyError:
33             storage[ident] = {name: value}
34 
35     def __delattr__(self, name):
36         try:
37             del self.__storage__[self.__ident_func__()][name]
38         except KeyError:
39             raise AttributeError(name)
40 
41 
42 local_values = Local()
43 
44 
45 def task(num):
46     local_values.name = num
47     import time
48     time.sleep(1)
49     print(local_values.name, threading.current_thread().name)
50 
51 
52 for i in range(5):
53     th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
54     th.start()
55 
56 
57 
58 执行结果:
59 0 线程0
60 1 线程1
61 2 线程2
62 4 线程4
63 3 线程3
View Code

2. 上下文原理

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from functools import partial
from flask.globals import LocalStack, LocalProxy
 
ls = LocalStack()
 
 
class RequestContext(object):
    def __init__(self, environ):
        self.request = environ
 
 
def _lookup_req_object(name):
    top = ls.top
    if top is None:
        raise RuntimeError(ls)
    return getattr(top, name)
 
 
session = LocalProxy(partial(_lookup_req_object, 'request'))
 
ls.push(RequestContext('c1')) # 当请求进来时,放入
print(session) # 视图函数使用
print(session) # 视图函数使用
ls.pop() # 请求结束pop
 
 
ls.push(RequestContext('c2'))
print(session)
 
ls.push(RequestContext('c3'))
print(session)
View Code

3. Flask内部实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
from greenlet import getcurrent as get_ident
 
 
def release_local(local):
    local.__release_local__()
 
 
class Local(object):
    __slots__ = ('__storage__', '__ident_func__')
 
    def __init__(self):
        # self.__storage__ = {}
        # self.__ident_func__ = get_ident
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)
 
    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}
 
    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
 
 
class LocalStack(object):
    def __init__(self):
        self._local = Local()
 
    def __release_local__(self):
        self._local.__release_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
 
 
stc = LocalStack()
 
stc.push(123)
v = stc.pop()
 
print(v)
View Code

------------------------------------------源码分析(重点):----------------------------------------------------------------------

首先看这段代码:

if __name__ == '__main__':
	# 1.1
    app.__call__
    app.request_class
    response_class = Response
    app.run()

在上面提到了上下文的原理:

-----------------------LocalStak-----------------Local-----------------------------------  

globals.py中有这么段代码,定义了request

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

  

# Local类

class Local(object):
    __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__
        """
        {
			唯一标识(__ident_func__):{
				stack:[ctx/requestcontext,]//栈
			}
        }
        """
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

  

# LocalStack类

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

# LocalStack()就是我们之前写过的一个多线程数据相互隔离的类,每个线程都有一个唯一标识
# 每个线程都有一个独立的存储数据和访问数据的空间
# __storage__   __ident_func__

# werkzeuglocal.py
# Local类  每个线程唯一标识 可以存储数据
# LocalStack类 实例化Local类

# LocalStack类
def push(self, obj):
    # 刚开始stack=[]
    rv = getattr(self._local, 'stack', None)
    if rv is None:
        self._local.stack = rv = []
    rv.append(obj)//执行Local的__setattr__方法
    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()

  

以上都是启动会加载的文件 ,创建唯一标识,并给这个标识赋值 stack=[],stack是个栈,存储着每个用户请求的对象(数据)RequestContext对象(ctx)

{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}

  

--------------------------------接下来开始看看执行流程:---------(app)-----------------------------------------------

app.__call__
app.request_class
response_class = Response
app.run()

执行app.__call__方法 

app.run()    =》run_simple(host, port, self, **options) =》app.__call__

# flaskapp.py
# 主要从这里入手 分析代码
# Flask类(app)

 app.py

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

app.py

def wsgi_app(self, environ, start_response):
    #将请求相关的数据environ封装到了RequestContext对象中
    #再将对象封装到Local中(每个线程/每个协程独立空间存储)
    #request_context对象中有如下:
        #ctx.app 当前APP
        #ctx.request Request对象(封装请求相关的东西)
        #ctx.session
        #RequestContext类  ctx.py
    ctx = self.request_context(environ)
    error = None
    try:
        try:
            #RequestContext.push()->LocalStack.push
            #将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
            #_request_ctx_stack = LocalStack()
             """
                {
                    唯一标识(__ident_func__):{
                        stack:[ctx/requestcontext,]//
                    }
                }
            """
            # 将ctx通过LocalStack添加到Local中
            # 此时ctx中的session中已经有值了
            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
        #RequestContext.pop()->LocalStack.pop(_request_ctx_stack.pop())
        #_request_ctx_stack = LocalStack()
        #最后将自己请求在Local中保存的数据清除
        ctx.auto_pop(error)

 ctx = self.request_context(environ) 记住了!!!

def request_context(self, environ):
  #self=app return RequestContext(self, environ)

ctx就是request_context对象

#request_context对象中有如下属性:
  #ctx.app 当前APP
  #ctx.request Request对象(封装请求相关的东西)
  #ctx.session

  

ctx.push():

#执行RequestContext.push()->LocalStack.push
#将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
"""
{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""

 

--------------------------RequestContext------------ctx.py-------------------------------------

# flaskctx.py
# RequestContext类
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._implicit_app_ctx_stack = []

    self.preserved = False

    self._preserved_exc = None

    self._after_request_functions = []

    self.match_request()


request = app.request_class(environ)




#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对象
        # app_ctx.app = Flask对象
        # app_ctx.g=对象(用于一个周期内要保存的值,相当于全局变量)
        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()

    #selft 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
    #_request_ctx_stack = LocalStack()
    #相当于执行LocalStack.push()
    #self 就是RequestContext类
    _request_ctx_stack.push()

    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)

RequestContext对象中有request,session等对象,#ctx.push执行的就是RequestContext.push,其中有这么段代码

 _request_ctx_stack.push()
	#self 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
	#_request_ctx_stack = LocalStack()
	#相当于执行LocalStack.push()
	#self 就是RequestContext类
      _request_ctx_stack.push()

  

组中执行的是Local中 storage[ident][name] = value

最终目的就是把RequestContext对象也就是ctx放到localStack中的唯一标识字典中的stack栈中

以上明白了吗?不明白可以去看看源码。。。。。  现在已经出现了

ctx=RequestContext     app    LocalStack      Local     四个类

globals.py  刚开始启动加载的文件

到这里 请求上下文已经差不多了,每个请求都有一个独立数据存储空间,互补影响彼此。就是把request对象放到RequestContext.request属性里面,然后又把RequestContext对象放到每个线程(请求)或者是协程的一个唯一标识的stack栈中

"""
storage:{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""

视图中执行print(request)发生了什么?

--------------------------request-------------------------------------------------

# flaskglobals.py
# 视图中执行print(request)发生了什么?
# 执行LocalProxy类中的__str__方法



# 在globals.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'))

# 偏函数做了啥?
#partial(_lookup_req_object, 'request')


# 函数_lookup_req_object  去LocalStack获取唯一标识栈里面的值(RequestContext对象)
def _lookup_req_object (name):
    # _request_ctx_stack = LocalStack()
    # 返回的其实就是刚开始塞到每个线程唯一标识里面的那个ctx/requestContext对象
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)
    # 到requestContext对象取request属性值 request对象
    return getattr(top, name)



# LocalProxy类中的__str__方法 最终就是执行_lookup_req_object函数而已
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)





在LocalStack类中:
    @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

    def _get_current_object(self):
    if not hasattr(self.__local, '__release_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__)
request = LocalProxy(partial(_lookup_req_object, 'request'))
这段代码要仔细看看了

print(request)  =>       LocalProxy。__str__    =>       LocalProxy.__str__ = lambda x: str(x._get_current_object()) =>( _get_current_object 里面执行了 )self.__local()

LocalProxy.__local  就是传入的偏函数(

partial(_lookup_req_object, 'request')

最后执行self.__local() 相当于执行偏函数,返回request对象了

LocalProxy类中

    def __init__(self, local, name=None):
        #Local = partial(_lookup_req_object, 'request')
        #_local =  _LocalProxy__local私有字段访问
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            object.__setattr__(self, '__wrapped__', local)

    def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:

_request_ctx_stack的应用

# _request_ctx_stack的应用
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()
_request_ctx_stack

上下文
- threading.Local和Flask自定义Local对象
- 请求到来
- ctx = 封装RequestContext(request,session)
- ctx放到Local中
- 执行视图时
- 导入request
- print(request) --> LocalProxy对象的__str__
- request.method --> LocalProxy对象的__getattr__
- request + 1 --> LocalProxy对象的__add__
- 调用 _lookup_req_object函数:去local中将requestContext想获取到,再去requestContext中获取request或session
- 请求结束
- ctx.auto_pop()
- ctx从local中移除。

多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)
View Code

离线脚本:

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'])
View Code
"""
需求:不用数据库连接池,显示数据库连接
"""
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()


# obj = SQLHelper()
# obj.open()
# obj.fetch('select ....')
# obj.close()


with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值
    obj.fetch('xxxx')
    # 当执行完毕后,自动调用类 __exit__ 方法

# 以后如果遇到:
View Code
原文地址:https://www.cnblogs.com/sunlong88/p/9606965.html