[Python] Lire le code source de Bottle Part 2

Cet article est une continuation de Dernière fois, mais vous devriez pouvoir le lire seul sans aucun problème. Eh bien, c'est un article pour ma propre organisation ...

Classe de bouteille de Honmaru

Enfin, la classe «Bouteille» de la tour du château. Tout d'abord, depuis la dernière fois, à partir de la méthode __call__.

bottle.py



class Bottle(object):

    def __call__(self, environ, start_response):
        """ Each instance of :class:'Bottle' is a WSGI application. """
        return self.wsgi(environ, start_response)

    def wsgi(self, environ, start_response):
        """ The bottle WSGI-interface. """
        try:
            out = self._cast(self._handle(environ))
            # rfc2616 section 4.3
            if response._status_code in (100, 101, 204, 304) or environ['REQUEST_METHOD'] == 'HEAD':
                if hasattr(out, 'close'): out.close()
                out = []
            start_response(response._status_line, response.headerlist)
            return out

En d'autres termes, la partie du corps du message de la réponse est générée à partir de self._cast (self._handle (environ)). Puisque self.cast () est un processus d'encodage de données dans un format adapté à un message HTTP, le contenu de la réponse est en fait créé parself._handle (environ).

C'est un peu long. Je vais donc essayer de le rendre rafraîchissant, sauf pour le bloc except.

bottle.py



#: A thread-safe instance of :class:`LocalResponse`. It is used to change the
#: HTTP response for the *current* request.
response = LocalResponse()

class Bottle(object):

    def _handle(self, environ):
        path = environ['bottle.raw_path'] = environ['PATH_INFO']
        if py3k:
            environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore')

        environ['bottle.app'] = self
        request.bind(environ)
        response.bind()

        try:
            while True: # Remove in 0.14 together with RouteReset
                out = None
                try:
                    self.trigger_hook('before_request')
                    route, args = self.router.match(environ)
                    environ['route.handle'] = route
                    environ['bottle.route'] = route
                    environ['route.url_args'] = args
                    out = route.call(**args)
                    break
                
                finally:
                    if isinstance(out, HTTPResponse):
                        out.apply(response)
                    try:
                        self.trigger_hook('after_request')

        return out

Response = LocalResponse () est soudainement apparu ici, mais la classe parente BaseResponse de la classe LocalResponse est définie ci-dessous.

bottle.py


class BaseResponse(object):
    """ Storage class for a response body as well as headers and cookies.

        This class does support dict-like case-insensitive item-access to
        headers, but is NOT a dict. Most notably, iterating over a response
        yields parts of the body and not the headers. #Les commentaires suivants sont omis
    """

    default_status = 200
    default_content_type = 'text/html; charset=UTF-8'

    def __init__(self, body='', status=None, headers=None, **more_headers):
        self._cookies = None
        self._headers = {}
        self.body = body

Il semble contenir des informations telles que des cookies, des en-têtes et des corps. Ne poursuivez pas plus loin pour le moment.

Maintenant, Bottle () ._ handler () est

route, args = self.router.match(environ)
out = route.call(**args)
return out

Cette partie est la clé, et il semble que le processus de routage soit effectué ici. Ici, self.router signifie Router () (défini dans Bottle .__ init__), alors jetons un coup d'œil à cette classe.

bottle.py



class Router(object):
    """ A Router is an ordered collection of route->target pairs. It is used to
        efficiently match WSGI requests against a number of routes and return
        the first target that satisfies the request. The target may be anything,
        usually a string, ID or callable object. A route consists of a path-rule
        and a HTTP method.

        The path-rule is either a static path (e.g. `/contact`) or a dynamic
        path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
        and details on the matching order are described in docs:`routing`.
    """

    def __init__(self, strict=False):
        self.rules = []  # All rules in order
        self._groups = {}  # index of regexes to find them in dyna_routes
        self.builder = {}  # Data structure for the url builder
        self.static = {}  # Search structure for static routes
        self.dyna_routes = {}
        self.dyna_regexes = {}  # Search structure for dynamic routes
        #: If true, static routes are no longer checked first.
        self.strict_order = strict
        self.filters = {
            're': lambda conf: (_re_flatten(conf or self.default_pattern),
                                None, None),
            'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))),
            'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))),
            'path': lambda conf: (r'.+?', None, None)
        }

    def match(self, environ):
        """ Return a (target, url_args) tuple or raise HTTPError(400/404/405). """
        verb = environ['REQUEST_METHOD'].upper()
        path = environ['PATH_INFO'] or '/'

        if verb == 'HEAD':
            methods = ['PROXY', verb, 'GET', 'ANY']
        else:
            methods = ['PROXY', verb, 'ANY']

        for method in methods:
            if method in self.static and path in self.static[method]:
                target, getargs = self.static[method][path]
                return target, getargs(path) if getargs else {}
            elif method in self.dyna_regexes:
                for combined, rules in self.dyna_regexes[method]:
                    match = combined(path)
                    if match:
                        target, getargs = rules[match.lastindex - 1]
                        return target, getargs(path) if getargs else {}

