源码流程

创建对象

from flask import Flask 

"""
1 实例化对象 app
"""
app = Flask(__name__) """
2 设置路由
将路由关系放在 app.url_map = {} 中
"""
@app.route("/index")
def index():
return "index" if —__name__ == "__main__":
"""
3 启动socket服务端
"""
app.run() """
4 用户请求到来就会执行 __call__ 方法
"""

run

# 启动入口简约版代码
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple class Flask(object):
def __call__(self,environ, start_response):
response = Response('hello')
return response(environ, start_response) def run(self):
run_simple('127.0.0.1', 8000, self)

run_simple(host,port,self,**options)
会对第三个传入的参数加()进行执行
第三个参数如果是app对象就执行其 __call__ 方法

__call__

def __call__(self,environ, start_response):
# environ 请求相关的所有数据 第一手的数据,由wsgi进行的初步封装
# start_response 用于设置响应相关数据
return wsgi_app(environ,start_response)

 call 返回的是 wsgi_app 的执行结果

wsgi_app

wsgi_app 里面做的事情就很多了。

我们一步一步来看

第一步 ctx 封装

首先把请求相关的所有数据都 封装了到 一个 ctx 对象

而且通过 __init__ 可以看到 封装了 request 以及创建了一个 空的 session 

第一步总结

  得到了 ctx 对象

  创建ctx.request 以及 ctx.session = None 

第二步 push

来看下 push 都做了什么

封了一个top,这是啥不知道,要继续看是个 LocalStack 对象

