[Python] Lire le code source de Flask

Auparavant j'ai jeté un œil au code source de Bottle, donc cette fois j'aimerais jeter un œil à Flask.

Premier depuis le démarrage

Selon le tutoriel de Flask, une application minimale peut être créée comme celle-ci.

from flask import Flask
app = Flask(__name__)

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

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

C'est presque la même chose que Bottle. La définition de la méthode run ressemble à ceci.

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

Dans le cas de Bottle, vous avez utilisé la bibliothèque wsgiref. Lors du démarrage d'un serveur avec wsgiref.simple_server.make_server () et de la création d'une réponse, un processus louche comme start_response () a été implémenté.

D'autre part, Flask utilise une bibliothèque pratique appelée werkzeug. D'après Documentation, l'exemple ressemble à ceci.


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)

Vous pouvez facilement renvoyer une réponse comme return Response ('Hello, World!').

Identique à Bottle en ce que la classe Flask de Honmaru est enregistrée comme fonction d'application avec run_simple (hôte, port, self, ** options).

Cela signifie que le contenu de la réponse doit être obtenu en appelant la méthode «call» sous la forme «Flask () ()».

Ensuite, la définition du décorateur

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`.
(Omis ci-dessous)
        """

        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)

(Omis ci-dessous)
        """
        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

après tout

self.view_functions[endpoint] = view_func

Dans la partie de, la fonction de visualisation définie par l'utilisateur est enregistrée en tant que dictionnaire. Les bases sont les mêmes que celles de Bottle.

Quand une demande arrive

Il a été dit que le contenu de la réponse serait obtenu à partir de la méthode «call» de l'instance «Flask».

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)

Je suis également inquiet pour la partie de request_started.send (self). Je me demande si cela gère le processus. Mais je vais laisser ça pendant un moment.

Donc ce que je veux chasser maintenant, c'est

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

Dans la partie de, fondamentalement, les données sont reçues de preprocess_request () ou self.dispatch_request (). «Rv» est-il une abréviation de «corps de réponse»? Jetons donc un coup d'œil à ces deux méthodes.

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» signifie «contexte», non? Les gestionnaires enregistrés dans le contexte sont faits pour exécuter le processus. Alors quand ctx a-t-il été empilé dans _request_ctx_stack?

En fait, je suis passé, mais le contexte a été créé et poussé dans la méthode wsgi_app () de la classe Flask que j'ai vue plus tôt (veuillez regarder en arrière).

À propos, _request_ctx_stack est

src/flask/globals.py


_request_ctx_stack = LocalStack()

Créé en. Les poussées sont définies ci-dessous.

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)

Ensuite, nous exécutons ici la fonction obtenue avec self.view_functions [rule.endpoint], mais quand sera-t-elle enregistrée ici?

J'ai trouvé deux scènes.

Le premier est vers la fin de la méthode Flask.add_url_rule () que nous avons vue plus tôt (regardez cela aussi).

Le second est


class Flask():

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

Partie de.

Mais par exemple, quand endpoint a fait quand ʻadd_url_rule () `a été fait, la scène d'utilisation n'est pas différente, hmm. Je suis fatigué, donc c'est de plus en plus difficile à vérifier.

Eh bien, j'ai l'impression d'avoir vu ʻadd_url_rule () , et après tout, la fonction de point final définie par l'utilisateur dans dispatch_request () `est en cours d'exécution.

Alors, process_response () est-il un plug-in ou un processus périphérique?

finalement

Je suis fatigué, donc cette fois-ci.

Recommended Posts

[Python] Lire le code source de Flask
[Python] Lire le code source de Bottle Part 2
[Python] Lire le code source de Bottle Part 1
J'ai téléchargé la source python
Demandez à python de lire la sortie de la commande
[PEP8] Reprenez le code source Python et écrivez-le proprement
Afficher le code source de l'implémentation dans iPython
[Python3] Réécrire l'objet code de la fonction
Lisons le fichier RINEX avec Python ①
Lisez le fichier ligne par ligne avec Python
Lisez le fichier ligne par ligne avec Python
Lisez le fichier en spécifiant le code de caractère.
[Python] Récupère le code de caractère du fichier
Obtenir la liste de codes EDINET en Python
[Python] Lire la ligne spécifiée dans le fichier
python setup.py tester le code en utilisant le multiprocessus
code de caractère python
[Python] Code conscient des algorithmes
[python] Lecture de données
Essayez CI le code python poussé sur GitHub.
Code pour vérifier le fonctionnement de Python Matplot lib
Convertir le code de caractère du fichier avec Python3
Différences dans la façon d'écrire du code source externe entre Ruby et Python
Lire la source Python-Markdown: Comment créer un analyseur
Trouvez le maximum de Python
Un moyen simple de vérifier la source des modules Python
[Python] Lecture du code source Django Vue à partir de zéro ①
Lire DXF avec python
Modèle de script python pour lire le contenu du fichier
Lire la documentation OpenCV
Réécrire le code Python2 en Python3 (2to3)
Comment enregistrer une interruption comme vu dans le code source
infomap code de dessin Python
Avant d'écrire du code Python
Décomposons les bases du code Python de TensorFlow
le zen de Python
Installer Python à partir de la source
Récupérer le code retour d'un script Python depuis bat
Lisez le fichier xml en vous référant au didacticiel Python
Lire le code QR à partir du fichier image avec Python (Mac)
Supprimer la journalisation des flacons python3
[Python] Fractionner la date
J'ai senti que j'avais porté le code Python en C ++ 98.
Code d'état des requêtes Python
Lire le fichier csv Python
Programmation avec Python Flask
[Python] Lire depuis Stdin
Comment changer le fichier de configuration pour qu'il soit lu par Python
Activer l'environnement virtuel Python de virtualenv pour Visual Studio Code
[Python] Lisez le fichier csv et affichez la figure avec matplotlib
Supprimer les commentaires sur une ligne, y compris le japonais du code source en Python
Lecture du code source Qiskit ~ Terra: lecture du backend Get, Call, Get Result
Je viens d'écrire le matériel original pour l'exemple de code python
Histoire que Python a cessé de travailler avec VS Code (Windows 10)
Le processus de création et d'amélioration du code Python orienté objet
Commerce du système à partir de Python3: obtenez le dernier code de programme