• flask多app和栈的应用


    一、简介

        flask的蓝图可以实现url的分发,当有多个app时也可以利用app进行url分发,这里介绍下使用方式和内部原理以及栈的应用。

    二、多app使用

    使用示例

    from werkzeug.wsgi import DispatcherMiddleware
    from werkzeug.serving import run_simple
    from flask import Flask
    
    app01 = Flask('app01')
    app02 = Flask('app02')
    
    @app01.route('/index')
    def index():
        return "app01"
    
    
    @app02.route('/index')
    def index2():
        return "app02"
    
    
    app = DispatcherMiddleware(app01, {
        '/app01': app01,
        '/app02': app02,
    })
    #默认使用app01的路由,也就是访问 http://127.0.0.1:5000/index 返回app01
    #当以app01开头时候使用app01的路由,也就是http://127.0.0.1:5000/app01/index 返回app01
    #当以app02开头时候使用app02的路由,也就是http://127.0.0.1:5000/app02/index 返回app02
    
    if __name__ == "__main__":
        run_simple('127.0.0.1', 5000, app)

    实现原理

    多app使用借助于DispatcherMiddleware,让我们看看其源码类的定义:

    class DispatcherMiddleware(object):
    
        """Allows one to mount middlewares or applications in a WSGI application.
        This is useful if you want to combine multiple WSGI applications::
    
            app = DispatcherMiddleware(app, {
                '/app2':        app2,
                '/app3':        app3
            })
        """
    
        def __init__(self, app, mounts=None):
            self.app = app
            self.mounts = mounts or {}
    
        def __call__(self, environ, start_response):
            script = environ.get('PATH_INFO', '')
            path_info = ''
            while '/' in script:
                if script in self.mounts:
                    app = self.mounts[script]
                    break
                script, last_item = script.rsplit('/', 1)
                path_info = '/%s%s' % (last_item, path_info)
            else:
                app = self.mounts.get(script, self.app)
            original_script_name = environ.get('SCRIPT_NAME', '')
            environ['SCRIPT_NAME'] = original_script_name + script
            environ['PATH_INFO'] = path_info
            return app(environ, start_response)

    从源码中可以看到,该类实例化接受两个参数,一个是app,第二个是mounts,此时我们运行app的时候使用的是run_simple('127.0.0.1', 5000, app),其中app就是DispatcherMiddleware对象,在flask上下文中有提及到run_simple会执行第三个参数的__call__方法,也就是以上DispatcherMiddleware的__call__方法,以我们的示例为列子,self.app=app01,self.mounts={‘/app01’:app01,’/app02’:app02},script是请求路径例如我们请求http://127.0.0.1:5000/index,script就是/index,接着看以下代码: 
    def __call__(self, environ, start_response):
            script = environ.get('PATH_INFO', '')
            path_info = ''
            while '/' in script:
                if script in self.mounts:
                    app = self.mounts[script]
                    break
                script, last_item = script.rsplit('/', 1)
                path_info = '/%s%s' % (last_item, path_info)
            else:
                app = self.mounts.get(script, self.app)
            original_script_name = environ.get('SCRIPT_NAME', '')
            environ['SCRIPT_NAME'] = original_script_name + script
            environ['PATH_INFO'] = path_info
            return app(environ, start_response)

    当script = /index是,while条件成立,同时对判断 /index是否在self.mounts,显然此时不在,分割/index,修改script为空,此时while不成立,执行app = self.mounts.get(script, self.app),返回self.app也就是app01,接着运行app01(),这也就是和在上下文中流程一样了。究其本质,就是通过url匹配出是哪个app,在运行该app的__call__方法。

    三、栈是使用

       flask中的请求数据存放实际利用列表构造的栈来存储的,每次pop都从最后pop出栈。当我们在进行测试或者写flask离线脚本时候可能会使用到上下文嵌套,例如:

    from flask import Flask, current_app, _app_ctx_stack
    
    app1 = Flask('app01')
    app1.debug = False
    
    
    app2 = Flask('app02')
    app2.debug = True
    
    
    
    with app1.app_context():
    
        print(_app_ctx_stack._local.__storage__)
        #{<greenlet.greenlet object at 0x10dc79e88>: {'stack': [<flask.ctx.AppContext object at 0x10dda7b00>]}}
        print(current_app.config['DEBUG']) # False
    
        
        with app2.app_context():
            print(_app_ctx_stack._local.__storage__) 
            #{<greenlet.greenlet object at 0x10dc79e88>: {'stack': [<flask.ctx.AppContext object at 0x10dda7b00>, <flask.ctx.AppContext object at 0x10dda7c18>]}}
            print(current_app.config['DEBUG']) # True
    
        print(current_app.config['DEBUG'])  # False

    在以上示例中在app01的上下文中嵌套了app02的上下文,所以在栈中会有两个app_ctx,但是在各自取上下文的时候都不会冲突,因为app02的上下文在最后,也就是第二个with中top是app02的app_ctx。

    四、关于with 

      with语法在python中非常多见,比如文件操作中打开文件,实时上with常常用于做一些先操作后清理的工作,比如文件操作最后需要关闭文件,数据库操作先进行拿数据库连接进行查询,最后关闭连接等等。
    如何工作:
      被with作用的对象必须有一个__enter__()方法和一个__exit__()方法,紧跟with后面的语句被调用,并返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。
    以下是一个with语句的示例:
    class Foo(object):
        def __init__(self,name):
            self.name=name
        def __enter__(self):
            print('run __enter__')
            return self.name
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('run __exit__')
    
    
    with Foo('wd') as myname:
        print("vars:",myname)
    
    结果:
    run __enter__
    vars: wd
    run __exit__

    可能你会注意在到,在以上的demo中__exit__方法中多了三个参数,即exc_type、exc_val、exc_tb这是对应着在with语句中代码出现异常时也会执行__exit__并接受异常,分别对应着:异常类型、异常值、以及异常的traceback。示例:

    class Foo(object):
        def __init__(self,name):
            self.name=name
        def __enter__(self):
            print('run __enter__')
            return self.name
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('run __exit__')
            print('exc_type:',exc_type)
            print('exc_val:',exc_val)
            print('exc_tb:',exc_tb)
    
    
    with Foo('wd') as myname:
        print("vars:", myname)
        a=[]
        v=a[1]
        print(v)
    
    结果:
    run __enter__
    vars: wd
    run __exit__
    exc_type: <class 'IndexError'>
    exc_val: list index out of range
    exc_tb: <traceback object at 0x10ec98508>
    Traceback (most recent call last):
      File "dbapi.py", line 21, in <module>
        v=a[1]
    IndexError: list index out of range

    最后我们回过头来看看app_context()对象中的__enter__方法和__exit__方法:

    def __enter__(self):
        self.push()
        return self
    
    def __exit__(self, exc_type, exc_value, tb):
        self.pop(exc_value)
    
        if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
            reraise(exc_type, exc_value, tb)

    实际上非常简单,执行__enter__时候调用push压栈,执行__exit__时pop出栈,这样就使得每个with里面都是当前app的上下文,而不会冲突。

      

      

  • 相关阅读:
    Mina、Netty、Twisted一起学(八):HTTP服务器
    Mina、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)
    梦想还是要有的,万一实现了呢(校招季)
    我的地盘听我的
    React源码剖析系列 - 生命周期的管理艺术
    实现搜索联想
    为你的简历加分
    twobin博客样式—“蓝白之风”
    一起学习jQuery2.0.3源码—1.开篇
    this的安身之处
  • 原文地址:https://www.cnblogs.com/wdliu/p/10148161.html
Copyright © 2020-2023  润新知