当前位置:网站首页>Flask 1.1.4 werkzeug1.0.1 analyse du code source: processus de démarrage

Flask 1.1.4 werkzeug1.0.1 analyse du code source: processus de démarrage

2022-07-07 05:52:00 Ingénieur

Basé surQuickStartUn desdemoPour analyser

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"


if __name__ == '__main__':
    app.run()

@app.route(“/”) Est une fonction d'usine de décoration qui reçoit les paramètres,Renvoie un décorateur qui ferme un paramètre spécifique

# flask/app.py L1288
def route(self, rule, **options):
	# Garniture d'enregistrement typique
    def decorator(f):
        endpoint = options.pop("endpoint", None)
        # Cette méthodeurlInformation etview_func Inscrivez - vous àflaskDans l'exemple
        self.add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

La clé est self.add_url_rule(rule, endpoint, f, **options) Méthodes Le code suivant a été simplifié

# flask/app.py L1178
@setupmethod
def add_url_rule(
    self,
    rule,
    endpoint=None,
    view_func=None,
    provide_automatic_options=None,
    **options
):
	# Ici.endpoint Est un concept important   Les détails suivants seront expliqués dans la section routage  
	# Inflask- Oui. url_mapEtview_functions  Un lien important entre 
	#  Chaque fois qu'une demande arrive  Vas - y.url_map Recherche dans  endpoint,args Et partir view_functions[endpoint](args) Obtenir les résultats
	# Si ce n'est pas le cas, Par défautview_func.__name__
    if endpoint is None:
        endpoint = _endpoint_from_view_func(view_func)
    options["endpoint"] = endpoint
    methods = options.pop("methods", None)

	# ...

	# Ici url_rule_class C'est unFlaskPropriétés de la classe La valeur est: werkzeug.routing.Rule
	#  Donc voici la construction  werkzeug.routing.Rule Objet
    rule = self.url_rule_class(rule, methods=methods, **options)
    rule.provide_automatic_options = provide_automatic_options
	
	# Construit ruleEnregistrer l'objet dans url_map Dans les propriétés de l'Instance  
	# url_map- Oui.werkzeug.routing.Map Objet
	# flask La partie routage est en fait basée sur werkzeugCapacité de
    self.url_map.add(rule)
    if view_func is not None:
        # ...
        # endpointPourkeyVa correspondre àview_funcEnregistrer dans view_functionsDans les propriétés
        # view_functions C'est ça. dict
        self.view_functions[endpoint] = view_func

C'est simple.,add_url_rule Le rôle principal est Oui.ruleEtview_func L'information est tenue à jour jusqu'à flaskExemple de url_mapEtview_functionsDans les propriétés

ok,Poursuivre les recherches app.run() C'est là que commence le processus (app.run() Ça va commencer par un simple web Le serveur est utilisé pour le développement et les tests , L'environnement de production utilisera d'autres web server, Mais avec cette étude, il est possible de lancer le processus )

# flask/app.py L889
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
  	# ...
  	
    from werkzeug.serving import run_simple

    try:
    	# Avec l'aide de werkzeug.serving.run_simple
    	# Notez le troisième paramètre Oui.flask Exemple transmis à  run_simpleMéthodes
    	# Suivi web serverAppelleflask Traitement logique de l'Instance 
        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

Regarde. run_simpleCode, Passez à la recherche werkzeug Code source

# werkzeug/serving.py L876
def run_simple(
    hostname,
    port,
    application,
    use_reloader=False,
    use_debugger=False,
    use_evalex=True,
    extra_files=None,
    reloader_interval=1,
    reloader_type="auto",
    threaded=False,
    processes=1,
    request_handler=None,
    static_files=None,
    passthrough_errors=False,
    ssl_context=None,
):
    
 	#...
 	# application Les paramètres correspondent à flaskExemple

    def inner():
        try:
            fd = int(os.environ["WERKZEUG_SERVER_FD"])
        except (LookupError, ValueError):
            fd = None
        #  C'est la clé  Créer unserverExemple
        srv = make_server(
            hostname,
            port,
            application,
            threaded,
            processes,
            request_handler,
            passthrough_errors,
            ssl_context,
            fd=fd,
        )
        if fd is None:
            log_startup(srv.socket)
        # Appelezweb serverExemple deserve_foreverMéthodes
        srv.serve_forever()

    if use_reloader:
        #...
    else:
        inner()

Continue.,Regardez d'abord make_serverMéthodes

# werkzeug/serving.py L830
def make_server(
    host=None,
    port=None,
    app=None,
    threaded=False,
    processes=1,
    request_handler=None,
    passthrough_errors=False,
    ssl_context=None,
    fd=None,
):
    if threaded and processes > 1:
        raise ValueError("cannot have a multithreaded and multi process server.")
    elif threaded:
        return ThreadedWSGIServer(
            host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
        )
    elif processes > 1:
        return ForkingWSGIServer(
            host,
            port,
            app,
            processes,
            request_handler,
            passthrough_errors,
            ssl_context,
            fd=fd,
        )
    else:
        return BaseWSGIServer(
            host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
        )

