Flask source process analysis (a)

Flask source process analysis:

1. Project start:

1. Flask objects instantiated

1. 重要的加载项:
    * url_rule_class = Rule
    * url_map_class = Map
    * session_interface = SecureCookieSessionInterface()
    * def __init__:
        * self.static_url_path = static_url_path
        * self.config = self.make_config(instance_relative_config)
        * self.view_functions = {}
        * self.before_request_funcs = {}
        * self.before_first_request_funcs = []
        * self.after_request_funcs = {}
1.1 url_rule_classrole:
1. 作用是封装"endporint"和"url"

2.当加载路由的时候:@app.route('/index')

3. 执行这个方法:self.add_url_rule(rule, endpoint, f, **options);这里"urle"就是上边route中的"/index",是一个url路径;看源码就能发现这是一个装饰器;

4. 里边执行了"rule = self.url_rule_class(rule, methods=methods, **options)"方法;这个方法就是实力化"Rule"这个类;"rule"是那个url路径,"methods"里边是请求方法;"**options"里边是一个字典,键是"endporint",值是别名;然后在这个类中进行封装操作;
1.2 url_map_classrole:
1.在Map类中加载的时候又这么一个类属性: self._rules = []

2.当加载路由的时候:@app.route('/index')

3. 执行这个方法:self.add_url_rule(rule, endpoint, f, **options);这里"urle"就是上边route中的"/index",是一个url路径;看源码就能发现这是一个装饰器;

4. 这个方法中执行了:self.url_map.add(rule)方法;

