[Python] Lire le code source de Bottle Part 1

Lire bottle.py

J'ai essayé de lire le code de Bottle, un framework web de Python. Le code source de Bottle est agrégé dans bottle.py, vous devriez donc lire ce fichier.

Cliquez ici pour GitHub. https://github.com/bottlepy/bottle

Le code cité ci-dessous provient de bottle.py avec uniquement les pièces nécessaires. De plus, j'ajoute des commentaires par endroits.

Il est difficile de suivre les détails depuis le début, je voudrais donc d'abord saisir le flux global.

Première entrée: courir ()

Je voudrais suivre la source dans l'ordre depuis le démarrage. Démarrez Bottle comme suit, par exemple, conformément à la documentation.

from bottle import route, run, template

@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

Commençons par run (). Encore une fois, juste au cas où, le code source est (principalement) cassé et cité.

bottle.py


def run(app=None,
        server='wsgiref',
        host='127.0.0.1',
        port=8080,
        interval=1,
        reloader=False,
        quiet=False,
        plugins=None,
        debug=None,
        config=None, **kargs):

    app = app or default_app()

    if server in server_names:
            server = server_names.get(server)
        if isinstance(server, basestring):
            server = load(server)
        if isinstance(server, type):
            server = server(host=host, port=port, **kargs)
        if not isinstance(server, ServerAdapter):
            raise ValueError("Unknown or unsupported server: %r" % server)

    if reloader:
            lockfile = os.environ.get('BOTTLE_LOCKFILE')
            bgcheck = FileCheckerThread(lockfile, interval)
            with bgcheck:
                server.run(app)
            if bgcheck.status == 'reload':
                sys.exit(3)
        else:
            server.run(app)

en bref

app = app or default_app()  #La valeur par défaut est app=None
server = server_names.get(server)  #La valeur par défaut est le serveur='wsgiref'
server.run(app)

C'est vrai. Jetons d'abord un œil à la définition de default_app () sur la première ligne.

Qu'est-ce que la classe AppStack?

La partie définition est

apps = app = default_app = AppStack()

alors,

bottle.py



class AppStack(list):
    """ A stack-like list. Calling it returns the head of the stack. """

    def __call__(self):
        """ Return the current default application. """
        return self.default

    def push(self, value=None):
        """ Add a new :class:`Bottle` instance to the stack """
        if not isinstance(value, Bottle):
            value = Bottle()
        self.append(value)
        return value
    new_app = push

    @property
    def default(self):
        try:
            return self[-1]
        except IndexError:
            return self.push()

Je vois. AppStack hérite de la liste. Du côté qui utilise ʻAppStack () ( server), l'important est la méthode call`.

Pour résumer l'examen de la méthode d'appel,

app = AppStack()
app()

Renvoie une instance de Bottle (avec des arguments par défaut). En même temps, c'est un mécanisme pour l'accumuler sous forme de liste.

J'aimerais rassembler la source de la classe Bottle, mais comme son nom l'indique, c'est le cœur de Bottle et c'est long. En premier lieu, l'instance ʻAppStack` est


server.run(app)

Il est passé comme argument à la méthode run du serveur, et il devrait être appelé ʻapp () après cela, donc vérifions d'abord server`.

Classe de serveur

En regardant un peu en arrière,


app = app or default_app()  #La valeur par défaut est app=None
server = server_names.get(server)  #La valeur par défaut est le serveur='wsgiref'
server.run(app)

L'histoire d'essayer de découvrir qui le serveur a défini dans la partie de.

bottle.py



server_names = {
    'cgi': CGIServer,
    'flup': FlupFCGIServer,
    'wsgiref': WSGIRefServer,  #C'est la valeur par défaut
    'waitress': WaitressServer,
    #Ce qui suit est omis
}

C'est un dictionnaire, et chaque valeur hérite de la classe ServerAdapter. Comme il s'agit d'un "parent de formulaire uniquement", je vais soudainement à la classe WSGIRefServer.

bottle.py


class WSGIRefServer(ServerAdapter):
    def run(self, app):  # pragma: no cover
        from wsgiref.simple_server import make_server
        from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
        import socket

        server_cls = self.options.get('server_class', WSGIServer)
        self.srv = make_server(self.host, self.port, app, server_cls,
                               handler_cls)
        self.port = self.srv.server_port  # update port actual port (0 means random)
        try:
            self.srv.serve_forever()
        except KeyboardInterrupt:
            self.srv.server_close()  # Prevent ResourceWarning: unclosed socket
            raise

ʻApp = AppStack () passé à server.run () est passé à la fonction make_server. La fonction make_server elle-même provient de la bibliothèque standard python wsgiref`.

https://docs.python.org/ja/3/library/wsgiref.html

L'interface de passerelle de serveur Web (WSGI) est une interface standard entre le logiciel de serveur Web et les applications Web écrites en Python. Une interface standard facilite l'utilisation d'applications prenant en charge WSGI sur un certain nombre de serveurs Web différents.

Jetons donc un œil à l'exemple dans la documentation wsgiref.


from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server

def simple_app(environ, start_response):
    setup_testing_defaults(environ)

    status = '200 OK'
    headers = [('Content-type', 'text/plain')]

    start_response(status, headers)

    ret = ["%s: %s\n" % (key, value)
           for key, value in environ.iteritems()]
    return ret

httpd = make_server('', 8000, simple_app)
print "Serving on port 8000..."
httpd.serve_forever()

-Passez la fonction que vous avez définie (fonction simple_app dans l'exemple) comme troisième argument de la fonction make_server! -Ensuite, lorsqu'une requête arrive, vous pouvez laisser cette fonction la gérer! -Cependant, préparez deux arguments (environ, start_response)!

C'est vrai.

Donc, en regardant les sources jusqu'à présent, cette fonction (simple_app dans l'exemple) était Bottle ().

C'est une instance, pas une fonction! mais c'est d'accord! Si vous avez une instance __call__, vous pouvez l'appeler comme une fonction!

Cela semble fastidieux, mais si vous l'organisez, c'est un échantillon


simple_app(environ, start_response)

L'acte à exécuter est

Bottle()(environ, start_response)

Équivaut à être exécuté.

C'est-à-dire </ b>

Cela semble évident, mais l'instance Bottle est responsable du traitement réel </ b>

J'ai découvert.

Réellement

bottle.py



class Bottle(object):

    def __call__(self, environ, start_response):

Est défini.

C'était si long.

La classe Bottle doit être Next.

Résumé à ce jour

Bottle () est passé comme argument à la fonction make_server de la bibliothèque wsgiref, et ceci est responsable du traitement réel.

Recommended Posts