C'est basé sur des paramètres spécifiques Construire unserverRetour d'instance Choisissez - en un et regardez Regarde. BaseWSGIServer

# werkzeug/serving.py L708
class BaseWSGIServer(HTTPServer, object):
    def __init__(
        self,
        host,
        port,
        app,
        handler=None,
        passthrough_errors=False,
        ssl_context=None,
        fd=None,
    ):
        if handler is None:
        	#  Notez ici que   Les demandes subséquentes seront traitées en détail  WSGIRequestHandler
            handler = WSGIRequestHandler

      	# Par ici  Liaison du port et écoute 
      	#  Et passer dans la classe  WSGIRequestHandler
        HTTPServer.__init__(self, server_address, handler)
		# appC'est - à - dire:flaksExemple
        self.app = app
       
    # Cette méthode est utilisée dansmake_server  Appelé immédiatement après le retour de l'Instance 
    def serve_forever(self):
        self.shutdown_signal = False
        try:
        	# Ici HTTPServer Situé àpythonBibliothèque intégréehttpMoyenne http.server.HTTPServer
        	# Va maintenantserverInstance passée comme paramètre
            HTTPServer.serve_forever(self)
        except KeyboardInterrupt:
            pass
        finally:
            self.server_close()

En bas HTTPServer.serve_forever() On peut le découvrir. serve_forever En fait, c'est le parent de son parent Bibliothèque intégrée socketserverMoyenne BaseServer Méthode d'implémentation de classe

