flask源码分析

Posted xuchengnotes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flask源码分析相关的知识,希望对你有一定的参考价值。

一、flask源码分析

# 创建一个flask项目
from flask import Flask

app = Flask(__name__)

if __name__ == '__main__':
    # app.__call__()
    app.run()

1.查看app.run()中run方法

首先进入app.run()方法,进入之后你会发现最核心的一句话, 导入了werkzeug工具包,启动了一个socket,其中self是app有Flask类创建的对象,host是ip地址,port是端口号

from werkzeug.serving import run_simple
# app.run调用werkzeug.seriving的run-simple
run_simple(host, port, self, **options)

技术图片

2.查看__call__方法

在进入Flask类中的__call__,发现的是里面有一个是wssgi_app方法将执行的结果返回回来,其中参数意思为:

  • self是app由flask创建的实例对象
  • environ:请求相关的参数也就是前端页面向后端发送请求携带过来的所有参数信息
  • start_response: 是请求响应相关的(response)
def __call__(self, environ, start_response):
    """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)

技术图片

3.查看wsgi_app源码

3.1总体代码分析

  • self:flask创建对象
  • environ:是请求相关
  • start_response:
    def wsgi_app(self, environ, start_response):
       # 获取请求相关,存到ctx,得到一个Rquest_context对象,这对象里面包含了requests和session
        ctx = self.request_context(environ) # 见3.2
        error = None
        try:
            try:
                # 把ctx存起来 见3.3
                ctx.push()
                # 执行任务获取response
                # 从ctx中获取请求的东西,是所有响应的东西
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                # 捕获异常
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            # 删除ctx,请求结束后执行ctx.auto_pop(error)把它在LOCAL对象中删除.删除的是ctx
            ctx.auto_pop(error)

3.2ctx代码分析

ctx = self.request_context(environ)分析,environ是请求相关request

1.进入request_content方法中,会返回一个RequestContext(self, environ)对象,self是app是flask创建的对象,environ是请求相关的数据,如下:

 def request_context(self, environ):
   
        return RequestContext(self, environ)

2.进入RequestContext类,在__init__中出现创建了将请求相关的所有数据都赋值给了request,并设置了session

def __init__(self, app, environ, request=None, session=None):
    self.app = app
    if request is None:
    request = app.request_class(environ)
    self.request = request
    self.url_adapter = None
    try:
    self.url_adapter = app.create_url_adapter(self.request)
    except HTTPException as e:
    self.request.routing_exception = e
    self.flashes = None
    self.session = session

3.总结:ctx = self.request_context(environ),ctx是一个RequestContext类对象,返回来这个对象中包含了request,session还有其他的属性

3.3 ctx.push() 把ctx保存起来

# globals.py类中实现的封装,只要项目已启动就会创建
_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"))

1.ctx.push()方法

# self是ctx
def push(self):
    top = _request_ctx_stack.top # return self._local.stack[-1] 不能理解,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)
    pass


# local.py中push,将ctv添加进去
    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 

2.在push中调用了_request_ctx_stack.push(self)方法,self是ctx,使用该方法需要导入from .globals import _request_ctx_stack

说明:_request_ctx_stack这个是一个全局变量是LocalStack()的对象

进入globals.py中会有如下:

# globals.py类中实现的封装,只要项目已启动就会创建
_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"))

最终调用是LocalStack()对象在localStack的初始化方法中生成的是 self._local = Local()Local类对象,

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

Local类对象的初始化方法如下:

def __init__(self):
    object.__setattr__(self, "__storage__", {})
    object.__setattr__(self, "__ident_func__", get_ident)

3.最终调用的是LocalStack()类中的push方法内容如下,

说明:_request_ctx_stack.push(self)self是LocalStack产生的对象,obj是ctv:

"""
1. self是LocalStack()产生的对象
2. self._local: 他是Local()类对象,从Local获取stack对象其实也就是
3. obj是ctv
4. 在push才将ctv进行保存了起来  rv.append(obj)
"""
def push(self, obj):
    rv = getattr(self._local, "stack", None) # 从local中获取stack如果没有获取到则会将ctv进行添加进来
    if rv is None:
        self._local.stack = rv = []
    # 把obj,也就是ctx存到了LOCAL对象中的storage,存的格式为: storage={"执行id(线程id)":{'stack':[ctx,]}}
    rv.append(obj)
    return rv

总结:真正保存的是在_request_ctx_stack.push(self)中完成了ctv的保存

3.4 response = self.full_dispatch_request()分析

response = self.full_dispatch_request(self)获取所有响应的东西,self是app是通过flask创建的对象

# 这个里面所有self都是app
def full_dispatch_request(self):
    # 执行第一次请求需要执行的所有函数
    #  见3.4.1说明
    self.try_trigger_before_first_request_functions()
    try:
        # 信号
        request_started.send(self)
        # rv 就是响应函数吗?
        # 见3.4.2说说明
        #rv是 before_request执行的结果,见3.4.2说说明,如果这个里面有返回值,后面的befor_request都不会执行
        rv = self.preprocess_request() 
        # rv如果为None,才会执行下面self.dispatch_request()才是真正的响应函数
        if rv is None:
            # rv 真正的响应函数,如果上面的before_request没有返回值就执行它,并拿到rv这个返回值,去获取对应路由返回的结果
            # 见3.4.3说明
            rv = self.dispatch_request() # 获取url请求响应结果
     except Exception as e:
            rv = self.handle_user_exception(e)
     # 请求之后的函数response,获取响应对象 见3.4.4说明
     return self.finalize_request(rv)

3.4.1 self.try_trigger_before_first_request_functions()

? self.try_trigger_before_first_request_functions():说明在请求来的时候都会被调用并且只会调用一次, 这个方法自己可以用装饰器@app.before_first_request修饰的函数都会被执行,只会执行一次,具体实现执行查看try_trigger_before_first_request_functions方法

# self是app
def try_trigger_before_first_request_functions(self):
   
    if self._got_first_request:
        return
    with self._before_request_lock:
        if self._got_first_request:
            return
        for func in self.before_first_request_funcs:
            func()
            self._got_first_request = True
@setupmethod
def before_first_request(self, f):
     # 添加被`@app.before_first_request`装饰的第一次请求的函数
     self.before_first_request_funcs.append(f)
     return f

3.4.2 self.preprocess_request()

执行被@befor_request装饰的请求函数是请求相关,其中只有有一个请求响应中函数中包含了return下面的请求函数都将不会被执行,如果有则将rv返回,如果不存在则返回一个None,系统的或者是自己通过装饰器创建请求相关的

    # self是app
    def preprocess_request(self):      
        bp = _request_ctx_stack.top.request.blueprint
        funcs = self.url_value_preprocessors.get(None, ())
        if bp is not None and bp in self.url_value_preprocessors:
            funcs = chain(funcs, self.url_value_preprocessors[bp])
        for func in funcs:
            func(request.endpoint, request.view_args)

        funcs = self.before_request_funcs.get(None, ())
        if bp is not None and bp in self.before_request_funcs:
            funcs = chain(funcs, self.before_request_funcs[bp])
        for func in funcs:
        # before_request被装饰器装饰的请求函数的执行结果,重要            
            rv = func()
            if rv is not None:
                return rv

3.4.3 self.preprocess_request()

full_dispatch_request中的rv = self.dispatch_request()真正的请求响应,这个函数所要做的事情就是通过url请求了链接中对应的请求去获取响应结结果,就是通过路由去获取函数的结果

    # self是app
    def dispatch_request(self):
      
        req = _request_ctx_stack.top.request # top是ctv
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        
        # 请求url路径
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        if (
            getattr(rule, "provide_automatic_options", False)
            and req.method == "OPTIONS"
        ): 
            # 获取真正的请求响用
            return self.make_default_options_response()  # 返回的是一个Response对象,响用结果
        # otherwise dispatch to the handler for that endpoint
        return self.view_functions[rule.endpoint](**req.view_args)

3.4.4 self.finalize_request(rv)请求之后的函数

获取响应结果response

    # self是app
    def finalize_request(self, rv, from_error_handler=False):
       # rv可能是字符串,可以是html,json数据
       # 创建respon返回对象
        response = self.make_response(rv)
        try:
            # 去执行被@app.after_request装饰的app响应结果 见3.4.4.1说明
            response = self.process_response(response)
            request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception(
                "Request finalizing failed with an error while handling an error"
            )
         # 返回响应结果
        return response

3.4.4.1 self.process_response(response)

遍历查询当前app中的被@app.after_request装饰的函数

    # 去执行自己创建的被@app.after_request装饰的函数结果
    def process_response(self, response):
       
        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
        for handler in funcs:
            response = handler(response)
        if not self.session_interface.is_null_session(ctx.session):
            self.session_interface.save_session(self, ctx.session, response)
        return response

4.request对象

@app.route("/")
def index():
    print(request.method)

    return "ok"
from flask import request
# ctrl+鼠标左键进入,会找到如下结果
# partial(_lookup_req_object, "request") 这句话意思是执行_lookup_req_object方法,从 _request_ctx_stack.top 也就是ctv中获取 request属性将其返回
request = LocalProxy(partial(_lookup_req_object, "request")) localproxy代理
# LocalProxy():中的初始化方法,localrequest
 def __init__(self, local, name=None):
        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)

技术图片

object.__setattr__(self, "_LocalProxy__local", local)解释:因为在LocalSack类的初始化方法中出现设置了隐藏属性self._local = Local(),所有可以通过_LocalProxy__local获取local,当我们通过requst.args获取参数的时候会调用该类中的__getattr__通过反射(request)中获取args(.method)具体属性如下:

# name就是args, host,url你要获取属性值的名字,name是request中的.method属性
# 从self._get_current_object()找具体的属性结果 
def __getattr__(self, name):
    if name == "__members__":
        return dir(self._get_current_object())
    
    # self._get_current_object()返回的结果就是偏函数执行的结果,偏函数返回的结果就是request:见下面
    # 从request中获取属性值
    return getattr(self._get_current_object(), name)

# local,LocalProxy(partial(_lookup_req_object, "request")) 传过来的是ctv,local也就是偏函数,
# 从_LocalProxy__local,其实是LocalProxy中的隐藏属性
def __init__(self, local, name=None):
    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)
#sdf     
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__"):
        # self.__local也就是partial(_lookup_req_object, "request")偏导函数执行的结果
        # self.__local()加括号执行也就是偏导函数加括号运行
        return self.__local()
    try:
        return getattr(self.__local, self.__name__)
    except AttributeError:
        raise RuntimeError("no object bound to %s" % self.__name__)

以上是关于flask源码分析的主要内容,如果未能解决你的问题,请参考以下文章

flask 请求处理流程及其源代码分析sessioncookie生成源码

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

1.flask 源码解析:简介

flask源码分析01:框架简介

flask源码分析