django起動プロセスソース解読

7754 ワード

前言
このノートは主にdjango起動プロセスのソースコードを調べています.私が使っているdjangoバージョンは2.1.8で、記録するときに一部のコードを削除してもっと理解しました.このノートを書くのは主に自分にdjangoをもっと理解させるためで、私の能力も限られていて、書く時に時間と精力を費やしましたが、理解上の不快感や間違いがあれば教えてください.
プロセスの開始
まずmanager.を見てみましょうpy
def main():

    #  settings          
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoStu.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    #     (sys.argv:      )
    execute_from_command_line(sys.argv)

manager.pyは2つのことをしました.1つ目はsettingモジュールが環境変数にロードされ、2つ目はコマンドを実行することです.execute_from_command_lineの方法でその役割を見てみましょうexecute_from_command_line
def execute_from_command_line(argv=None):
    """Run a ManagementUtility."""
    utility = ManagementUtility(argv)
    utility.execute()

まずManagementUtility(django-adminとmanage.pyユーティリティをカプセル化したクラス)がインスタンス化され、executeが実行されたことがコードからわかります.
ManagementUtilityのインスタンス化方法_init__.py
def __init__(self, argv=None):
    self.argv = argv or sys.argv[:]
    # self.prog_name:manager.py
    self.prog_name = os.path.basename(self.argv[0])
    #      __main__.py
    if self.prog_name == '__main__.py':
        self.prog_name = 'python -m django'
    self.settings_exception = None

次にexecuteメソッドが何をしたのかを見てみましょう
Excuteメソッド
executeメソッドが何をしているのか分かりやすくするために、一部のコードを削除して、具体的な自分を見てソースコードを見に行きましょう.
def execute(self):
    try:
        #   runserver  
        subcommand = self.argv[1]
    except IndexError:
        subcommand = 'help'  # Display help if no arguments were given.

    #        (--settings --pythonpath)
    parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)
    parser.add_argument('--settings')
    parser.add_argument('--pythonpath')
    parser.add_argument('args', nargs='*')  # catch-all
    try:
        options, args = parser.parse_known_args(self.argv[2:])
        #                 
        handle_default_options(options)
    except CommandError:
        pass
    if settings.configured:
        #  runserver    
        if subcommand == 'runserver' and '--noreload' not in self.argv:
            autoreload.check_errors(django.setup)()
        else:
            django.setup()
    self.autocomplete()
    # subcommand     
    if subcommand == 'help':
        pass
    elif subcommand == 'version' or self.argv[1:] == ['--version']:
        pass
    elif self.argv[1:] in (['--help'], ['-h']):
        pass
    else:
        self.fetch_command(subcommand).run_from_argv(self.argv)


上記のコードから分かるように、executeメソッドではまずself.argv[1]がrunserverであるか否かを処理し、そうでなければhelpを付与、後に処理を行う、処理コードは以下のように見られ、subcommandがhelpであるか否か、versionであるか否かを判断し、場合によって異なる情報を示す、いずれもそうでなければselfを歩く.fetch_command(subcommand).run_from_argv(self.argv).
判断argv[1]の論理は以下の通りである.
if subcommand == 'help':
   if '--commands' in args:
       sys.stdout.write(self.main_help_text(commands_only=True) + '
') elif not options.args: sys.stdout.write(self.main_help_text() + '
') else: self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0]) elif subcommand == 'version' or self.argv[1:] == ['--version']: sys.stdout.write(django.get_version() + '
') elif self.argv[1:] in (['--help'], ['-h']): sys.stdout.write(self.main_help_text() + '
') else: self.fetch_command(subcommand).run_from_argv(self.argv)

handle_default_options(デフォルト)
さらに下を見ると、--settingsと--pythonpathに対して前処理が行われています.これは問題があれば使用に影響し、後でデフォルト処理が行われるからです.
def handle_default_options(options):
    """
                       ,  ManagementUtility                 。
    """
    if options.settings:
        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    if options.pythonpath:
        sys.path.insert(0, options.pythonpath)

runserver処理
#  runserver    
if subcommand == 'runserver' and '--noreload' not in self.argv:
    autoreload.check_errors(django.setup)()
else:
    django.setup()

次にrunserverを処理し、上のexecuteソースコードに示すように、1つ目は自動的に再ロードされるルートであり、autoreload.check_errors(django.setup)()エージェントによって完了する(パケットを閉じることで異常をキャプチャした).もう1つのルートは、パラメータに--noreloadがある場合、django.setup()でサービスを開始します.
subcommand処理
最後にsubcommandの処理を開始し、helpまたはversionでなければself.fetch_command(subcommand).run_from_argv(self.argv)に進む.この方法は2つのステップに分けられ,1つはコマンドを実行するために必要なクラスを取得し,次にコマンドパラメータをパラメータとして実行関数に渡して実行する.
1-まずfetchを見てみましょうcommandコードは、理解しやすいように一部のコードを外します
def fetch_command(self, subcommand):
    """
              ,     ,              (   “ django-admin” “ manage.py”)      。
    """
    #                    。
    # {command_name:app_name}   :{'check': 'django.core', 'compilemessages': 'django.core',.....}
    commands = get_commands()
    try:
        app_name = commands[subcommand]
    except KeyError:
        #       
        sys.exit(1)
    if isinstance(app_name, BaseCommand):
        #       ,     
        klass = app_name
    else:
        # load_command_class:    
        klass = load_command_class(app_name, subcommand)
    return klass

fetch_commandメソッドでget_commandsは辞書{command_name:app_name}を返し、get_commandsの著者はpythonのfunctoolsを採用した.lru_Cacheはキャッシュを実装し、毎回再ロードする必要はありません.以下に示します.
@functools.lru_cache(maxsize=None)
def get_commands():
    """
                       。

    """
    commands = {name: 'django.core' for name in find_commands(__path__[0])}

    if not settings.configured:
        return commands

    for app_config in reversed(list(apps.get_app_configs())):
        path = os.path.join(app_config.path, 'management')
        commands.update({name: app_config.name for name in find_commands(path)})

    return commands

app_nameがロードされている場合はそのまま使用し、逆にload_を介してcommand_classはモジュールを動的にロードします.
def load_command_class(app_name, name):
    module = import_module('%s.management.commands.%s' % (app_name, name))
    return module.Command()

fetch_についてcommandのコードrun_を見てみましょうfrom_argv
2- run_from_Argvコード解析runserver命令を実行するモジュールは、django.contrib.staticfiles.management.commands.runserverがモジュールに定義されたCommandクラスを返す例である.インスタンスが取得されると、run_from_argv(self.argv)が呼び出されます.
def run_from_argv(self, argv):
    self._called_from_command_line = True
    parser = self.create_parser(argv[0], argv[1])
    # Namespace(addrport=None, ...)     Namespace   
    options = parser.parse_args(argv[2:]) 
    cmd_options = vars(options) #       
    #               optparse
    args = cmd_options.pop('args', ())
    #       
    handle_default_options(options)     
    try:
        #        execute
        self.execute(*args, **cmd_options) 
    except Exception as e:
        sys.exit(1)
    finally:
        connections.close_all()

 
おすすめ読書
pythonキャッシュメカニズムとfunctools.lru_cache