class LocalStack(object):

    """This class works similar to a :class:`Local` but keeps a stack
of objects instead. This is best explained with an example:: >>> ls = LocalStack()
>>> ls.push(42)
>>> ls.top
42
>>> ls.push(23)
>>> ls.top
23
>>> ls.pop()
23
>>> ls.top
42 They can be force released by using a :class:`LocalManager` or with
the :func:`release_local` function but the correct way is to pop the
item from the stack after using. When the stack is empty it will
no longer be bound to the current context (and as such released). By calling the stack without arguments it returns a proxy that resolves to
the topmost item on the stack. .. versionadded:: 0.6.1
""" 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):
"""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

LocalStack 源码

这个 LocalStack 对象是什么我们也还是不知道,还需需要继续看,发现其实是个 Local 对象

看到Local 到这里发现应该是看到底了,这样稍微看下 local 对象就是在 __storage__ 里面存了个数据结构

这个数据结构是这样的,

__storage__ = {
线程/协程id:{}
线程/协程id:{}
线程/协程id:{}
}
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[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 源码

然后我们在回头看下 LocalStark 是干嘛的, 原来这货是帮助维护这个数据结构的,

会把 Local 的数据进一步维护成这样

__storage__ = {
线程/协程id:{stack:[ctx]} # 维护成一个栈
线程/协程id:{stack:[ctx]}
线程/协程id:{stack:[ctx]}
线程/协程id:{stack:[ctx]}
} # 都是通过 Localstack 来操作 local 里面的数据结构

而且 LocalStark 里面还有一系列的操作方法,由此可见 LocalStark 应该就是专门操作 Local 对象数据的

ok,走到头了回去接着往下走

有封装了一个 app_ctx ,其实刚刚看 top 的时候就已经发现了 下面有个 app_ctx_stack  了

可见其实封装了两个 LocalStark 对象,分别是 请求相关的 以及 app相关的

然后继续往下走,这里分装了 app_ctx 是 AppContext 的 对象,

里面封装了,app 和 g

看到了这里

这里就是对 session 的操作了。之前的 session 一直是 空,这里进行真正的赋值。

可见 session 的赋值是通过配置文件的 配置名字 取到 session ,然后解密反序列化生成字典重新赋值 ctx.session 的具体操作实现

至此,push() 的代码看完了。

第二步总结

分装了 两个 ctx :

  - 请求上下文(ctx=RequestContext()):request/session
  - App上下文(app_ctx=AppContext()): app/g

  这两 ctx 都分别保存了两个 LocalStark 以及 两个 Local  

以及 赋值了 ctx.session

第三步 full_dispatch_request

这里就不详细看了。

大概做了两件事,执行了处理视图函数,以及一系列善后工作,

第四步 弹出请求对象

视图函数执行完毕后,一次请求结束,把请求的 ctx 对象弹出,

上下文管理

在看过了 整体的 Flask 的一次请求的流程之后,

我们再来分析上下文管理 ,直接看到这个比较重点的地方把

两个  LocalStack 对象,以及重要的 request,session 的产生的地方。

这里使用了 LocalProxy用于代理Local对象和LocalStack对象,以及偏函数

流程

程序启动

两个Local:
  local1 = {}   local2 = {} 两个LocalStack:
  _request_ctx_stack
  _app_ctx_stack

请求到来

对数据进行封装:
ctx = RequestContext(request,session)
app_ctx = AppContext(app,g)

保存数据
将包含了(app,g)数据的app_ctx对象,利用 _app_ctx_stack(LocalStack())将app_ctx添加到Local中
storage = {
1231:{stack:[app_ctx(app,g),]}
}

  将包含了request,session数据的ctx对象,利用_request_ctx_stack(LocalStack()),将ctx添加到Local中
storage = {
1231:{stack:[ctx(request,session),]}
}

视图函数处理

@app.route('/index')
def index():
# 去请求上下文中获取值 _request_ctx_stack
request.method
session['xxx'] # 去app上下文中获取值:_app_ctx_stack
print(current_app)
print(g) return "Index"
方式一:直接找LocalStack获取
from flask.globals import _request_ctx_stack
print(_request_ctx_stack.top.request.method) 方式二:通过代理LocalProx获取
from flask import Flask,request
print(request.method)

请求结束

_app_ctx_stack.pop()
_request_ctx_stack.pop()

上下文管理总流程图

面试相关问题

问题一:flask和django的区别:
  对于django来说,内部组件特别多,自身功能强大,有点大而全,而flask,内置组件很少,但是它的第三方组件很多,扩展性强,有点短小精悍,而它们之间也有相似之处,
  因为它们两个框架都没有写sockte,都是基于wsgi协议做的,在此之外,flask框架中的上下文管理较为耀眼。   
  相同点:它们两个框架都没有写sockte,都是基于wsgi协议做的
  请求相关数据传递的方式不同:django:通过传递request参数取值
                flask:见问题二
           组件不同:django组件多
                flask组件少,第三方组件丰富 问题1.1: flask上下文管理:
  简单来说,falsk上下文管理可以分为三个阶段:
        1、请求进来时,将请求相关的数据放入上下问管理中
        2、在视图函数中,要去上下文管理中取值
        3、请求响应,要将上下文管理中的数据清除
  
  详细点来说:
        1、请求刚进来,将request,session封装在RequestContext类中,app,g封装在AppContext类中,并通过LocalStack将requestcontext和appcontext放入Local类中
        2、视图函数中,通过localproxy--->偏函数--->localstack--->local取值
        3、请求相应时,先执行save.session()再各自执行pop(),将local中的数据清除
         问题1.2 flask第三方组件
  flask:
      -flask-session 默认放入cookie,可以放入redis
      -flask-redis
      -flask-migrate
      -flask-script
      -blinker 信号
 公共: DBUtils 数据库连接池
      wtforms 表单验证+生成HTML标签
      sqlalchemy
  自定义:Auth 参考falsk-login 问题二:Flask中的session是什么时候创建,什么时候销毁的?
  当请求进来时,会将requset和session封装为一个RequestContext对象,通过LocalStack将RequestContext放入到Local对象中,因为
请求第一次来session是空值,所以执行open_session,给session(uuid4())赋值,再通过视图函数处理,请求响应时执行save.session,将签名session写入cookie中,再讲Local中的数值pop掉。 问题三:flask中一共有几个LocalStack和Local对象
  两个LocalStack,两个Local
  request、session共同用一个LocalStack和Local
  g、app共同用一个Localstack和Local 问题四: 为什么把请求放到RequestContext中:
   因为request和session都是在视图中操作频繁的数据,也是用户请求需要用的数据,将request和session封装在RequestContext中top,pop一次就可以完成,而单独不封装在一起就会多次操作,     ctx = RequestContext(request,session) 问题五:local作用
    -保存 请求上下文对象和app上下文对象      -localstack的源码与threading.local(线程处理)作用相似,不同之处是Local是通过greenlet(协程)获取唯一标识,粒度更细
      
 问题六:Localstack作用
    2、将local对象中的数据维护成一个栈【ctx,ctx】(先进后出)
{
“协程或线程的唯一标识”: { stack:[ctx,ctx,ctx,] }
}
     为什么维护成一个栈?
   当是web应用时:不管是单线程还是多线程,栈中只有一个数据
   - 服务端单线程:
    {
    111:{stack: [ctx, ]}
    }
  - 服务端多线程:
    {
    111:{stack: [ctx, ]}
    112:{stack: [ctx, ]}
    }
离线脚本:可以在栈中放入多个数据
with app01.app_context():
  print(current_app)
  with app02.app_context():
    print(current_app)
  print(current_app)  问题七:什么是g?
    g 相当于一次请求的全局变量,当请求进来时将g和current_app封装为一个APPContext类,在通过LocalStack将Appcontext放入Local中,取值时通过偏函数,LocalStack、loca l中取值,响应时将local中的g数据删除:
 问题八:怎么获取Session/g/current_app/request
    通过 、偏函数(lookup_req_object)、Localstack、Local取值
 问题九: 技术点:
  - 反射 (LocalProxy())
  - 面向对象,封装:RequestContext
  - 线程(threading.local)
  - 笔试:自己写一个类+列表 实现栈。(LocalStack)
问题十:python基本哪些内容比较重要:
1、反射
  -CBV
  -django配置文件
  -wtforms中的Form()示例化中 将"_fields中的数据封装到From类中"
2、装饰器 (迭代器,生成器)
  -flask:路由、装饰器   -认证
  -csrf
3、面向对象
-继承、封装、多态(简单描述)
 -双下划线:
    __mro__ wtform中 FormMeta中继承类的优先级
   __dict__  
    __new__ ,实例化但是没有给当前对象
     wtforms,字段实例化时返回:不是StringField,而是UnboundField
       rest frawork many=Turn 中的序列化
    __call__
       flask 请求的入口app.run()
     字段生成标签时:字段.__str__ => 字段.__call__ => 插件.__call__
    
__iter__ 循环对象是,自定义__iter__         wtforms中BaseForm中循环所有字段时定义了__iter__
    metaclass
        - 作用:用于指定当前类使用哪个类来创建
        - 场景:在类创建之前定制操作
        示例:wtforms中,对字段进行排序。

Flask 源码流程,上下文管理的更多相关文章

  1. Flask源码流程分析(一)

    Flask源码流程分析: 1.项目启动: 1.实例化Flask对象 1. 重要的加载项: * url_rule_class = Rule * url_map_class = Map * session ...

  2. Flask源码流程剖析

    在此之前需要先知道类和方法,个人总结如下:  1.对象是类创建,创建对象时候类的__init__方法自动执行,对象()执行类的 __call__ 方法 2.类是type创建,创建类时候type的__i ...

  3. Python Web Flask源码解读(一)——启动流程

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  4. flask 源码专题(十一):LocalStack和Local对象实现栈的管理

    目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于localstack的实现 3. 总结 04 LocalS ...

  5. flask 源码专题(二):请求上下文与全文上下文

    源码解析 0. 请求入口 if __name__ == '__main__': app.run() def run(self, host=None, port=None, debug=None, lo ...

  6. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  7. 07 flask源码剖析之用户请求过来流程

    07 Flask源码之:用户请求过来流程 目录 07 Flask源码之:用户请求过来流程 1.创建ctx = RequestContext对象 2. 创建app_ctx = AppContext对象 ...

  8. 用尽洪荒之力学习Flask源码

    WSGIapp.run()werkzeug@app.route('/')ContextLocalLocalStackLocalProxyContext CreateStack pushStack po ...

  9. flask 源码剖析

    flask 上下文管理源码流程及涉及的部分技术点 [flask源码梳理]之一  偏函数_mro [flask源码梳理]之二  面向对象中__setattr__ [flask源码梳理]之三  Local ...

随机推荐

  1. 学习笔记—JDBC

    JDBC的概念 JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言 ...

  2. 可编辑且宽度自适应input

    默认的input项是比较难看的,并且它的宽度还无法随着输入而变化,这样未免有些呆板,不过借助JavaScript可以达到宽度自适应的效果,下面为了方便使用了jQuery: <div class= ...

  3. AFO && OI回忆录

    技不如人,甘拜下风 今天是2019.4.6,联考第一天,菜鸡attack原题爆炸(其实是都不会)心灰意冷(其实并没有很难过)写下了这篇文章 T1 2h写个跟\(k\)无关的假算法写到最后发现是三个lo ...

  4. 我的世界 ParaCraft 结合开源地图 OpenStreetMap 生成3D校园的方法简介

    我的世界ParaCraft结合开源地图OpenStreetMap生成3D校园的方法简介 版本1.0 日期2019.2.3 作者Ray (82735589@qq.com) www.TimeGIS.com ...

  5. SQL优化小技巧

    我们要做到不但会写SQL,还要做到写出性能优良的SQL语句. 1.使用表的别名(Alias): 当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析 ...

  6. 【原】无脑操作:IDEA使用时,提示"8080端口被占用"的解决

    1.问题描述:IDEA使用时,提示"8080端口被占用" Description:    The Tomcat connector configured to listen on ...

  7. 吴军武志红万维刚薛兆丰何帆曾鸣李笑来罗永浩等得到APP专栏作者的书3

    整理了一下最近两三年内看过的得到APP专栏与课程作者的得到精选文集和他们写过的书共本.新增吴军1本,武志红1本. 其中:武志红3本,熊太行1本,薛兆丰2本,吴军4本,何帆3本,曾鸣2本,万维刚1本,李 ...

  8. python学习_2

    1.pycharm部分技巧 1)创建时路径尽量要避免中文2)用滚轮调整编辑器字体大小    1.file->setting...->editor->general 搜索'mouse' ...

  9. 配置安全的管理访问(GNS3)

    实验拓扑: 实验目的: 尝试用R2/R3远程登陆R1路由器并进行管理,在此之前先在R1配置安全的管理访问 1.端口IP配置 R1: e1/0 192.168.1.1/24 e1/1 192.168.2 ...

  10. mysql中几个日期时间类型之间的区别和使用

    MySQL中有如下几个时间类型:date.time.datetime.timestamp.year MySQL数据类型           含义 date                     只存 ...