flask源码分析

Flask上下文管理流程图

image-20200902215851932

路由源码分析

# 第一步
@app.route('/')

# 第二步
def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop('endpoint', None)
        # 1. 路由声明@app.route('/')本质内部就是调用了add_url_rule, 因此路由写法也可以写成: 
        # app.add_url_rule(rule, endpoint, 路由对应的视图函数名, **以关键字形式传参的请求方式methods=[''] 或者 用于反向解析的别名endpoint='别名')
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator


# 第三步:
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    if endpoint is None:
        # 2. _endpoint_from_view_func实现了endpoint没有传递时默认以函数名称作为方向解析的别名
        '''
        def _endpoint_from_view_func(view_func):
            assert view_func is not None, 'expected view func if endpoint ' \
                                          'is not provided.'
            return view_func.__name__   
        '''
        endpoint = _endpoint_from_view_func(view_func)
    options['endpoint'] = endpoint
    methods = options.pop('methods', None)

    if methods is None:
        # 3. 这里实现了如果路由没有指定methods请求方式, 默认就是get请求
        methods = getattr(view_func, 'methods', None) or ('GET',)
    if isinstance(methods, string_types):
        raise TypeError('Allowed methods have to be iterables of strings, '
                        'for example: @app.route(..., methods=["POST"])')
    # 4. 这里实现了控制请求方式无论指定大写还是小写都可以如: methods=["POST", 'get']
    methods = set(item.upper() for item in methods)
    ...

app.run()源码分析

# 流程: app.run() -> run_simple -> app(request) -> app.__call__()

# 第一步: 从run_simple('localhost', 4000, hello) 推理成 app(request)
from werkzeug.wrappers import Request, Response

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, hello)
    
# 第二步: app.__call__()
def __call__(self, environ, start_response):
    """
    environ: {'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', ...}
    start_response: <function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x00000171291CB378>
    """
    return self.wsgi_app(environ, start_response)   
    
# 第三步: self.wsgi_app (注意: Flask源码核心)
def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)
    ctx.push()
    error = None
    try:
        try:
            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
        ctx.auto_pop(error)   

CBV源码分析

1. View源码

# as_view('index') -> View -> as_view -> cls.decorators -> view -> self.dispatch_request

class View(object):
    # 如果书写CBV那么必须指定请求方式: methods=['POST']
    methods = None

    decorators = ()

    def dispatch_request(self):
        # 如果继承View那么必须重写dispatch_request方法, 如果不重写, 就会抛出异常
        raise NotImplementedError()

    @classmethod
    def as_view(cls, name, *class_args, **class_kwargs):
        def view(*args, **kwargs):
            self = view.view_class(*class_args, **class_kwargs)
            return self.dispatch_request(*args, **kwargs)

        if cls.decorators:
            # name是指定函数别名: 本质就是防止反向解析的别名都是同一个
            '''
            def _endpoint_from_view_func(view_func):
                assert view_func is not None, 'expected view func if endpoint ' \
                                  'is not provided.'
                return view_func.__name__
            '''
            view.__name__ = name
            view.__module__ = cls.__module__
            for decorator in cls.decorators:
                # 这里就是循环去执行配置的装饰器列表
                view = decorator(view)
        
        # cls就是你自定义的类
        view.view_class = cls
        view.__name__ = name
        view.__doc__ = cls.__doc__
        view.__module__ = cls.__module__
        view.methods = cls.methods
        return view

2. MethodView源码

# 流程: as_view('index') -> View -> as_view -> cls.decorators -> view -> dispatch_request

def view(*args, **kwargs):
    # self就是自定以类实例化出来的对象. view_class就是自定义的视图类
    self = view.view_class(*class_args, **class_kwargs)
    return self.dispatch_request(*args, **kwargs)

class MethodView(with_metaclass(MethodViewType, View)):
    def dispatch_request(self, *args, **kwargs):
        # 将你自定义类中的methods=['POST', 'get']字符串请求方式变成小写获取对应的请求方法
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method
        # 再执行对应的方法
        return meth(*args, **kwargs)

session源码分析

# 分析: app.session_interface
session_interface=SecureCookieSessionInterface()
    get_signing_serializer方法: 请求来是将加密的session字符串解密成字典(s.loads). 请求走时将字典加密成字符串(s.dumps)
    open_session方法: 请求来时的session的解密操作
    save_session方法: 请求走时的session的加密操作

# 第一步: app.session_interface

# 第二步: session_interface=SecureCookieSessionInterface()