# socketserver.py L153
class BaseServer:
    timeout = None

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor. May be extended, do not override."""
        self.server_address = server_address
        # Cette propriété est WSGIRequestHandler
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()
        try:
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)
				#  C'est une boucle infinie   Recevoir une demande traiter une demande 
                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    # bpo-35017: shutdown() called during select(), exit immediately.
                    if self.__shutdown_request:
                        break
                    if ready:
                    	#  Après avoir reçu une demande spécifique  Traitement
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
            
	def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request()
        except OSError:
            return
        if self.verify_request(request, client_address):
            try:
            	#  Après avoir reçu une demande spécifique  Traitement
                self.process_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
            except:
                self.shutdown_request(request)
                raise
        else:
            self.shutdown_request(request)
            
    def process_request(self, request, client_address):
    	#  Après avoir reçu une demande spécifique  Traitement
        self.finish_request(request, client_address)
        self.shutdown_request(request)
        
    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        #  Chaque fois qu'il est instantané  WSGIRequestHandler Traitement des demandes
        # Attention!  Le troisième paramètre prend le courant serverInstance passée dans
        self.RequestHandlerClass(request, client_address, self)

D'accord. Final final Le traitement de la demande a eu lieu le WSGIRequestHandler Traitement pendant l'Instanciation
Voyons voir. WSGIRequestHandlerCode pour
WSGIRequestHandler L'initialisation prend en fait la classe mère de la classe mère BaseRequestHandler De__init__Méthodes

# socketserver.py 696
class BaseRequestHandler:
    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
        	#  Traitement spécifique   En fait, c'est l'implémentation de la Sous - classe appelée 
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass

# werkzeug/serving.py L163
class WSGIRequestHandler(BaseHTTPRequestHandler, object):
	#  Approche de base du traitement des demandes 
    def run_wsgi(self):
		#  Classique ici  Oui.http Le contenu est converti en conformité avec wsgiContenu du format  Parce qu'il y a un wsgiDeappOh là là!
        self.environ = environ = self.make_environ()
        headers_set = []
        headers_sent = []
		# Ça te dit quelque chose? Appelezwsgi appUn des paramètres de
        def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if headers_sent:
                        reraise(*exc_info)
                finally:
                    exc_info = None
            elif headers_set:
                raise AssertionError("Headers already set")
            headers_set[:] = [status, response_headers]
            return write

        def execute(app):
        	# Classiquewsgi appAppelez  La valeur de retour peut être itérative 
        	# Iciapp- Oui.flaskExemple  L'instance peut être appelée naturellement parce que FlaskC'est fait.__call__Méthodes
            application_iter = app(environ, start_response)
            
        try:
        	# Exécuter cette méthode Le paramètre estflaskExemple
            execute(self.server.app)
        except (_ConnectionError, socket.timeout) as e:
            self.connection_dropped(e, environ)
        except Exception:
            #...

	#  Appelé par la classe précédente handleC'est ça
    def handle(self):
        """Handles a request ignoring dropped connections."""
        try:
        	# Appelé parenthandle
            BaseHTTPRequestHandler.handle(self)
        except (_ConnectionError, socket.timeout) as e:
            self.connection_dropped(e)
        except Exception as e:
            if self.server.ssl_context is None or not is_ssl_error(e):
                raise
        if self.server.shutdown_signal:
            self.initiate_shutdown()
    
    # C'est vrai  La classe suivante appelle à nouveau cette méthode 
    def handle_one_request(self):
        """Handle a single HTTP request."""
        self.raw_requestline = self.rfile.readline()
        if not self.raw_requestline:
            self.close_connection = 1
        elif self.parse_request():
        	# Final final  On y va. 
            return self.run_wsgi()

# http/server.py 147
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
	#  C'est ce que la classe précédente a appelé 
    def handle(self):
        """Handle multiple requests if necessary."""
        self.close_connection = True
		#  Encore une fois appelé ici  WSGIRequestHandler De handle_one_request
        self.handle_one_request()
        while not self.close_connection:
            self.handle_one_request()

La ligne d'appel ci - dessus semble un peu tordue , Parce qu'il s'agit d'une superposition des méthodes d'héritage , Certains appellent le parent , Il y a des sous - classes qui marchent .

Je vois.: En fait, c'est écouter le port , Et ouvre un cycle infini , Chaque fois qu'une demande est reçue ,Instancie justeWSGIRequestHandlerTraitement,EtWSGIRequestHandlerSurtout.HTTPFormat des données àWSGIConversion des données,Et utiliserWSGILa façon dontflask L'Instance effectue le traitement logique réel et renvoie les données .

Regarde ça.Flask L'Instance supporte le Code appelé .

# flask/app.py L103
class Flask(_PackageBoundObject):

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
        
    def wsgi_app(self, environ, start_response):
    	# Important  Créer l'objet contextuel actuellement demandé  flask.ctx.RequestContextObjet
    	#  Les informations demandées ont été traitées ici (J'ai endpointEtview_func_args)
        ctx = self.request_context(environ)
        error = None
        try:
            try:
            	#  Mettre le contexte de la requête actuelle sur la pile (LocalStack)
            	# IciFlask Détails de la mise en oeuvre du contexte   Transmettre le contexte par le partage  À l'arrière.view_func
                ctx.push()
                #  Exécution de la distribution 
                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
            # Une fois le traitement de la demande terminé  Contexte de nettoyage  Hors de la pile
            ctx.auto_pop(error)
            
    def full_dispatch_request(self):
    	# hook
        self.try_trigger_before_first_request_functions()
        try:
        	# hook
            request_started.send(self)
            rv = self.preprocess_request()
            if rv is None:
            	#  Méthode principale d'exécution de la logique 
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)

    def dispatch_request(self):
    	#  Obtenir le contexte actuel de la demande directement à partir de la pile de contexte 
        req = _request_ctx_stack.top.request
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        # ...
        # SelonendpointObtenir la correspondanceview_func Exécuter et retourner
        return self.view_functions[rule.endpoint](**req.view_args)

okk,IciFlask Le processus de démarrage est presque terminé .
Résumez les étapes :

  1. Créer unFlaskExemple app
  2. Oui.urlEtview_func Adoption app.add_url_rule() EntretienappDeurl_mapEtview_functions Dans les propriétés.url_map Contient la logique de routage ,view_functions La fonction logique correspondante est stockée ,Par les deuxendpointCorrélation.
  3. Étapes1Et2 Une fois terminé, En fait, on suit WSGIAccord web applicationPrêt à, Tout ce qu'on va faire, c'est l'accrocher au même support WSGIAccordweb serverEn bas.web serverAcceptéHTTPDemande d'accord,Et le convertir enWSGIContenu du format,Puis appelez app(environ, start_response) Effectuer un traitement logique spécifique ,Et revenir àWSGIRésultats du format.Plus tardWSGI Le résultat du format est converti en HTTP Le format est retourné au client .
  4. werkzeug Dans BaseWSGIServer HéritéPythonDe la bibliothèque intégrée http.server.HTTPServer, J'en ai tiré HTTPServer Capacité d'écouter les ports et d'obtenir les demandes ,Et intégré appEt WSGIRequestHandler. Chaque fois qu'une demande est prête , Donne - le à un WSGIRequestHandlerTraitement des instances.WSGIRequestHandlerC'est fait.HTTPFormat des données àWSGIConversion du format,Et la mise en œuvreapp(environ, start_response) ,Retour à la réponse.
  5. app(environ, start_response) Cette étape revient à flask Logique de traitement des demandes pour ,Selonenviron Avec des informations déjà liées url_map, Obtenir des informations et des paramètres de routage spécifiques (endpoint,view_func_args),Et de view_functions Extraire la correspondance dans le Dictionnaire view_functionExécuter et retourner les résultats.
原网站

版权声明
本文为[Ingénieur]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207070027415912.html