python manage.py runserver
위 커맨드를 입력하면 어떻게 작동할까?
입력한 Command Line을
list: sys.argv
를 이용하여'명령어'에 대응하는 모듈을 찾고,
모듈 내 class: Command 의 instance를 생성하고
해당 command instance의method: run_from_argv
를 실행시켜주는 과정이다.
python manage.py
를 통해
python으로 manage.py를 실행하게 된다.
이 때, 우리는 python manage.py 뒤에 'runserver'라는 argument를 붙여주게되는데,
이를 sys.argv
에 담아 활용하게 된다.
python manage.py runserver
sys.argv =['D:\\...\\manage.py', 'runserver']
# LAB: python_djang_practice/rest_api/REST_API/manage.py
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE", 'django_study.settings')
try:
from django.core.management import execute_from_command_line
except:
...
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
Command Line을 List로 변환한 sys.argv
를 execute_from_command_line
에 그대로 전달하여 진행한다.
sys.argv
를 통하여 Class: ManagementUtility
의 instance를 생성하고
내부의 method: execute
를 호출한다.
# LAB: .venv/Lib/site-packages/django/core.management/__init__.py
def execute_from_command_line(argv=None):
utility = ManamentUtility(argv)
utility.execute()
Class: ManagementUtility
는 django-admin과 manage.py를 통해 입력한 CommandLine의 실행을 담당한다.
# LAB: .venv.Lib/site-packages/django/core.management/__init__.py
class ManagementUtility:
"""
Encapsulate the logic of the django-admin and manage.py utilities.
"""
def __init__(self, argv=None)
self.argv = argv or sys.argv[:]
...
def execute(self):
"""
Given the command-line arguments, figure out which subcommand is being
run, create a parser appropriate to that command, and run it.
"""
try:
subcommand = self.argv[1]
except IndexError:
subcommand = "help"
parser = CommandParser(...)
...
try:
...
except CommandError:
pass # Ignore any option errors at this point.
...
else:
self.fetch_command(subcommand).run_from_argv(self.argv)
utility instance의 method: execute
는 self.argv[1]
인 ‘runserver’를 subcommand
에 할당하고,
Class: CommandParser
를 통해 유효성검사와, 초기화 작업을 해준다.(이 부분은 전체적 흐름을 위해 생략하겠습니다.)
이후 self.fetch_command(subcommand).run_from_argv(self.argv)
를 호출한다.
🎇
self.fetch_command
이 부분이 실질적으로subcommand
를 통하여,
알맞은 Class와 연결시켜주는 부분이다.
subcommand
에 할당된 'runserver'를 이용하여,
이에 해당하는 모듈을 찾아서 연결시켜주는 작업을 할 것이다.
# LAB: .venv.Lib/site-packages/django/core.management/__init__.py
class Managementutility:
def fetch_command(self, subcommand):
"""
Try to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually
"django-admin" or "manage.py") if it can't be found.
"""
commands = get_commands()
try:
app_name = commands[subcommand]
except KeyError:
...
if isinstance(app_name, BaseCommand):
klass = app_name
else:
klass = load_command_class(app_name, subcommand)
return klass
commands = get_commands()
를 통해 명령어와 모듈을 연결시킨 dict
을 받아온다.
명령어(subcommand)와 모듈 이름을 연결시켜주는 dict을 반환한다.
django.core의 management.commands 패키지와 설치된 app들을 뒤져서,
dict를 만들어주는 것.
def get_commands():
"""
Return a dictionary mapping command names to their callback applications.
Look for a management.commands package in django.core, and in each
installed application -- if a commands package exists, register all
commands in that package.
Core commands are always included. If a settings module has been
specified, also include user-defined commands.
The dictionary is in the format {command_name: app_name}. Key-value
pairs from this dictionary can then be used in calls to
load_command_class(app_name, command_name)
If a specific version of a command must be loaded (e.g., with the
startapp command), the instantiated module can be placed in the
dictionary in place of the application name.
The dictionary is cached on the first call and reused on subsequent
calls.
"""
commands = {name: "django.core" for name in find_commands(__path__[0])}
...
return commands
그런데, runserver에 대하여 처음에는 django.core
였는데
django.contrib.staticfiles
로 바뀌는 것을 볼 수 있다.
다시 돌아와서, subcommand
인 'runserver'를 통해
app_name
변수를 통해 연결시킬 모듈을 확정한 다음,
load_command_class
를 통해 연결시킬 모듈의 Class: Command
의 Instance를 생성한다.
# LAB: .venv.Lib/site-packages/django/core.management/__init__.py
class Managementutility:
def fetch_command(self, subcommand):
"""
Try to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually
"django-admin" or "manage.py") if it can't be found.
"""
commands = get_commands()
try:
app_name = commands[subcommand]
except KeyError:
...
else:
klass = load_command_class(app_name, subcommand)
return klass
klass = load_command_class(app_name, subcommand)
해당되는 package의 경로를 찾아
Class: Command Class
의 instance를 반환한다.
# django.core.management.__init__.py
def load_command_class(app_name, name):
"""
Given a command name and an application name, return the Command
class instance. Allow all errors raised by the import process
(ImportError, AttributeError) to propagate.
"""
module = import_module("%s.management.commands.%s" % (app_name, name))
return module.Command()
utility.fetch_command(subcommand)
에 의해 생성된 instance의
method: run_from_argv
를 호출함으로써,
'runserver'라는 명령어의 구체적인 기능을 python을 통해 수행할 수 있게된다.
# LAB: .venv.Lib/site-packages/django/core.management/__init__.py
class ManagementUtility:
"""
Encapsulate the logic of the django-admin and manage.py utilities.
"""
def __init__(self, argv=None)
self.argv = argv or sys.argv[:]
...
def execute(self):
"""
Given the command-line arguments, figure out which subcommand is being
run, create a parser appropriate to that command, and run it.
"""
try:
subcommand = self.argv[1]
except IndexError:
subcommand = "help"
parser = CommandParser(...)
...
try:
...
except CommandError:
pass # Ignore any option errors at this point.
...
else:
self.fetch_command(subcommand).run_from_argv(self.argv)
모듈을 찾아서, Command
instance의 생성까지 설명해보았다.
다음 글에서는, 이렇게 만들어진 ‘runserver’에 의한 Command
instance가
run_from_argv
를 통해서 어떻게 개발서버를 구성하고 작동시키는지에 대해
정리해보고자 한다.