# 第三步: SecureCookieSessionInterface
class SecureCookieSessionInterface(SessionInterface):
    salt = 'cookie-session'
    digest_method = staticmethod(hashlib.sha1)
    key_derivation = 'hmac'
    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app):
        # 如果使用session不指定app.secret_key那么就无法使用session
        if not app.secret_key:
            return None
        signer_kwargs = dict(
            key_derivation=self.key_derivation,
            digest_method=self.digest_method
        )
        # 解密session与加密session的类: URLSafeTimedSerializer
        return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
                                      serializer=self.serializer,
                                      signer_kwargs=signer_kwargs)

    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        # 请求来的时候获取session字符串(提示: 也就是获取cookie)
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            # 请求来的时候如果没有获取到cookie那么, 就会生成一个空的session字典{}, 目的是防止session.get('key')取值时抛出异常
            return self.session_class()
        # app.permanent_session_lifetime获取的就是app.config配置文件中的session过期时间. 默认就是31天
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            # 请求来时将加密的session字符串解密成data字典格式的数据
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        if not session:
            # 如果session['key']=value操作就会自动触发session类的__setitem__方法, 就会将之前字典中对应的key的value清空, 换成新的
            if session.modified:
                # app.session_cookie_name获取在cookies中存放session加密字符串的名字
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            return

        # app.config['SESSION_REFRESH_EACH_REQUEST']控制会话如何刷新
        if not self.should_set_cookie(app, session):
            return

        # 控制 cookie 是否应被设置 httponly 的标. 默认: True
        httponly = self.get_cookie_httponly(app)
        # 控制 cookie 是否应被设置安全标志. 默认: False
        secure = self.get_cookie_secure(app)
        # 控制Session的生命周期(天). 默认31天
        expires = self.get_expiration_time(app, session)
        # 将session字典格式的数据进行加密, 再设置到cookie中
        val = self.get_signing_serializer(app).dumps(dict(session))
        response.set_cookie(app.session_cookie_name, val,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

# 什么时候执行了open_session
# 流程: 
'''
ctx = self.request_context(environ)
request_context
RequestContext
push
self.session = self.app.open_session(self.request) 
'''

# 什么时候执行了save_session
'''
response = self.full_dispatch_request()
full_dispatch_request
return self.finalize_request(rv)
finalize_request
response = self.process_response(response)
process_response
self.save_session(ctx.session, response)
'''

请求扩展源码分析

before_first_request源码

# 第一步: self.before_first_request_funcs.append(f)
@setupmethod
def before_first_request(self, f):
    # 把f这个被装饰的路由函数放到before_first_request_funcs列表中
    self.before_first_request_funcs.append(f)
    return f


# 第二步: self._got_first_request = False
def run(self, host=None, port=None, debug=None, **options):
    from werkzeug.serving import run_simple
    if host is None:
        host = '127.0.0.1'
    if port is None:
        server_name = self.config['SERVER_NAME']
        if server_name and ':' in server_name:
            port = int(server_name.rsplit(':', 1)[1])
        else:
            port = 5000
    if debug is not None:
        self.debug = bool(debug)
    options.setdefault('use_reloader', self.debug)
    options.setdefault('use_debugger', self.debug)
    try:
        run_simple(host, port, self, **options)
    finally:
        # 项目已启动就为self这个flask对象赋值一个_got_first_request属性, 初始化为False
        self._got_first_request = False


# 第三步:
app(request)

# 第四步:
app.__call__()


# 第五步:
def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)


# 第六步: full_dispatch_request
def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)
    ctx.push()
    error = None
    try:
        try:
            # 执行flask对应的完整的请求分发方法full_dispatch_request
            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
        ctx.auto_pop(error)


# 第七步: try_trigger_before_first_request_functions(翻译: 第一个请求函数之前尝试触发)
def full_dispatch_request(self):
    # 初始化在第一个请求函数之前尝试触发的这个方法: try_trigger_before_first_request_functions
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)


# 第八步:
def try_trigger_before_first_request_functions(self):
    # 项目第一次启动_got_first_request就默认为False
    if self._got_first_request:
        return
    with self._before_request_lock:
        if self._got_first_request:
            return
        # for循环self.before_first_request_funcs中那些被before_first_request装饰器装的路由函数加括号一个一个执行
        for func in self.before_first_request_funcs:
            func()
        # 执行完毕以后将_got_first_request执行为True. 从第二次请求开始self._got_first_request就是True, 以后被before_first_request装饰器装饰的路由函数再也不执行了
        self._got_first_request = True

