Maison  >  Article  >  développement back-end  >  Comment le rechargement automatique est-il implémenté en mode développeur Django ?

Comment le rechargement automatique est-il implémenté en mode développeur Django ?

大家讲道理
大家讲道理original
2017-08-19 14:47:541987parcourir

Dans le processus de développement d'applications Django, il est particulièrement pratique d'utiliser le mode développeur pour démarrer les services. Vous n'avez besoin que de python manage.py runserver pour exécuter le service, et il fournit un mécanisme de rechargement automatique très convivial, qui n'est pas nécessaire. nécessite une opération manuelle. Redémarrez le programme pour modifier le code et voir les commentaires. Lorsque je l’ai découvert pour la première fois, je pensais que cette fonction était plus conviviale et je ne pensais pas qu’il s’agissait d’une technologie particulièrement avancée. Plus tard, quand j'avais du temps libre, j'ai réfléchi à ce que je ferais si je devais implémenter ce rechargement automatique. Après avoir longuement réfléchi, je n'arrivais pas à comprendre. Il y avait toujours des choses que je n'arrivais pas à comprendre. . Il semble que ma première réaction ait été que j’avais trop d’ambition et pas assez de compétences. J'ai donc passé du temps à étudier comment Django implémente le rechargement automatique, j'ai regardé le code source pour chaque étape et je n'ai rien laissé pour acquis :

1. Avant d'entrer dans le sujet, il y a en fait un long paragraphe d'absurdités sur la façon dont la commande runserver est exécutée. Cela n'a pas grand-chose à voir avec le sujet, je vais donc le mentionner brièvement :
Après avoir tapé python manage.py runserver sur le ligne de commande, Django va chercher la commande runserver. Le module d'exécution tombe finalement sur le module
djangocontribstaticfilesmanagementcommandsrunserver.py :


#django\contrib\staticfiles\management\commands\runserver.pyfrom django.core.management.commands.runserver import \
Command as RunserverCommandclass Command(RunserverCommand):
  help = "Starts a lightweight Web server for development and also serves static files."


.

La fonction d'exécution de cette commande est ici :


#django\core\management\commands\runserver.pyclass Command(BaseCommand):
  def run(self, **options):
  """  Runs the server, using the autoreloader if needed
  """  use_reloader = options['use_reloader']

  if use_reloader:
    autoreload.main(self.inner_run, None, options)
  else:
    self.inner_run(None, **options)


Voici les jugements sur use_reloader. Si nous n'ajoutons pas --noreload dans la commande de démarrage, le programme ira à la fonction autoreload.main Si nous l'ajoutons, il ira à self.inner_run et démarrera directement l'application.
En fait, il ressort des paramètres de autoreload.main qu'il devrait avoir une certaine encapsulation de self.inner_run. Le mécanisme de rechargement automatique est dans ces encapsulations.

PS : Quand j'ai regardé le code source, j'ai trouvé que le mode commande de Django est magnifiquement implémenté et mérite d'être appris.

2. module de rechargement automatique. Regardez autoreload.main() :


#django\utils\autoreload.py:def main(main_func, args=None, kwargs=None):
  if args is None:
    args = ()
  if kwargs is None:
    kwargs = {}
  if sys.platform.startswith('java'):
    reloader = jython_reloader
  else:
    reloader = python_reloader

  wrapped_main_func = check_errors(main_func)
  reloader(wrapped_main_func, args, kwargs)


Ici, nous faisons une distinction entre jpython et les autres pythons, ignorez d'abord jpython ; check_errors Ignorez simplement la gestion des erreurs de main_func. Regardez python_reloader :


#django\utils\autoreload.py:def python_reloader(main_func, args, kwargs):
  if os.environ.get("RUN_MAIN") == "true":
    thread.start_new_thread(main_func, args, kwargs)
    try:
      reloader_thread()
    except KeyboardInterrupt:
      pass  else:
    try:
      exit_code = restart_with_reloader()
      if exit_code < 0:
        os.kill(os.getpid(), -exit_code)
      else:
        sys.exit(exit_code)
    except KeyboardInterrupt:
      pass


Quand je suis arrivé ici pour la première fois, la variable RUN_MAIN dans la variable d'environnement n'était pas "true", ou même Non, alors allez ailleurs et regardez restart_with_reloader :


#django\utils\autoreload.py:def restart_with_reloader():    while True:
      args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] + sys.argv
    if sys.platform == "win32":
      args = ['"%s"' % arg for arg in args]
    new_environ = os.environ.copy()
    new_environ["RUN_MAIN"] = 'true'    exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
    if exit_code != 3:
      return exit_code


Ici, nous démarrons d'abord une boucle while et modifions en interne RUN_MAIN sur "true", puis utilisez la méthode os.spawnve pour ouvrir un sous-processus (sous-processus), jetez un œil aux instructions de os.spawnve :


   _spawnvef(mode, file, args, env, execve)