5.在"add"这个方法中执行的"self._rules.append(rule)",就是将传入的那个"rule"实例化的对象增加到"self._rules = []"列表中;
1.3 static_url_pathrole:
1. 这个是静态文件的路径;
1.4 configrole:
1. 相当于一个空的字典
1.5view_functions` role:
1. 在"add_url_rule()"方法中,操作了个空字典:
    self.view_functions[endpoint] = view_func
    这个字典的键是视图的别名,对应的值是视图函数
1.6 before_request_funcsrole:
1. 首先说一下"@app.before_request",这个是处理路由规则对应的 view_function 之前执行的函数;  
2. 初始化是一个空的字典,这是一个存放对应函数的空字典;
    def before_request(self, f):
        self.before_request_funcs.setdefault(None, []).append(f)
        return f
    可以看出这个里边的键是一个"None",值是一个列表,列表里边是"@app.before_request"下的函数的函数名;
1.7 before_first_request_funcsrole:
1. 这个列表中保存的是函数是在用户请求进来第一个执行,但是只执行一次;
1.8 after_request_funcsrole:
1. 在请求最后需要发送给浏览器的时候才执行这个下边的函数;

2. Load Profile

2.1 app.config.from_object("xxx.xx.x")role:
1. 首先需要传一个文件的相对路径;下边是它的逻辑代码;    
    def from_object(self, obj):
        if isinstance(obj, string_types):
            obj = import_string(obj)
        for key in dir(obj):
            if key.isupper():
                self[key] = getattr(obj, key)
                
2.  obj = import_string(obj),其实就是对括号内的字符串进行拆分,把.之前的路径取出来,引入这个文件目录,再把文件名引入进来去调用里面的各个配置(类似于importlib模块)

再回到from_object函数中,最后self[key] = getattr(obj, key),将所有配置以字典形式存储,键为大写字母.值为键在配置文件中所对应的内容.

这样就读取到了配置文件的内容到内存中,放到了app.config的字典中了.                

3. Load @app.route('/index'):

  • effect:

    1. 这个装饰器里边是将当这个视图函数中的信息进行一个封装;
      self.add_url_rule(rule, endpoint, f, **options)
    
    2. 这个方法中有将"route('/index')"中的信息封装成一个对象,并且将这个对象添加到"Map"类中的"self._rules = []"列表中:
    
    3. 将函数和endpoint放到"view_functions"字典中,键是endpoint,值是函数名;
    
  • Code execution:

    # 下边的是app.py中Flask类中的:
    def add_url_rule( 
            self,
            rule,
            endpoint=None,
            view_func=None,
            provide_automatic_options=None,
            **options):
        rule = self.url_rule_class(rule, methods=methods, **options)
        # 将rule添加到Map类的列表中
        self.url_map.add(rule)
        # 将endpoint和函数添加到字典中
        self.view_functions[endpoint] = view_func
    
    self.url_map = self.url_map_class()
    url_map_class = Map
    
    # 下边的是routing.py文件中的Map类:
    def add(self, rulefactory):
       for rule in rulefactory.get_rules(self):
          rule.bind(self)
          self._rules.append(rule)
          self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
       self._remap = True
    
    
    class Map(object):
         def __init__():
          self._rules = []

4. Start program

  • carried outapp.run()

    1. 这个方法中都是一些判断ip和端口等信息的逻辑代码;
    
    2. 最后执行了"run_simple(host, port, self, **options)"这个方法启动程序,等待请求到来,这个中的"self"是当前实例化的app对象;
    

2. The request comes after

1. execution __call__()method

1. 这个方法是在请求进来之后触发的,并且传入两个参数,触发这个方法的是werkzeug.serving:WSGIRequestHandler的 run_wsgi 中的一段代码:
    def execute(app):
    application_iter = app(environ, start_response)
    try:
        for data in application_iter:
            write(data)
        if not headers_sent:
            write(b'')
    finally:
        if hasattr(application_iter, 'close'):
        application_iter.close()
        application_iter = None
    
    
def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)

    # 可以看到 application_iter = app(environ, start_response) 就是调用代码获取结果的地方,触发"__call__()"方法;

2. 两个参数都是什么:
    * "environ":里边是用户请求相关的信息;
    * "start_response" 是在应用执行完成后发送给浏览器的信息;

2. Perform wsgi_app(self, environ, start_response)internal method of analysis:

2.1 Creating RequestContext(self, environ)objects:
1. 这个"RequestContext"类中将"request"和"session"封装到这个对象中,但是现在的"session"是空的

Code:

def __init__(self, app, environ, request=None, session=None):
    self.app = app
    if request is None:
        # 获取request;
        request = app.request_class(environ)
    self.request = request
    self.session = session        
2.2 execution ctx.push()process (to be saved to stack app_ctx)

First create app_context()instances of objects

def push(self):
    app_ctx = self.app.app_context()
    app_ctx.push()

Examples of the AppContext()class:

def __init__(self, app):
    self.app = app
    # g是一个对象,先放着,后边有用;
    self.g = app.app_ctx_globals_class()

Then execute this objectapp_ctx.push()

def push(self):
    _app_ctx_stack.push(self)
    appcontext_pushed.send(self.app)

This method is performed in the _app_ctx_stack.push(self)method, this _app_ctx_stackis to LocalStack()instantiate an object;

_app_ctx_stack = LocalStack()

It is equivalent to the top of the execution LocalStack()of the push()method;

def push(self, obj):
    rv = getattr(self._local, "stack", None)
    if rv is None:
        self._local.stack = rv = []
    rv.append(obj)
    return rv

This code triggered self._localproperty, this property is Localto instantiate an object class;

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

And performing self._local.stacktime triggered Localclass __setattr__methods;

def __setattr__(self, name, value):
    ident = self.__ident_func__()
    storage = self.__storage__
    try:
        storage[ident][name] = value
    except KeyError:
        storage[ident] = {name: value}

This __storage__is a dictionary, the dictionary here to add a key-value pair, the key is to get the current thread unique identification ident, value is a dictionary that the key is fixed stack, the value is a list, then this will app_ctxadd objects to this list;

Also the top of a request to initiate a context;

Execution ctx.push()process (ctx Save to save the stack)

Here's the top way to preserve and app_ctxsave the same;

def push(self):
    # 这个self就是ctx
    _request_ctx_stack.push(self)

This approach is currently the ctxadd objects to the request context named __storage__thread id behind in the dictionary corresponding to stackthe list of post;

2.3 then perform self.full_dispatch_request()the method;

This approach is the implementation of the relevant inside view function,

code show as below:

def full_dispatch_request(self):
    # 触发所有的before_first_request函数;
    # 只有启动程序后,第一个请求到来时执行;
    self.try_trigger_before_first_request_functions()
    try:
        # 信号
        request_started.send(self)
        # 视图之前,执行所有的before_request_funcs
        rv = self.preprocess_request()
        if rv is None:
            
            # 执行视图函数
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
        
    # 视图函数执行完之后:
        1. 执行所有的after_request_funcs
        2. 保存session
    return self.finalize_request(rv)
2.4 execute ctx.auto_pop(error)code does:

After this is finished as a last view of the destruction ctxand app_ctx;

code show as below:

def auto_pop(self, exc):
    self.pop(exc)
    
def pop(self, exc=_sentinel):
    
    app_ctx = self._implicit_app_ctx_stack.pop()
    
    rv = _request_ctx_stack.pop()

_request_ctx_stackAre LocalStack()examples of object; performing pop()the method is to perform the method in this class; this information is to be saved in the stack which deleted;

Guess you like

Origin www.cnblogs.com/zhufanyu/p/11936955.html