1. werkzurg

from werkzur.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("")
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("")
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)

Flask:上下文管理的更多相关文章

  1. Flask上下文管理、session原理和全局g对象

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  2. Flask上下文管理

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  3. Flask上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  4. Flask 上下文管理

    为什么用threading.local? 我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程 ...

  5. Flask上下文管理及源码刨析

    基本流程概述 - 与django相比是两种不同的实现方式. - django/tornado是通过传参数形式实现 - 而flask是通过上下文管理, 两种都可以实现,只不实现的方式不一样罢了. - 上 ...

  6. flask 上下文管理 &源码剖析

    基本流程概述 - 与django相比是两种不同的实现方式. - django/tornado是通过传参数形式实现 - 而flask是通过上下文管理, 两种都可以实现,只不实现的方式不一样罢了. - 上 ...

  7. Flask 上下文管理-- (session,request,current_app的传递)--类似本地线程实现,以及多app应用

    Flask session,request,current_app的传递 请求上下文的作用 -- 封装请求相关得数据(request,session) 请求上下文 request session re ...

  8. Flask上下文管理机制流程(源码剖析)

    Flask请求上下文管理 1 偏函数 partial 使用该方式可以生成一个新函数 from functools import partial def mod( n, m ): return n % ...

  9. Flask - 上下文管理(核心)

    参考 http://flask.pocoo.org/docs/1.0/advanced_foreword/#thread-locals-in-flask https://zhuanlan.zhihu. ...

  10. python - Flask 上下文管理 流程

    上下文管理:    - 请求上下文 (ctx=RequestContext())  : request/session    - App上下文  (app_ctx=AppContext())  : a ...

随机推荐

  1. HearthBuddy CSharpCodeProvider 如何编译cs文件

    源码 源码1 namespace Hearthbuddy.Windows{    // Token: 0x02000041 RID: 65    public class MainWindow : W ...

  2. Qt之CMake和MinGW编译OpenCV

    这里编译的是最新版OpenCV3.4.3,编译过程比较曲折,大部分编译错误都可以查得到解决方法,也遇到了Google无果的错误,好在最后还是解决了,特此记录. 编译环境 win10-64bit Qt5 ...

  3. mha之vip漂移 配置binlog-server备份服务器 Atlas

    MHAvip漂移 配置 通过MHA自带脚本方式,管理虚拟IP的漂移 获取管理脚本master_ip_failover cp master_ip_failover /usr/local/bin/ #脚本 ...

  4. Spring事务注解分析

    1.使用spring事务注解 2.手写事务注解 1).sql执行器 2).事务注解定义 3).AOP实现事务具体实现(同一个线程中使用同一个连接) 4).应用使用注解前 5).应用使用注解后

  5. Linux环境下warning: no newline at end of file

    今天在Windows下VS2012写了一个程序, 然后放在Linux系统下进行编译.Linux下使用的编译器是CC,结果,一编译出现了很多诸如下面的警告信息,似乎每一个.cpp和.h文件都有. Ite ...

  6. WebApi实现通讯加密 (转)

    http://www.cnblogs.com/jonneydong/p/WebApi_Encryption.html 一. 场景介绍: 如题如何有效的,最少量的现有代码侵入从而实现客户端与服务器之间的 ...

  7. JAVA 基础编程练习题1 【程序 1 不死神兔】

    1 [程序 1 不死神兔] 题目:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月 又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 程序分析: 兔子 ...

  8. CentOS8 使用 aliyun 阿里云 镜像站点的方法

    CentOS8现已可使用国内的aliyun阿里云镜像站,方法如下: 用cd命令切换到yum.repos目录,备份原始的3个repo文件:cd /etc/yum.repos.d/sudo cp Cent ...

  9. OSI七层模型非专业简介

    七层模型从下到上分别是:物理层.数据链路层.网络层.传输层.会话层.表示层.应用层. 1.第一层物理层:最简单理解,我们所看到的网线,就是物理层的.物理层是传输媒介,所以无线网络也算是物理层,在线路中 ...

  10. 安装aodh

    在控制节点上执行 #!/bin/bash MYSQL_ROOT_PASSWD='m4r!adbOP' GNOCCHI_PASSWD='gnocchi1234!' CEILOMETER_PASSWD=' ...