Zuvor habe ich mir den Quellcode von Bottle angesehen, daher möchte ich diesmal einen Blick auf Flask werfen.
Gemäß dem Tutorial von Flask kann eine minimale Anwendung wie diese erstellt werden.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run()
Es ist fast das gleiche wie Flasche.
Die Definition der run
Methode sieht so aus.
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
Im Fall von Bottle haben Sie die wsgiref
-Bibliothek verwendet.
Beim Starten eines Servers mit "wsgiref.simple_server.make_server ()" und Erstellen einer Antwort wurde ein fischartiger Prozess wie "start_response ()" implementiert.
Auf der anderen Seite verwendet Flask eine praktische Bibliothek namens "werkzeug". Laut Dokumentation sieht das Beispiel so aus.
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)
Sie können ganz einfach eine Antwort wie "Antwort zurückgeben (" Hallo Welt! ")" Zurückgeben.
Wie Bottle, da die Flask
-Klasse von Honmaru als Anwendungsfunktion mit run_simple (Host-, Port-, Self-, ** -Optionen)
registriert ist.
Das bedeutet, dass der Antwortinhalt durch Aufrufen der Methode "call" in Form von "Flask () ()" abgerufen werden sollte.
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`.
(Unten weggelassen)
"""
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)
(Unten weggelassen)
"""
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
Nach alldem
self.view_functions[endpoint] = view_func
In dem Teil von wird die vom Benutzer definierte Ansichtsfunktion als Wörterbuch registriert. Die Grundlagen sind die gleichen wie bei Bottle.
Es wurde gesagt, dass der Antwortinhalt von der __call__
-Methode der Flask
-Instanz erhalten würde.
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)
Ich mache mir auch Sorgen um den Teil "request_started.send (self)". Ich frage mich, ob dies den Prozess verwaltet. Aber ich werde das für eine Weile verlassen.
Was ich jetzt verfolgen möchte, ist
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
Im Teil von werden Daten grundsätzlich von "preprocess_request ()" oder "self.dispatch_request ()" empfangen. Ist "rv" eine Abkürzung für "Antwortkörper"? Schauen wir uns also diese beiden Methoden an.
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
bedeutet" Kontext ", richtig?
Die im Kontext registrierten Handler werden ausgeführt, um den Prozess auszuführen.
Wann wurde dann "ctx" in "_request_ctx_stack" gestapelt?
Eigentlich habe ich durchlaufen, aber der Kontext wurde erstellt und in der wsgi_app ()
Methode der Flask
Klasse gepusht, die ich zuvor gesehen habe (bitte schauen Sie zurück).
Übrigens ist _request_ctx_stack
src/flask/globals.py
_request_ctx_stack = LocalStack()
Erstellt in. Pushs sind unten definiert.
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)
Als nächstes führen wir hier die mit "self.view_functions [rule.endpoint]" erhaltene Funktion aus, aber wann wird sie hier registriert?
Ich habe zwei Szenen gefunden.
Die erste ist gegen Ende der Methode "Flask.add_url_rule ()", die wir zuvor gesehen haben (schauen Sie auch darauf zurück).
Der zweite ist
class Flask():
@setupmethod
def endpoint(self, endpoint):
def decorator(f):
self.view_functions[endpoint] = f
return f
return decorator
Teil von.
Dies sind jedoch keine unterschiedlichen Verwendungsszenen, da der Endpunkt dies tat, als beispielsweise "add_url_rule ()" ausgeführt wurde, hmm. Ich werde müde, daher wird es schwieriger zu überprüfen.
Nun, es fühlt sich an, als hätte ich "add_url_rule ()" gesehen, und schließlich wird die vom Benutzer in "dispatch_request ()" definierte Endpunktfunktion ausgeführt.
Ist "process_response ()" dann ein Plug-In oder ein Peripherieprozess?
Ich bin müde, also diesmal.
Recommended Posts