threading.local

多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储

1. 不用threading.local

# 不用local
from threading import Thread,current_thread
import time

yang = -1


def task(arg):
    global yang
    yang = arg
    time.sleep(2)
    print(f'{current_thread()}:{yang}')


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

# 10个线程执行 task 的打印结果
'''
<Thread(Thread-6, started 14272)>:9
<Thread(Thread-3, started 1656)>:9
<Thread(Thread-5, started 7636)>:9
<Thread(Thread-2, started 10408)>:9
<Thread(Thread-10, started 6728)>:9
<Thread(Thread-9, started 11316)>:9
<Thread(Thread-8, started 11088)>:9
<Thread(Thread-7, started 14228)>:9
<Thread(Thread-4, started 10540)>:9
<Thread(Thread-1, started 1540)>:9
''' 

2. threading.local使用

# 使用local
from threading import Thread, current_thread
from threading import local
import time
from threading import get_ident

# 特殊的对象
yang = local()


# yang中的格式类似于: {'线程id':{value:1},'线程id':{value:2}....}
def task(arg):
    yang.value = arg
    time.sleep(2)
    print(f'{current_thread()}: {yang.value}')


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()


# 10个线程执行 task 的打印结果
'''
<Thread(Thread-10, started 10440)>: 9
<Thread(Thread-8, started 8772)>: 7
<Thread(Thread-7, started 13432)>: 6
<Thread(Thread-5, started 14772)>: 4
<Thread(Thread-4, started 1388)>: 3
<Thread(Thread-3, started 9492)>: 2
<Thread(Thread-1, started 13488)>: 0
<Thread(Thread-9, started 3744)>: 8
<Thread(Thread-2, started 9108)>: 1
<Thread(Thread-6, started 5492)>: 5
'''

3. 通过字典自定义threading.local(函数)

# 自己写一个类似local的东西
from threading import get_ident, Thread
import time

storage = {}


# {'线程id':{value:1},'线程id':{value:2}....}
def set(k, v):
    ident = get_ident()
    if ident in storage:
        storage[ident][k] = v
    else:
        storage[ident] = {k: v}


def get(k):
    ident = get_ident()
    return storage[ident][k]


def task(arg):
    set('val', arg)
    v = get('val')
    print(f'{get_ident()}:{v}')


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

# 10个线程执行 task 的打印结果
'''
4360:0
3816:1
10736:2
1580:3
4772:4
4024:5
9476:6
11036:7
12316:8
7372:9
'''

print(storage)
'''
{
11080: {'val': 0}, 2000: {'val': 1}, 13944: {'val': 2}, 
12552: {'val': 3}, 13736: {'val': 4}, 12476: {'val': 5}, 
9392: {'val': 6}, 14900: {'val': 7}, 12660: {'val': 8}, 8648: {'val': 9}
}
'''

4. 面向对象版

# 面向对象版本
from threading import get_ident, Thread
import time


class Local(object):
    storage = {}

    def set(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def get(self, k):
        ident = get_ident()
        return Local.storage[ident][k]


obj = Local()


def task(arg):
    obj.set('val', arg)
    time.sleep(1)
    v = obj.get('val')

    print(f'{get_ident()}:{v}')


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

# 10个线程执行 task 的打印结果
'''
1844:0
7868:1
6040:9
11020:7
6780:8
8988:6
13136:5
14468:4
10892:3
12196:2
'''

print(obj.storage)
'''
{
1844: {'val': 0}, 7868: {'val': 1}, 12196: {'val': 2}, 
10892: {'val': 3}, 14468: {'val': 4}, 13136: {'val': 5}, 
8988: {'val': 6}, 11020: {'val': 7}, 6780: {'val': 8}, 6040: {'val': 9}
}
'''

5. 通过setattr和getattr实现

# 自己写的像local的类
from threading import get_ident, Thread
import time


class Local(object):
    storage = {}

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return Local.storage[ident][k]


obj = Local()


def task(arg):
    obj.val = arg
    time.sleep(1)
    print(f'{get_ident()}:{obj.val}')


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

# 10个线程执行 task 的打印结果
'''
236:0
10448:6
8852:4
10728:2
4040:1
3780:5
10800:3
7560:9
10580:8
7664:7
'''

print(obj.storage)
'''
{
236: {'val': 0}, 4040: {'val': 1}, 10728: {'val': 2}, 
10800: {'val': 3}, 8852: {'val': 4}, 3780: {'val': 5}, 
10448: {'val': 6}, 7664: {'val': 7}, 10580: {'val': 8}, 7560: {'val': 9}
}
'''

6. 每个对象有自己的存储空间(字典)

# 每次实例化得到一个local对象,用自己的字典存储
from threading import get_ident, Thread
import time


class Local(object):
    def __init__(self):
        object.__setattr__(self, 'storage', {})
        #  self.storage={}  # 这种方式不能用, 否则会出现递归调用问题

    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]