En fait, je viens d'ajuster à nouveau la ligne de commande et d'exécuter à nouveau python manage.py runserver.

Ensuite, regardez la boucle while dans restart_with_reloader. Il convient de noter que la seule condition pour que la boucle while se termine est exit_code!=3. Si le processus enfant ne se termine pas, il s'arrêtera à l'étape os.spawnve ; si le processus enfant se termine et que le code de sortie n'est pas 3, le while est terminé s'il est 3, la boucle continue et le processus enfant est terminé ; recréé. À partir de cette logique, nous pouvons deviner le mécanisme de rechargement automatique : le processus actuel (processus principal) ne fait rien, mais surveille l'état d'exécution du processus enfant. Le processus enfant est réel si le processus enfant se termine avec exit_code=3 ; (cela devrait être dû à une détection lorsque le fichier est modifié), redémarrez le sous-processus et le nouveau code prendra naturellement effet si le sous-processus se termine avec exit_code!=3, le processus principal se terminera également et ; tout le programme Django sera arrêté. Ceci n’est qu’une conjecture et sera vérifiée ci-dessous.

3. Processus enfant. Il y a en fait une question ci-dessus puisqu'il a été redémarré, pourquoi le processus enfant ne génère-t-il pas de processus enfants ? La raison en est que la variable d'environnement RUN_MAIN est remplacée par true dans le processus principal lorsque le processus enfant accède à la fonction python_reloader :


#django\utils\autoreload.py:def python_reloader(main_func, args, kwargs):
  if os.environ.get("RUN_MAIN") == "true":
    thread.start_new_thread(main_func, args, kwargs)
    try:
      reloader_thread()
    except KeyboardInterrupt:
      pass  else:
    try:
      exit_code = restart_with_reloader()
      if exit_code < 0:
        os.kill(os.getpid(), -exit_code)
      else:
        sys.exit(exit_code)
    except KeyboardInterrupt:
      pass


Si la condition est remplie, cela prendra une branche logique différente du processus principal. Ici, ouvrez d’abord un fil de discussion et exécutez main_func, qui est Command.inner_run ci-dessus. Le module thread ici est importé comme ceci :


#django\utils\autoreload.py:from django.utils.six.moves import _thread as thread


Le rôle du module six ici est d'être compatible avec divers python versions :


[codeblock six]#django\utils\six.pyclass _SixMetaPathImporter(object):"""A meta path importer to import six.moves and its submodules.

This class implements a PEP302 finder and loader. It should be compatible
with Python 2.5 and all existing versions of Python3"""官网说明:# https://pythonhosted.org/six/Six: Python 2 and 3 Compatibility Library
Six provides simple utilities for wrapping over differences between Python 2 and Python 3. It is intended to support codebases that work on both Python 2 and 3 without modification. six consists of only one Python file, so it is painless to copy into a project.


Donc, si le programme veut fonctionner à la fois sur python2 et python3, et sur Lupin, six est un outil important. Ensuite, prenez le temps d’en regarder six et de le noter.

Ensuite, ouvrez un reloader_thread :


=== change ==3)      change ==1)


ensure_echo_on() En fait, je ne l'ai pas encore compris, il semble que pour le traitement des fichiers système de type Unix, ignorez-le d'abord ;

USE_INOTIFY est également une variable liée aux opérations sur les fichiers système. Choisissez la méthode pour détecter les modifications de fichiers en fonction de la disponibilité ou non d'inotify.
En boucle While, vérifiez l'état du fichier toutes les 1 secondes. S'il y a un changement dans le fichier ordinaire, le processus se terminera avec un code de sortie de 3. Lorsque le processus principal verra que le code de sortie est 3, il redémarrera. le processus enfant. . . . Ceci est lié à ce qui précède ; s'il ne s'agit pas d'un changement de fichier ordinaire, mais de I18N_MODIFIED (changement de fichier avec le suffixe .mo, fichier de bibliothèque binaire, etc.), alors reset_translations, ce qui signifie probablement vider le cache de la bibliothèque chargée la prochaine fois. .

Ce qui précède est le processus du mécanisme de rechargement automatique. Il y a encore quelques détails qui ne sont pas particulièrement clairs, comme la détection des modifications de fichiers dans différents systèmes d'exploitation, mais ce sont des choses très détaillées et n'impliquent pas le processus principal. Après avoir lu ceci, je me suis demandé à nouveau : que ferais-je si on me demandait de concevoir le mécanisme de rechargement automatique ? Maintenant, ma réponse est : utilisez directement le fichier djangoutilsautoreload.py. En fait, il s'agit d'un module très indépendant et très polyvalent. Il peut être utilisé comme solution de rechargement automatique universelle. Je l'ai même écrit moi-même.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn