Auparavant j'ai jeté un œil au code source de Bottle, donc cette fois j'aimerais jeter un œil à Flask.
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 () ()».
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.
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?
Je suis fatigué, donc cette fois-ci.
Recommended Posts