[Python] Lesen Sie den Flask-Quellcode

Zuvor habe ich mir den Quellcode von Bottle angesehen, daher möchte ich diesmal einen Blick auf Flask werfen.

Zuerst vom Start

Gemäß dem Tutorial von Flask kann eine minimale Anwendung wie diese erstellt werden.

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!"

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

Es ist fast das gleiche wie Flasche. Die Definition der run Methode sieht so aus.

src/flask/app.py


class Flask(_PackageBoundObject):

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

        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

Im Fall von Bottle haben Sie die wsgiref-Bibliothek verwendet. Beim Starten eines Servers mit "wsgiref.simple_server.make_server ()" und Erstellen einer Antwort wurde ein fischartiger Prozess wie "start_response ()" implementiert.

Auf der anderen Seite verwendet Flask eine praktische Bibliothek namens "werkzeug". Laut Dokumentation sieht das Beispiel so aus.


from werkzeug.wrappers import Request, Response

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

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, application)

Sie können ganz einfach eine Antwort wie "Antwort zurückgeben (" Hallo Welt! ")" Zurückgeben.

Wie Bottle, da die Flask-Klasse von Honmaru als Anwendungsfunktion mit run_simple (Host-, Port-, Self-, ** -Optionen) registriert ist.

Das bedeutet, dass der Antwortinhalt durch Aufrufen der Methode "call" in Form von "Flask () ()" abgerufen werden sollte.

Als nächstes die Definition des Dekorateurs

src/flask/app.py



class Flask(_PackageBoundObject):

    def route(self, rule, **options):
        """A decorator that is used to register a view function for a
        given URL rule.  This does the same thing as :meth:`add_url_rule`
        but is intended for decorator usage::

            @app.route('/')
            def index():
                return 'Hello World'

        For more information refer to :ref:`url-route-registrations`.
(Unten weggelassen)
        """

        def decorator(f):
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f

        return decorator


    @setupmethod
    def add_url_rule(
        self,
        rule,
        endpoint=None,
        view_func=None,
        provide_automatic_options=None,
        **options
    ):
        """
        Basically this example::

            @app.route('/')
            def index():
                pass

        Is equivalent to the following::

            def index():
                pass
            app.add_url_rule('/', 'index', index)

(Unten weggelassen)
        """
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options["endpoint"] = endpoint
        methods = options.pop("methods", None)

        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            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"])'
            )
        methods = set(item.upper() for item in methods)

        # Methods that should always be added
        required_methods = set(getattr(view_func, "required_methods", ()))

        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        if provide_automatic_options is None:
            provide_automatic_options = getattr(
                view_func, "provide_automatic_options", None
            )

        if provide_automatic_options is None:
            if "OPTIONS" not in methods:
                provide_automatic_options = True
                required_methods.add("OPTIONS")
            else:
                provide_automatic_options = False

        # Add the required methods now.
        methods |= required_methods

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError(
                    "View function mapping is overwriting an "
                    "existing endpoint function: %s" % endpoint
                )
            self.view_functions[endpoint] = view_func

Nach alldem

self.view_functions[endpoint] = view_func

In dem Teil von wird die vom Benutzer definierte Ansichtsfunktion als Wörterbuch registriert. Die Grundlagen sind die gleichen wie bei Bottle.

Wenn eine Anfrage kommt

Es wurde gesagt, dass der Antwortinhalt von der __call__ -Methode der Flask-Instanz erhalten würde.

src/flask/app.py