Ici, c'est un peu brouillon et il est difficile de comprendre ce qu'est la cible de la cible de retour, get args (path) ʻest, mais comme vous pouvez le voir en regardant route, args = self.router.match (environ), L'instanceRoute` est retournée, n'est-ce pas?

En d'autres termes, avant que vous ne le sachiez, Route () est enregistré comme self.static [method] [path].

Avant que vous ne le sachiez! ?? </ b>

Si vous regardez les sources ci-dessous, vous verrez progressivement la vue d'ensemble!

bottle.py



class Bottle(object):

    def route(self,
              path=None,
              method='GET',
              callback=None,
              name=None,
              apply=None,
              skip=None, **config):
        """ A decorator to bind a function to a request URL. Example::

                @app.route('/hello/<name>')
                def hello(name):
                    return 'Hello %s' % name

            The ``<name>`` part is a wildcard. See :class:`Router` for syntax
            details.
        """
        if callable(path): path, callback = None, path
        plugins = makelist(apply)
        skiplist = makelist(skip)

        def decorator(callback):
            if isinstance(callback, basestring): callback = load(callback)
            for rule in makelist(path) or yieldroutes(callback):
                for verb in makelist(method):
                    verb = verb.upper()
                    route = Route(self, rule, verb, callback, name=name, plugins=plugins, skiplist=skiplist, **config)
                    self.add_route(route)
            return callback

        return decorator(callback) if callback else decorator


    def add_route(self, route):
        """ Add a route object, but do not change the :data:`Route.app`
            attribute."""
        self.routes.append(route)
        self.router.add(route.rule, route.method, route, name=route.name)
        if DEBUG: route.prepare()

Alors on


@app.route('/hello/<name>')
    def hello(name):
        return 'Hello %s' % name

En attachant un décorateur, quand une demande s'envole


route = Route(self, rule, verb, callback, name=name, plugins=plugins, skiplist=skiplist, **config)
self.add_route(route)

Et vous avez donné les informations d'instance Router sur l'instance Route.

D'une manière ou d'une autre, ma tête s'est fâchée ...

Données de réponse

Ce que j'ai appris de ce qui précède

route, args = self.router.match(environ)
out = route.call(**args)
return out  #Les données de réponse sont incluses!

La route in est une instance de la Route (bien que cela semble évident), et leRoute (). Call ()contient les données de réponse souhaitées!

Donc si vous regardez la définition de la classe Route

bottle.py



class Route(object):
    """ This class wraps a route callback along with route specific metadata and
        configuration and applies Plugins on demand. It is also responsible for
        turing an URL path rule into a regular expression usable by the Router.
    """

    def __init__(self, app, rule, method, callback,
                 name=None,
                 plugins=None,
                 skiplist=None, **config):
        #: The application this route is installed to.
        self.app = app
        self.callback = callback
        #: A list of route-specific plugins (see :meth:`Bottle.route`).
        self.plugins = plugins or []

    @cached_property
    def call(self):
        """ The route callback with all plugins applied. This property is
            created on demand and then cached to speed up subsequent requests."""
        return self._make_callback()

    def _make_callback(self):
        callback = self.callback
        for plugin in self.all_plugins():
            try:
                if hasattr(plugin, 'apply'):
                    callback = plugin.apply(callback, self)
                else:
                    callback = plugin(callback)
            except RouteReset:  # Try again with changed configuration.
                return self._make_callback()
            if not callback is self.callback:
                update_wrapper(callback, self.callback)
        return callback

