01. Flask源码分析之【werkzurg】

1.1 引子

我们知道所有的 python web 框架都要遵循 WSGI 协议,而Flask本身并没有,而是依赖werkzeug实现的
那么先抛开Flask不谈,通过一段简单的代码,看看werkzeug的作用

from werkzeug.serving import run_simple


def func(environ, start_response):
    print("请求来了")
    pass


if __name__ == '__main__':
    run_simple('127.0.0.1', 5000, func)
  • 访问127.0.0.1: 5000可以打印出来"请求来了",说明,请求进来后,会执行func(),
  • 同理,如果run_simple()中的func是个app对象,则执行app()就会执行app对象的__call__方法

所以可写为:

from werkzeug.serving import run_simple

class Flask(object):
    def __call__(self, environ, start_response):
        return '来了啊狗子'

app = Flask()


if __name__ == '__main__':
    run_simple('127.0.0.1', 5000, app)

 
 

1.2 flask快速上手

一个最小的 Flask 应用如下:

from flask import Flask, render_template

# 1.创建app
app = Flask(__name__)  
# app是Flask的实例,它接收包或者模块的名字作为参数,但一般都是传递__name__。 
# 有了这个参数, Flask 才能知道在哪里可以找到模板和静态文件等东西。

# 2.路由和视图
@app.route('/index')
def login():
    return 'hello world'

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

应用启动的代码是 app.run(),源码解析如下(简化):

def run(self, host=None, port=None, debug=None, **options):

    # 如果host 和 port 没有指定,则设置 host 和 port 的默认值 127.0.0.1 和 5000
        if not host:
            if sn_host:
                host = sn_host
            else:
                host = "127.0.0.1"

        if port or port == 0:
            port = int(port)
        elif sn_port:
            port = int(sn_port)
        else:
            port = 5000

    # 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数
    # 注意第三个参数传进去的是 self,也就是要执行的 app
    try:
        run_simple(host, port, self, **options)
    finally:
        self._got_first_request = False

概括:

  • flask是依赖werkzurg实现wsgi,flask自己没有wsgi
  • 请求一旦进来,就会执行app.__call__方法

接下来看看Flask类中的两个方法:

class Flask(Scaffold):
    
    def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
        
        ctx = self.request_context(environ)
        error: t.Optional[BaseException] = None
        try:
            try:
                ctx.push()
                # 正确的请求处理路径,会通过路由找到对应的视图函数
                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.auto_pop(error)

    def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:

        return self.wsgi_app(environ, start_response)
      

果然有__call__方法,从它入手!那么上面这段代码简单概括就是:找到视图函数,然后调用它。

除了异常处理之外,我们还看到了 context 相关的内容(开始有 ctx.push(),最后有 ctx.auto_pop()的逻辑),后面再介绍。

posted on 2022-06-19 22:00  吃大飞  阅读(175)  评论(0)    收藏  举报