class Flask(_PackageBoundObject):

    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)

    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.
        """
        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 full_dispatch_request(self):
        """Dispatches the request and on top of that performs request
        pre and postprocessing as well as HTTP exception catching and
        error handling.
        """
        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)

Ich mache mir auch Sorgen um den Teil "request_started.send (self)". Ich frage mich, ob dies den Prozess verwaltet. Aber ich werde das für eine Weile verlassen.

Was ich jetzt verfolgen möchte, ist

rv = self.preprocess_request()
if rv is None:
    rv = self.dispatch_request()

Im Teil von werden Daten grundsätzlich von "preprocess_request ()" oder "self.dispatch_request ()" empfangen. Ist "rv" eine Abkürzung für "Antwortkörper"? Schauen wir uns also diese beiden Methoden an.

1、 process_response()

src/flask/app.py



class Flask(_PackageBoundObject):

    def process_response(self, response):
        """Can be overridden in order to modify the response object
        before it's sent to the WSGI server.  By default this will
        call all the :meth:`after_request` decorated functions.
        """
        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
        for handler in funcs:
            response = handler(response)
        if not self.session_interface.is_null_session(ctx.session):
            self.session_interface.save_session(self, ctx.session, response)
        return response

ctx bedeutet" Kontext ", richtig? Die im Kontext registrierten Handler werden ausgeführt, um den Prozess auszuführen. Wann wurde dann "ctx" in "_request_ctx_stack" gestapelt?

Eigentlich habe ich durchlaufen, aber der Kontext wurde erstellt und in der wsgi_app () Methode der Flask Klasse gepusht, die ich zuvor gesehen habe (bitte schauen Sie zurück).

Übrigens ist _request_ctx_stack

src/flask/globals.py


_request_ctx_stack = LocalStack()

Erstellt in. Pushs sind unten definiert.

src/flask/ctx.py


class RequestContext(object):

    def push(self):
        """Binds the request context to the current context."""
        
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        # Before we push the request context we have to ensure that there
        # is an application context.
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, "exc_clear"):
            sys.exc_clear()

        _request_ctx_stack.push(self)

2、 dispatch_request()

src/flask/app.py



class Flask(_PackageBoundObject):

    def dispatch_request(self):
        """Does the request dispatching.  Matches the URL and returns the
        return value of the view or error handler.  This does not have to
        be a response object.  In order to convert the return value to a
        proper response object, call :func:`make_response`.
        """
        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        # if we provide automatic options for this URL and the
        # request came with the OPTIONS method, reply automatically
        if (
            getattr(rule, "provide_automatic_options", False)
            and req.method == "OPTIONS"
        ):
            return self.make_default_options_response()
        # otherwise dispatch to the handler for that endpoint
        return self.view_functions[rule.endpoint](**req.view_args)

Als nächstes führen wir hier die mit "self.view_functions [rule.endpoint]" erhaltene Funktion aus, aber wann wird sie hier registriert?

Ich habe zwei Szenen gefunden.

Die erste ist gegen Ende der Methode "Flask.add_url_rule ()", die wir zuvor gesehen haben (schauen Sie auch darauf zurück).

Der zweite ist


class Flask():

    @setupmethod
    def endpoint(self, endpoint):
        def decorator(f):
            self.view_functions[endpoint] = f
            return f
        return decorator

Teil von.

Dies sind jedoch keine unterschiedlichen Verwendungsszenen, da der Endpunkt dies tat, als beispielsweise "add_url_rule ()" ausgeführt wurde, hmm. Ich werde müde, daher wird es schwieriger zu überprüfen.

Nun, es fühlt sich an, als hätte ich "add_url_rule ()" gesehen, und schließlich wird die vom Benutzer in "dispatch_request ()" definierte Endpunktfunktion ausgeführt.

Ist "process_response ()" dann ein Plug-In oder ein Peripherieprozess?

Schließlich

Ich bin müde, also diesmal.

Recommended Posts

[Python] Lesen Sie den Flask-Quellcode
[Python] Lesen Sie den Quellcode von Flasche Teil 2
[Python] Lesen Sie den Quellcode von Flasche Teil 1
Ich habe die Python-Quelle heruntergeladen
Lassen Sie Python die Befehlsausgabe lesen
[PEP8] Übernehmen Sie den Python-Quellcode und schreiben Sie ihn ordentlich
Zeigen Sie den Implementierungsquellcode in iPython an
[Python3] Schreiben Sie das Codeobjekt der Funktion neu
Lesen wir die RINEX-Datei mit Python ①
Lesen Sie die Datei Zeile für Zeile mit Python
Lesen Sie die Datei Zeile für Zeile mit Python
Lesen Sie die Datei, indem Sie den Zeichencode angeben.
[Python] Ruft den Zeichencode der Datei ab
Ruft die EDINET-Codeliste in Python ab
[Python] Lesen Sie die angegebene Zeile in der Datei
python setup.py testet den Code mit Multiprocess
Python-Zeichencode
[Python] Code, der Algorithmen kennt
[Python] Daten lesen
Probieren Sie CI den Push-Python-Code auf GitHub aus.
Code zum Überprüfen des Betriebs von Python Matplot lib
Konvertieren Sie den Zeichencode der Datei mit Python3
Unterschiede beim Schreiben von externem Quellcode zwischen Ruby und Python
Lesen Sie die Python-Markdown-Quelle: So erstellen Sie einen Parser
Finden Sie das maximale Python
Einfache Möglichkeit, die Quelle der Python-Module zu überprüfen
[Python] Lesen des Django-Quellcodes Ansicht ab Null ①
Lesen Sie DXF mit Python
Vorlage des Python-Skripts zum Lesen des Inhalts der Datei
Lesen Sie die OpenCV-Dokumentation
Schreiben Sie Python2-Code in Python3 um (2to3)
So registrieren Sie einen Interrupt wie im Quellcode angegeben
Infomap Python-Zeichencode
Vor dem Schreiben von Python-Code
Lassen Sie uns die Grundlagen des Python-Codes von TensorFlow aufschlüsseln
der Zen von Python
Installieren Sie Python von der Quelle
Holen Sie sich den Rückkehrcode eines Python-Skripts von bat
Lesen Sie die XML-Datei anhand des Python-Tutorials
Lesen Sie den QR-Code aus der Bilddatei mit Python (Mac).
Unterdrücken Sie die Python3-Kolbenprotokollierung
[Python] Teilen Sie das Datum
Ich hatte das Gefühl, dass ich den Python-Code nach C ++ 98 portiert habe.
Python Fordert den Statuscode an
Lesen Sie die Python-CSV-Datei
Programmieren mit Python Flask
[Python] Von Stdin lesen
So wechseln Sie die Konfigurationsdatei, die von Python gelesen werden soll
Aktivieren Sie die virtuelle Python-Umgebung von virtualenv für Visual Studio Code
[Python] Lesen Sie die CSV-Datei und zeigen Sie die Abbildung mit matplotlib an
Entfernen Sie einzeilige Kommentare einschließlich Japanisch aus dem Quellcode in Python
Qiskit-Quellcode lesen ~ Terra: Backend lesen Get, Call, Get Result
Ich habe gerade das Originalmaterial für den Python-Beispielcode geschrieben
Die Geschichte, dass Python nicht mehr mit VS Code (Windows 10) arbeitet
Der Prozess, Python-Code objektorientiert zu machen und zu verbessern
Systemhandel ab Python3: Holen Sie sich den neuesten Programmcode