En d'autres termes, eh bien, le plug-in est pris en sandwich, et la partie importante est self.callback, mais celui-ci est dans Bottle.route en premier lieu

bottle.py



        def decorator(callback):
            if isinstance(callback, basestring): callback = load(callback)
            for rule in makelist(path) or yieldroutes(callback):
                for verb in makelist(method):
                    verb = verb.upper()
                    route = Route(self, rule, verb, callback, name=name, plugins=plugins, skiplist=skiplist, **config)
                    self.add_route(route)
            return callback

        return decorator(callback) if callback else decorator

Vous venez de passer le "callback" qui est sorti. c'est

@app.route('/hello/<name>')
    def hello(name):
        return 'Hello %s' % name

Cela correspond à la fonction "bonjour" dans, qui est également assez naturelle, mais les données de réponse sont générées selon la fonction définie ici.

Chan Chan.

quelqu'un aide

J'ai donc finalement eu une vue d'ensemble (bien que j'aie ignoré les parties de journalisation et de gestion des erreurs).

Mais la question est

from bottle import route

Je n'ai aucune idée de la raison pour laquelle le décorateur route est chargé. On a l'impression que la fonction décoratrice définie dans la classe Route est enregistrée dans l'environnement sous le nom route.

Si quelqu'un comprend, faites-le moi savoir ... en pleurant

Recommended Posts

[Python] Lire le code source de Bottle Part 2
[Python] Lire le code source de Bottle Part 1
[Python] Lire le code source de Flask
[Python3] Réécrire l'objet code de la fonction
[Python] Récupère le code de caractère du fichier
Pourquoi l'implémentation Python d'ISUCON 5 a utilisé Bottle
le zen de Python
Code pour vérifier le fonctionnement de Python Matplot lib
Convertir le code de caractère du fichier avec Python3
[Python + OpenCV] Peignez la partie transparente de l'image en blanc
Modèle de script python pour lire le contenu du fichier
Explication du concept d'analyse de régression à l'aide de python Partie 2
Découpez une partie de la chaîne à l'aide d'une tranche Python
Décomposons les bases du code Python de TensorFlow
Récupérer le code retour d'un script Python depuis bat
Explication du concept d'analyse de régression à l'aide de Python Partie 1
Vers la retraite de Python2
Expliquez le code de Tensorflow_in_ROS
2.x, 3.x code de caractères des séries python
À propos des fonctionnalités de Python
Installation source et installation de Python
Principes de base de Python x SIG (1)
J'ai téléchargé la source python
Le pouvoir des pandas: Python
Sakura L'histoire du fonctionnement de la bouteille Python sur Internet
Le processus de création et d'amélioration du code Python orienté objet
Récupérez la source de la page à charger indéfiniment avec python.
Principes de base de Python x SIG (3)
L'histoire de Python et l'histoire de NaN
[Python] La pierre d'achoppement de l'importation
First Python 3 ~ Le début de la répétition ~
Existence du point de vue de Python
pyenv-changer la version python de virtualenv
Demandez à python de lire la sortie de la commande
[Python] Comprendre le potentiel_field_planning de Python Robotics
Revue des bases de Python (FizzBuzz)
Principes de base de Python x SIG (partie 2)
À propos de la liste de base des bases de Python
Apprenez les bases de Python ① Débutants élémentaires
Compter les lignes de code source (SLOC)
Envelopper (partie de) la bibliothèque AtCoder en Cython pour une utilisation en Python
Suivez le flux de QAOA (VQE) au niveau du code source de Blueqat
Mesurez la couverture de test du code python poussé sur GitHub.
[PEP8] Reprenez le code source Python et écrivez-le proprement
Premier python ② Essayez d'écrire du code tout en examinant les fonctionnalités de python
Lire la sortie standard d'un sous-processus ligne par ligne en Python
J'ai écrit le code pour écrire le code Brainf * ck en python
Résumons le degré de couplage entre les modules avec du code Python
Installation du code Visual Studio et installation de python
Afficher le code source de l'implémentation dans iPython
Changer la longueur des chaînes csv Python
Vérifiez le comportement du destroyer en Python
[Python3] Comprendre les bases de Beautiful Soup
Comment couper la partie inférieure droite de l'image avec Python OpenCV
Passez le chemin du module python importé
Implémenter une partie du processus en C ++
Notes d'apprentissage depuis le début de Python 1