django起動プロセスソース解読
前言
このノートは主にdjango起動プロセスのソースコードを調べています.私が使っているdjangoバージョンは2.1.8で、記録するときに一部のコードを削除してもっと理解しました.このノートを書くのは主に自分にdjangoをもっと理解させるためで、私の能力も限られていて、書く時に時間と精力を費やしましたが、理解上の不快感や間違いがあれば教えてください.
プロセスの開始
まずmanager.を見てみましょうpy
manager.pyは2つのことをしました.1つ目はsettingモジュールが環境変数にロードされ、2つ目はコマンドを実行することです.
まずManagementUtility(django-adminとmanage.pyユーティリティをカプセル化したクラス)がインスタンス化され、executeが実行されたことがコードからわかります.
ManagementUtilityのインスタンス化方法_init__.py
次にexecuteメソッドが何をしたのかを見てみましょう
Excuteメソッド
executeメソッドが何をしているのか分かりやすくするために、一部のコードを削除して、具体的な自分を見てソースコードを見に行きましょう.
上記のコードから分かるように、executeメソッドではまずself.argv[1]がrunserverであるか否かを処理し、そうでなければhelpを付与、後に処理を行う、処理コードは以下のように見られ、subcommandが
判断argv[1]の論理は以下の通りである.
handle_default_options(デフォルト)
さらに下を見ると、--settingsと--pythonpathに対して前処理が行われています.これは問題があれば使用に影響し、後でデフォルト処理が行われるからです.
runserver処理
次にrunserverを処理し、上のexecuteソースコードに示すように、1つ目は自動的に再ロードされるルートであり、
subcommand処理
最後にsubcommandの処理を開始し、helpまたはversionでなければ
1-まずfetchを見てみましょうcommandコードは、理解しやすいように一部のコードを外します
fetch_commandメソッドでget_commandsは辞書{command_name:app_name}を返し、get_commandsの著者はpythonのfunctoolsを採用した.lru_Cacheはキャッシュを実装し、毎回再ロードする必要はありません.以下に示します.
app_nameがロードされている場合はそのまま使用し、逆にload_を介してcommand_classはモジュールを動的にロードします.
fetch_についてcommandのコードrun_を見てみましょうfrom_argv
2- run_from_Argvコード解析
おすすめ読書
pythonキャッシュメカニズムとfunctools.lru_cache
このノートは主に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