[Django] 원본 해석 Django 시작 및 접근 프로세스 (1)

Django 부팅 프로세스 (1)
전언
         ‘python manage.py runserver ip:port’    django  ,      manage.py      ,    django     。

manage.py
#       
os.makedirs('../logs')
#       
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
#      manage.py   ,      
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
  • manage.py runserver 매개 변수
  • optional arguments:
      -h, --help            show this help message and exit
      --ipv6, -6            Tells Django to use an IPv6 address. # IPV6,    
      --nothreading         Tells Django to NOT use threading. #    ,    
      --noreload            Tells Django to NOT use the auto-reloader. #     ,    
      --noasgi              Run the old WSGI-based runserver rather than the ASGI-based one
      --http_timeout HTTP_TIMEOUT
                            Specify the daphne http_timeout interval in seconds (default: no timeout)
      --websocket_handshake_timeout WEBSOCKET_HANDSHAKE_TIMEOUT
                            Specify the daphne websocket_handshake_timeout interval in seconds (default: 5)
      --version             show program's version number and exit
      -v {0,1,2,3}, --verbosity {0,1,2,3}
                            Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output
      --settings SETTINGS   The Python path to a settings module, e.g. "myproject.settings.main". If this isn't provided, the DJANGO_SETTINGS_MODULE environment variable will be used.
      --pythonpath PYTHONPATH
                            A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".
      --traceback           Raise on CommandError exceptions
      --no-color            Don't colorize the command output.
      --force-color         Force colorization of the command output.
    

    위 코드에서 알 수 있듯이 시작 파라미터는'django.'에 건네졌다.core.management.init.execute_from_command_라인 처리.
    django.core.management.init.execute_from_command_line
    def execute_from_command_line(argv=None):
        """Run a ManagementUtility."""
        #    ManagementUtility 
        utility = ManagementUtility(argv)
        #          , [‘manage.py’, 'runserver','0.0.0.0:8000',...]  ,      
        utility.execute()
    

    위에서 알 수 있듯이 접근 매개 변수는'django'에 계속 전달되었다.core.management.init.ManagementUtility 인스턴스의 "execute"메서드를 처리합니다.
    django.core.management.init.ManagementUtility.execute
    #        
    ...
    #       ,runserver
    subcommand = self.argv[1]
    ...
    #             , --settings --pythonpath
    options, args = parser.parse_known_args(self.argv[2:])
    #       
    handle_default_options(options)
    ....
    #          INSTALLED_APPS,         
    try:
        settings.INSTALLED_APPS
    except ImproperlyConfigured as exc:
        self.settings_exception = exc
    except ImportError as exc:
        self.settings_exception = exc
    
    #         
    if settings.configured:
        # Start the auto-reloading dev server even if the code is broken.
        # The hardcoded condition is a code smell but we can't rely on a
        # flag on the command class because we haven't located it yet.
        # 
        if subcommand == 'runserver' and '--noreload' not in self.argv:
            try:
            	#         --noreload,          
                autoreload.check_errors(django.setup)()
            except Exception:
                ...
        # In all other cases, django.setup() is required to succeed.
        else:
            django.setup()    
    ...
    # self.fetch_command(subcommand)  get_command argv[1]         
    #   ‘if isinstance(app_name, BaseCommand):klass = app_name;’    BaseCommand    
    #     BaseCommand     run_from_argv  ,     django.contrib.staticfiles.management.commands.runserver.Command
    self.fetch_command(subcommand).run_from_argv(self.argv)        
    

    위 코드에서'fetch 'command'통과'django.core.management.init.ManagementUtility.get_commands () '는 실행하는 구체적인 명령을 해석한 다음 명령을 통과하는runfrom_argv 시작 서비스.
    django.core.management.init.ManagementUtility.get_commands()
    #      ,    get_commands  
    @functools.lru_cache(maxsize=None)
    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.
        """
        # find_commands       commands         
        #      django.core.management.commands           
        commands = {
         name: 'django.core' for name in find_commands(__path__[0])}
    	
    	#          ,   commands
        if not settings.configured:
            return commands
    	#         INSTALLED_APPS,       APP   management.commands           
    	#   App         ,      
    	#   'django.contrib.staticfiles' management.commands      runserver.py  
    	#       django.core.management.commands  runserver.py  
        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
    

    위 코드에서 알 수 있듯이 django는 먼저'django'로 간다.core.management.commands는 명령 모듈을 가져와 등록된 app 아래의 관리를 통해 가져옵니다.commands 디렉터리 아래에서 명령 모듈을 가져오고 앞에서 가져온 같은 이름의 모듈을 덮어씁니다.실제로runserver가 실행하는 것은 등록된staticfiles라는 응용 프로그램 아래의 runserer 모듈입니다. 다음은 이 모듈을 해석합니다.
    django.contrib.staticfiles.management.commands.runserver.py
    from django.conf import settings
    from django.contrib.staticfiles.handlers import StaticFilesHandler
    from django.core.management.commands.runserver import (
        Command as RunserverCommand,
    )
    
    
    class Command(RunserverCommand):
        help = "Starts a lightweight Web server for development and also serves static files."
    
        def add_arguments(self, parser):
            super().add_arguments(parser)
            parser.add_argument(
                '--nostatic', action="store_false", dest='use_static_handler',
                help='Tells Django to NOT automatically serve static files at STATIC_URL.',
            )
            parser.add_argument(
                '--insecure', action="store_true", dest='insecure_serving',
                help='Allows serving static files even if DEBUG is False.',
            )
    
        def get_handler(self, *args, **options):
            """
            Return the static files serving handler wrapping the default handler,
            if static files should be served. Otherwise return the default handler.
            """
            handler = super().get_handler(*args, **options)
            use_static_handler = options['use_static_handler']
            insecure_serving = options['insecure_serving']
            if use_static_handler and (settings.DEBUG or insecure_serving):
                return StaticFilesHandler(handler)
            return handler
    

    이 명령은'django'에서 상속됩니다.core.management.commands.runserver.Command', run 없음from_argv 방법, 계속 아래로 해석.
    django.core.management.commands.runserver.Command
    from django.core.management.base import BaseCommand, CommandError
    
    class Command(BaseCommand):
        help = "Starts a lightweight Web server for development."
    
        # Validation is called explicitly each time the server is reloaded.
        requires_system_checks = False
        stealth_options = ('shutdown_message',)
    
        default_addr = '127.0.0.1'
        default_addr_ipv6 = '::1'
        default_port = '8000'
        protocol = 'http'
        server_cls = WSGIServer
    
        def add_arguments(self, parser):
    		#       django.contrib.staticfiles.management.commands.runserver.Command.add_arguments
    		...
    
        def execute(self, *args, **options):
            if options['no_color']:
                # We rely on the environment because it's currently the only
                # way to reach WSGIRequestHandler. This seems an acceptable
                # compromise considering `runserver` runs indefinitely.
                os.environ["DJANGO_COLORS"] = "nocolor"
            super().execute(*args, **options)
    
        def get_handler(self, *args, **options):
            #       django.contrib.staticfiles.management.commands.runserver.Command.get_handler
            ...
    
        def handle(self, *args, **options):
            if not settings.DEBUG and not settings.ALLOWED_HOSTS:
                raise CommandError('You must set settings.ALLOWED_HOSTS if DEBUG is False.')
    
            self.use_ipv6 = options['use_ipv6']
            if self.use_ipv6 and not socket.has_ipv6:
                raise CommandError('Your Python does not support IPv6.')
            self._raw_ipv6 = False
            if not options['addrport']:
                self.addr = ''
                self.port = self.default_port
            else:
                m = re.match(naiveip_re, options['addrport'])
                if m is None:
                    raise CommandError('"%s" is not a valid port number '
                                       'or address:port pair.' % options['addrport'])
                self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups()
                if not self.port.isdigit():
                    raise CommandError("%r is not a valid port number." % self.port)
                if self.addr:
                    if _ipv6:
                        self.addr = self.addr[1:-1]
                        self.use_ipv6 = True
                        self._raw_ipv6 = True
                    elif self.use_ipv6 and not _fqdn:
                        raise CommandError('"%s" is not a valid IPv6 address.' % self.addr)
            if not self.addr:
                self.addr = self.default_addr_ipv6 if self.use_ipv6 else self.default_addr
                self._raw_ipv6 = self.use_ipv6
            self.run(**options)
    
        def run(self, **options):
            """Run the server, using the autoreloader if needed."""
            use_reloader = options['use_reloader']
    
            if use_reloader:
                autoreload.run_with_reloader(self.inner_run, **options)
            else:
                self.inner_run(None, **options)
    
        def inner_run(self, *args, **options):
            # If an exception was silenced in ManagementUtility.execute in order
            # to be raised in the child process, raise it now.
            autoreload.raise_last_exception()
    
            threading = options['use_threading']
            # 'shutdown_message' is a stealth option.
            shutdown_message = options.get('shutdown_message', '')
            quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'
    
            self.stdout.write("Performing system checks...

    "
    ) self.check(display_num_errors=True) # Need to check migrations here, so can't use the # requires_migrations_check attribute. self.check_migrations() now = datetime.now().strftime('%B %d, %Y - %X') self.stdout.write(now) self.stdout.write(( "Django version %(version)s, using settings %(settings)r
    "
    "Starting development server at %(protocol)s://%(addr)s:%(port)s/
    "
    "Quit the server with %(quit_command)s.
    "
    ) % { "version": self.get_version(), "settings": settings.SETTINGS_MODULE, "protocol": self.protocol, "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr, "port": self.port, "quit_command": quit_command, }) try: handler = self.get_handler(*args, **options) run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) except socket.error as e: # Use helpful error messages instead of ugly tracebacks. ERRORS = { errno.EACCES: "You don't have permission to access that port.", errno.EADDRINUSE: "That port is already in use.", errno.EADDRNOTAVAIL: "That IP address can't be assigned to.", } try: error_text = ERRORS[e.errno] except KeyError: error_text = e self.stderr.write("Error: %s" % error_text) # Need to use an OS exit because sys.exit doesn't work in a thread os._exit(1) except KeyboardInterrupt: if shutdown_message: self.stdout.write(shutdown_message) sys.exit(0)

    django.contrib.staticfiles.management.commands.runserver는django에서 계승합니다.core.management.commands.runserver django.core.management.commands.runserver는django에서 계승합니다.core.management.base.BaseCommand 아래에서 runfrom_argv 방법은 남은 시작 과정을 계속 분석합니다
    django.core.management.base.BaseCommand.run_from_argv
    ...
    #           ,      
    #     django.core.management.commands.runserver.Command.execute,         ‘DJANGO_COLORS’      
    #        execute
    self.execute(*args, **cmd_options)
    #              
    connections.close_all()
    

    'django'에 부팅 매개 변수가 전달되었습니다.core.management.base.BaseCommand.execute '가 계속 처리됩니다.
    django.core.management.base.BaseCommand.execute
    ...
    #       
    #     django.core.management.commands.runserver.Command.requires_system_checks=False,     
    if self.requires_system_checks and not options.get('skip_checks'):
        self.check()
    #        ,         
    if self.requires_migrations_checks:
        self.check_migrations()
    #     ,    django.core.management.commands.runserver.Command.handle        
    output = self.handle(*args, **options)
    ...
    

    'django'에 부팅 매개 변수가 전달되었습니다.core.management.commands.runserver.Command.handle'계속 처리.다음 편, 우리는'django'에서core.management.commands.runserver.Command.handle'django의 시작 원본 계속 분석

    좋은 웹페이지 즐겨찾기