Flask--核心实现

flask运行源码

  • flask源码app.py实现
...
    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
        """Runs the application on a local development server.

        Do not use ``run()`` in a production setting. It is not intended to
        meet security and performance requirements for a production server.
        Instead, see :ref:`deployment` for WSGI server recommendations.

        If the :attr:`debug` flag is set the server will automatically reload
        for code changes and show a debugger in case an exception happened.

        If you want to run the application in debug mode, but disable the
        code execution on the interactive debugger, you can pass
        ``use_evalex=False`` as parameter.  This will keep the debugger's
        traceback screen active, but disable code execution.

        It is not recommended to use this function for development with
        automatic reloading as this is badly supported.  Instead you should
        be using the :command:`flask` command line script's ``run`` support.

        .. admonition:: Keep in Mind

           Flask will suppress any server error with a generic error page
           unless it is in debug mode.  As such to enable just the
           interactive debugger without the code reloading, you have to
           invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
           Setting ``use_debugger`` to ``True`` without being in debug mode
           won't catch any exceptions because there won't be any to
           catch.

        :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
            have the server available externally as well. Defaults to
            ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable
            if present.
        :param port: the port of the webserver. Defaults to ``5000`` or the
            port defined in the ``SERVER_NAME`` config variable if present.
        :param debug: if given, enable or disable debug mode. See
            :attr:`debug`.
        :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
            files to set environment variables. Will also change the working
            directory to the directory containing the first file found.
        :param options: the options to be forwarded to the underlying Werkzeug
            server. See :func:`werkzeug.serving.run_simple` for more
            information.

        .. versionchanged:: 1.0
            If installed, python-dotenv will be used to load environment
            variables from :file:`.env` and :file:`.flaskenv` files.

            If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`
            environment variables will override :attr:`env` and
            :attr:`debug`.

            Threaded mode is enabled by default.

        .. versionchanged:: 0.10
            The default port is now picked from the ``SERVER_NAME``
            variable.
        """
        # Change this into a no-op if the server is invoked from the
        # command line. Have a look at cli.py for more information.
        if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
            from .debughelpers import explain_ignored_app_run

            explain_ignored_app_run()
            return

        if get_load_dotenv(load_dotenv):
            cli.load_dotenv()

            # if set, let env vars override previous values
            if "FLASK_ENV" in os.environ:
                self.env = get_env()
                self.debug = get_debug_flag()
            elif "FLASK_DEBUG" in os.environ:
                self.debug = get_debug_flag()

        # debug passed to method overrides all other sources
        if debug is not None:
            self.debug = bool(debug)

        _host = "127.0.0.1"
        _port = 5000
        server_name = self.config.get("SERVER_NAME")
        sn_host, sn_port = None, None

        if server_name:
            sn_host, _, sn_port = server_name.partition(":")

        host = host or sn_host or _host
        # pick the first value that's not None (0 is allowed)
        port = int(next((p for p in (port, sn_port) if p is not None), _port))

        options.setdefault("use_reloader", self.debug)
        options.setdefault("use_debugger", self.debug)
        options.setdefault("threaded", True)

        cli.show_server_banner(self.env, self.debug, self.name, False)

        from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)
        finally:
            # reset the first request information if the development server
            # reset normally.  This makes it possible to restart the server
            # without reloader and that stuff from an interactive shell.
            self._got_first_request = False
...
    def wsgi_app(self, environ, start_response):
        """The actual WSGI application. This is not implemented in
        :meth:`__call__` so that middlewares can be applied without
        losing a reference to the app object. Instead of doing this::

            app = MyMiddleware(app)

        It's a better idea to do this instead::

            app.wsgi_app = MyMiddleware(app.wsgi_app)

        Then you still have the original application object around and
        can continue to call methods on it.

        .. versionchanged:: 0.7
            Teardown events for the request and app contexts are called
            even if an unhandled error occurs. Other events may not be
            called depending on when an error occurs during dispatch.
            See :ref:`callbacks-and-errors`.

        :param environ: A WSGI environment.
        :param start_response: A callable accepting a status code,
            a list of headers, and an optional exception context to
            start the response.
        """
        ctx = self.request_context(environ)
        error = 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, 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)
...
  • 阅读源码,可以看出代码运行顺序是:
  • 启动
  1. app.run()—>
  2. run_simple(host, port, self...)—>
  3. make_server(hostname,port,application,threaded,processes...)—>
  4. ThreadedWSGIServer(BaseWSGIServer(host, port, app...))—>
    5.WSGIRequestHandler(BaseHTTPRequestHandler, object)—>
  • 请求处理
  1. run_wsgi --> app(environ, start_response),即调用Flask()的__call__方法式
  2. __call__
  3. 调用wsgi_app函数
    3.1 RequestContext = Request(environ)
    3.2 app.request_class(environ)和ctx.push

    3.3 auto_pop

flask请求处理

四大上下文变量

  • 四大变量脱离请求后无法访问
from flask import Flask,current_app,request,g,session


app = Flask(__name__)


print(current_app)    # RuntimeError: Working outside of application context.
print(g)              # RuntimeError: Working outside of application context.
print(session)        # RuntimeError: Working outside of request context.
print(request)        # RuntimeError: Working outside of request context.
  • 四大变量在请求中可以访问,表明需要上下文环境
from flask import Flask,current_app,request,g,session


app = Flask(__name__)


@app.route('/')
def index():
    print(current_app)    # <Flask 'demo'>
    print(g)              # <flask.g of 'demo'>
    print(session)        # <NullSession {}>
    print(request)        # <Request 'http://127.0.0.1:5000/' [GET]>
    return 'Hello'


if __name__ == '__main__':
    app.run(debug=True)
  • 外部环境依赖解决思路:
  • 参照上例请求传参
  • 全局变量传参,如下
request = {
    
    'username':'zhangsan','pwd':'123456','age': 18}

@app.route('/')
def index():
    return request.get('username')
  • 假设有多个用户访问,此时就会有多个线程,username变量就会混乱。此时可以采用[{},{}…]存储,但由于列表是无须的,所以还是可能混乱。flask采用将数据绑定到线程上,保证数据不会混乱。
request = {
    
    '线程_id': 
        {
    
    'username':'zhangsan','pwd':'123456','age': 18}
        }

@app.route('/')
def index():
    return request.get('username')

flask上下文处理

在这里插入图片描述

参考文档

https://blog.csdn.net/m0_37519490/article/details/80704717
https://www.h5w3.com/13717.html

flask线程隔离

参考文档

https://blog.csdn.net/weixin_41207499/article/details/80724826
https://blog.csdn.net/weixin_43116723/article/details/107994241

猜你喜欢

转载自blog.csdn.net/qq_25672165/article/details/116056815