obj = Local()
obj1 = Local()


def task(arg):
    obj.val = arg
    time.sleep(1)
    print(obj.val)


def task1(arg):
    obj1.val = arg
    time.sleep(1)
    print(obj1.val)


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

for i in range(10):
    t = Thread(target=task1, args=(i * 100,))
    t.start()


print(obj.storage)
'''
{
13440: {'val': 0}, 13092: {'val': 1}, 10404: {'val': 2}, 
5528: {'val': 3}, 14272: {'val': 4}, 14128: {'val': 5}, 
2312: {'val': 6}, 13736: {'val': 7}, 12620: {'val': 8}, 9524: {'val': 9}
}
'''

print(obj1.storage)
'''
{
12208: {'val': 0}, 10808: {'val': 100}, 13180: {'val': 200}, 
5292: {'val': 300}, 792: {'val': 400}, 1900: {'val': 500}, 
12212: {'val': 600}, 12680: {'val': 700}, 624: {'val': 800}, 1596: {'val': 900}
}
'''

7. 兼容线程和协程

关于greenlet与gevent: https://www.cnblogs.com/linhaifeng/articles/7429894.html#_label3

try:
    # from greenlet import getcurrent as get_ident
    from gevent import getcurrent as get_ident
except Exception as e:
    from threading import get_ident

from gevent import monkey;monkey.patch_all()
import gevent

from threading import Thread
import time


from functools import partial

class Local(object):
    def __init__(self):
        object.__setattr__(self, 'storage', {})

    def __setattr__(self, k, v):
        ident = get_ident()

        # 方式一:
        # if ident in self.storage:
        #     self.storage[ident][k] = v
        # else:
        #     self.storage[ident] = {k: v}

        # 方式二:
        try:
            self.storage[ident][k] = v
        except KeyError:
            self.storage[ident] = {k: v}

    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]


obj = Local()
obj1 = Local()


def task(arg):
    obj.val = arg
    # obj.xxx = arg
    print(obj.val)


def task1(arg):
    obj1.val = arg
    # obj.xxx = arg
    print(obj1.val)


for i in range(10):
    t = Thread(target=task, args=(i,))
    t.start()

g_li = []
for i in range(10):
    g = gevent.spawn(partial(task1, i * 100))
    g_li.append(g)

gevent.joinall(g_li)


print(obj.storage)
'''
{
<Greenlet at 0x2473b552148: _run>: {'val': 0}, <Greenlet at 0x2473b552248: _run>: {'val': 1}, 
<Greenlet at 0x2473b552448: _run>: {'val': 2}, <Greenlet at 0x2473b552648: _run>: {'val': 3}, 
<Greenlet at 0x2473b552748: _run>: {'val': 4}, <Greenlet at 0x2473b552848: _run>: {'val': 5}, 
<Greenlet at 0x2473b552948: _run>: {'val': 6}, <Greenlet at 0x2473b552a48: _run>: {'val': 7}, 
<Greenlet at 0x2473b552b48: _run>: {'val': 8}, <Greenlet at 0x2473b552c48: _run>: {'val': 9}
}
'''


print(obj1.storage)
'''
{
<Greenlet at 0x2473b552d48: _run>: {'val': 0}, <Greenlet at 0x2473b552e48: _run>: {'val': 100}, 
<Greenlet at 0x2473be62048: _run>: {'val': 200}, <Greenlet at 0x2473be62148: _run>: {'val': 300},
<Greenlet at 0x2473be62248: _run>: {'val': 400}, <Greenlet at 0x2473be62348: _run>: {'val': 500}, 
<Greenlet at 0x2473be62448: _run>: {'val': 600}, <Greenlet at 0x2473be62548: _run>: {'val': 700}, 
<Greenlet at 0x2473be62648: _run>: {'val': 800}, <Greenlet at 0x2473be62748: _run>: {'val': 900}
}
'''
posted @ 2020-09-08 16:23  给你加马桶唱疏通  阅读(128)  评论(0编辑  收藏  举报