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 ...
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'instance
Route` 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 ...
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.
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