ホームページ  >  記事  >  バックエンド開発  >  Django 開発者モードでは自動リロードはどのように実装されますか?

Django 開発者モードでは自動リロードはどのように実装されますか?

大家讲道理
大家讲道理オリジナル
2017-08-19 14:47:541891ブラウズ

Django アプリケーションの開発プロセスでは、開発者モードを使用してサービスを開始するのが特に便利です。サービスを実行するために必要なのは python manage.py runserver だけです。これは、非常にユーザーフレンドリーな自動リロード メカニズムを提供します。プログラムを手動で再起動してコードを変更し、フィードバックを確認できます。初めて触れたときは、「もっと使いやすい機能だ」という印象で、特に高度な技術だとは思いませんでした。後で時間があるときに、このオートリロードを実装する場合はどうするかを考えましたが、ずっと考えてもわかりませんでした。私の最初の反応は、自分には野心がありすぎてスキルが不足しているということでした。そこで私は、Django が自動リロードを実装する方法を少し時間をかけて研究し、すべてのステップのソース コードを確認しました。

1. Runserver コマンド。本題に入る前に、runserver コマンドの実行方法については実際には意味のないことがたくさんありますが、本題とはほとんど関係がないので、簡単に説明します。
コマンド ラインで python manage.py runserver と入力した後、 Django は runserver コマンドの実行モジュールを探し、最終的に


#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."


モジュールにたどり着きます。そして、このコマンドの実行関数はここにあります:


#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)


use_reloaderに関する判定は以下の通りです。起動コマンドに --noreload を追加しない場合、プログラムは autoreload.main 関数に移動し、追加すると self.inner_run に移動し、アプリケーションを直接起動します。

実際、autoreload.main のパラメータから、autoreload のメカニズムがこれらのカプセル化に含まれていることがわかります。

追記: ソース コードを見たところ、Django のコマンド モードは依然として非常に美しく実装されており、学ぶ価値があることがわかりました。

2. 自動リロードモジュール。 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)


ここで、jpython と他の Python を区別します。最初に jpython を無視して main_func のエラーを処理し、それを最初に無視します。 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


初めてここに来たとき、環境変数の RUN_MAIN 変数は「true」ではなく、そこにもありませんでした。それでは、他の場所に行き、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
を見てください。


ここでは、まず while ループを開始し、内部で RUN_MAIN を "true" に変更してから、os.spawnve メソッドを使用してサブプロセス (サブプロセス) を開きます。

の説明を見てください。

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


実際には、コマンドラインを再度調整して、python manage.py runserver を再度実行するだけです。

次に、restart_with_reloader の while ループを見てください。while ループが終了するための唯一の条件は exit_code!=3 であることに注意してください。 子プロセスが終了しない場合は、os.spawnve ステップで停止します。子プロセスが終了し、終了コードが 3 でない場合は、while が終了し、ループが続行され、子プロセスは終了します。再作成されました。このロジックから、自動リロードのメカニズムを推測できます。現在のプロセス (メイン プロセス) は実際には何もしませんが、子プロセスが exit_code=3 で終了した場合は子プロセスの実行ステータスを監視します。 (ファイルが変更されたときの検出によるものです)、サブプロセスを再度開始すると、新しいコードが自然に有効になります。サブプロセスが exit_code!=3 で終了すると、メイン プロセスも終了します。 Django プログラム全体がダウンします。これは単なる推測ですので、以下で検証していきます。

3. 子プロセス。実は、再起動したのに、なぜ子プロセスが子プロセスを生成しないのかという疑問があります。その理由は、メインプロセスで RUN_MAIN 環境変数が true に変更されるためです。サブプロセスが python_reloader 関数に進むとき、条件が満たされると、ロジックがメインプロセスと異なります。プロセスの分岐。ここでは、まずスレッドを開き、上記の Command.inner_run である main_func を実行します。ここのスレッド モジュールは次のようにインポートされます:


#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


ここでの 6 つのモジュールの役割は、さまざまな Python バージョンと互換性があることです:


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


python2 と python3 で実行可能 ランニング、ルパン、シックスは重要なツールです。次に、時間をかけて 6 つを見て印を付けます。

次に reloader_thread を開きます:


[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.

ensure_echo_on() 実は、これは unix 風のシステムファイル処理のためのもののようですので、最初はスキップしてください。システムファイル操作に関連する変数 inotify が利用可能かどうかに基づいてファイル変更を検出する方法を選択します。

ループ中、1秒ごとにファイルのステータスをチェックします。通常のファイルに変更があった場合、プロセスは終了コード3で終了します。メインプロセスは終了コードが3であることを確認すると、子プロセスを再起動します。プロセス。 。 。 。これは、通常のファイル変更ではなく、I18N_MODIFIED (拡張子 .mo が付いているファイル変更、バイナリ ライブラリ ファイルなど) の場合は、reset_translations に関連します。これは、おそらく次回ロードされたライブラリ キャッシュをクリアすることを意味します。 。

上記はオートリロード機構のプロセスです。さまざまなオペレーティング システムでのファイル変更の検出など、特に明確ではない詳細がまだいくつかありますが、これらは非常に詳細なものであり、メインのプロセスには関与しません。これを読んだ後、私はオートリロード機構の設計を頼まれたらどうするだろうかと改めて自問しました。私の答えは、「djangoutilsautoreload.py ファイルを直接使用する」です。実際、これは非常に独立したモジュールであり、汎用の自動リロード ソリューションとして使用することもできます。

以上がDjango 開発者モードでは自動リロードはどのように実装されますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。