• django启动过程剖析


    在manage.py文件中

     1 if __name__ == '__main__':
     2     os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test11.settings')
     3     try:
     4         from django.core.management import execute_from_command_line
     5     except ImportError as exc:
     6         raise ImportError(
     7             "Couldn't import Django. Are you sure it's installed and "
     8             "available on your PYTHONPATH environment variable? Did you "
     9             "forget to activate a virtual environment?"
    10         ) from exc
    11     execute_from_command_line(sys.argv) #在这里获取命令行参数

    在execute_from_command_line函数中实现一系列操作:

    def execute_from_command_line(argv=None):
        """Run a ManagementUtility."""
        utility = ManagementUtility(argv) #实例化ManagementUtility对象
        utility.execute()

    在ManagementUtility对象的execute方法中:

     1     def execute(self):
     2         """
     3         Given the command-line arguments, figure out which subcommand is being
     4         run, create a parser appropriate to that command, and run it.
     5         """
     6         try:
     7             subcommand = self.argv[1] #这里获取第一个参数(命令)如runserver
     8         except IndexError:
     9             subcommand = 'help'  # Display help if no arguments were given.
    10 
    11         # Preprocess options to extract --settings and --pythonpath.
    12         # These options could affect the commands that are available, so they
    13         # must be processed early.
    14         parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)
    15         parser.add_argument('--settings')
    16         parser.add_argument('--pythonpath')
    17         parser.add_argument('args', nargs='*')  # catch-all
    18         try:
    19             options, args = parser.parse_known_args(self.argv[2:])  #这里把后面的参数装入options中如['127.0.0.1:8000']
    20             handle_default_options(options)
    21         except CommandError:
    22             pass  # Ignore any option errors at this point.
    23 
    24         try:
    25             settings.INSTALLED_APPS
    26         except ImproperlyConfigured as exc:
    27             self.settings_exception = exc
    28         except ImportError as exc:
    29             self.settings_exception = exc
    30 
    31         if settings.configured:
    32             # Start the auto-reloading dev server even if the code is broken.
    33             # The hardcoded condition is a code smell but we can't rely on a
    34             # flag on the command class because we haven't located it yet.
    35             if subcommand == 'runserver' and '--noreload' not in self.argv:
    36                 try:
    37                     autoreload.check_errors(django.setup)()
    38                 except Exception:
    39                     # The exception will be raised later in the child process
    40                     # started by the autoreloader. Pretend it didn't happen by
    41                     # loading an empty list of applications.
    42                     apps.all_models = defaultdict(OrderedDict)
    43                     apps.app_configs = OrderedDict()
    44                     apps.apps_ready = apps.models_ready = apps.ready = True
    45 
    46                     # Remove options not compatible with the built-in runserver
    47                     # (e.g. options for the contrib.staticfiles' runserver).
    48                     # Changes here require manually testing as described in
    49                     # #27522.
    50                     _parser = self.fetch_command('runserver').create_parser('django', 'runserver')
    51                     _options, _args = _parser.parse_known_args(self.argv[2:])
    52                     for _arg in _args:
    53                         self.argv.remove(_arg)
    54 
    55             # In all other cases, django.setup() is required to succeed.
    56             else:
    57                 django.setup() #加载APP
    58 
    59         self.autocomplete()
    60 
    61         if subcommand == 'help':
    62             if '--commands' in args:
    63                 sys.stdout.write(self.main_help_text(commands_only=True) + '
    ')
    64             elif not options.args:
    65                 sys.stdout.write(self.main_help_text() + '
    ')
    66             else:
    67                 self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
    68         # Special-cases: We want 'django-admin --version' and
    69         # 'django-admin --help' to work, for backwards compatibility.
    70         elif subcommand == 'version' or self.argv[1:] == ['--version']:
    71             sys.stdout.write(django.get_version() + '
    ')
    72         elif self.argv[1:] in (['--help'], ['-h']):
    73             sys.stdout.write(self.main_help_text() + '
    ')
    74         else:
    75             self.fetch_command(subcommand).run_from_argv(self.argv)

    在django.setup中调用apps.populate方法:

     1     def populate(self, installed_apps=None):
     2         """
     3         Load application configurations and models.
     4 
     5         Import each application module and then each model module.
     6 
     7         It is thread-safe and idempotent, but not reentrant.
     8         """
     9         if self.ready:
    10             return
    11 
    12         # populate() might be called by two threads in parallel on servers
    13         # that create threads before initializing the WSGI callable.
    14         with self._lock:
    15             if self.ready:
    16                 return
    17 
    18             # An RLock prevents other threads from entering this section. The
    19             # compare and set operation below is atomic.
    20             if self.loading:
    21                 # Prevent reentrant calls to avoid running AppConfig.ready()
    22                 # methods twice.
    23                 raise RuntimeError("populate() isn't reentrant")
    24             self.loading = True
    25 
    26             # Phase 1: initialize app configs and import app modules.
    27             for entry in installed_apps:
    28                 if isinstance(entry, AppConfig):
    29                     app_config = entry
    30                 else:
    31                     app_config = AppConfig.create(entry) #创建AppConfig对象
    32                 if app_config.label in self.app_configs:
    33                     raise ImproperlyConfigured(
    34                         "Application labels aren't unique, "
    35                         "duplicates: %s" % app_config.label)
    36 
    37                 self.app_configs[app_config.label] = app_config
    38                 app_config.apps = self
    39 
    40             # Check for duplicate app names.
    41             counts = Counter(
    42                 app_config.name for app_config in self.app_configs.values()) #判断是否有重复的AppConfig对象
    43             duplicates = [
    44                 name for name, count in counts.most_common() if count > 1]
    45             if duplicates:
    46                 raise ImproperlyConfigured(
    47                     "Application names aren't unique, "
    48                     "duplicates: %s" % ", ".join(duplicates))
    49 
    50             self.apps_ready = True
    51 
    52             # Phase 2: import models modules.
    53             for app_config in self.app_configs.values(): #导入models
    54                 app_config.import_models()
    55 
    56             self.clear_cache()
    57 
    58             self.models_ready = True
    59 
    60             # Phase 3: run ready() methods of app configs.
    61             for app_config in self.get_app_configs(): #注册AppConfig对象
    62                 app_config.ready()
    63 
    64             self.ready = True

     在autocomplete中:

     1     def autocomplete(self):
     2         """
     3         Output completion suggestions for BASH.
     4 
     5         The output of this function is passed to BASH's `COMREPLY` variable and
     6         treated as completion suggestions. `COMREPLY` expects a space
     7         separated string as the result.
     8 
     9         The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
    10         to get information about the cli input. Please refer to the BASH
    11         man-page for more information about this variables.
    12 
    13         Subcommand options are saved as pairs. A pair consists of
    14         the long option string (e.g. '--exclude') and a boolean
    15         value indicating if the option requires arguments. When printing to
    16         stdout, an equal sign is appended to options which require arguments.
    17 
    18         Note: If debugging this function, it is recommended to write the debug
    19         output in a separate file. Otherwise the debug output will be treated
    20         and formatted as potential completion suggestions.
    21         """
    22         # Don't complete if user hasn't sourced bash_completion file.
    23         if 'DJANGO_AUTO_COMPLETE' not in os.environ:
    24             return
    25 
    26         cwords = os.environ['COMP_WORDS'].split()[1:]
    27         cword = int(os.environ['COMP_CWORD'])
    28 
    29         try:
    30             curr = cwords[cword - 1]
    31         except IndexError:
    32             curr = ''
    33 
    34         subcommands = list(get_commands()) + ['help']
    35         options = [('--help', False)]
    36 
    37         # subcommand
    38         if cword == 1:
    39             print(' '.join(sorted(filter(lambda x: x.startswith(curr), subcommands))))
    40         # subcommand options
    41         # special case: the 'help' subcommand has no options
    42         elif cwords[0] in subcommands and cwords[0] != 'help':
    43             subcommand_cls = self.fetch_command(cwords[0]) #在这里获取命令模块
    44             # special case: add the names of installed apps to options
    45             if cwords[0] in ('dumpdata', 'sqlmigrate', 'sqlsequencereset', 'test'):
    46                 try:
    47                     app_configs = apps.get_app_configs()#获取所有AppConfig对象
    48                     # Get the last part of the dotted path as the app name.
    49                     options.extend((app_config.label, 0) for app_config in app_configs)#把所有AppConfig对象加载到options中
    50                 except ImportError:
    51                     # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
    52                     # user will find out once they execute the command.
    53                     pass
    54             parser = subcommand_cls.create_parser('', cwords[0]) #创建命令行解析器
    55             options.extend(
    56                 (min(s_opt.option_strings), s_opt.nargs != 0)
    57                 for s_opt in parser._actions if s_opt.option_strings
    58             )
    59             # filter out previously specified options from available options
    60             prev_opts = {x.split('=')[0] for x in cwords[1:cword - 1]}
    61             options = (opt for opt in options if opt[0] not in prev_opts)
    62 
    63             # filter options by current input
    64             options = sorted((k, v) for k, v in options if k.startswith(curr))
    65             for opt_label, require_arg in options:
    66                 # append '=' to options which require args
    67                 if require_arg:
    68                     opt_label += '='
    69                 print(opt_label)
    70         # Exit code of the bash completion function is never passed back to
    71         # the user, so it's safe to always exit with 0.
    72         # For more details see #25420.
    73         sys.exit(0)

    在run_from_argv中

     1     def run_from_argv(self, argv):
     2         """
     3         Set up any environment changes requested (e.g., Python path
     4         and Django settings), then run this command. If the
     5         command raises a ``CommandError``, intercept it and print it sensibly
     6         to stderr. If the ``--traceback`` option is present or the raised
     7         ``Exception`` is not ``CommandError``, raise it.
     8         """
     9         self._called_from_command_line = True
    10         parser = self.create_parser(argv[0], argv[1])  #创建命令行解析器
    11 
    12         options = parser.parse_args(argv[2:]) #获取命令行后续参数[2:]
    13         cmd_options = vars(options)
    14         # Move positional args out of options to mimic legacy optparse
    15         args = cmd_options.pop('args', ())
    16         handle_default_options(options)
    17         try:
    18             self.execute(*args, **cmd_options) #调用BaseCommand的execute方法
    19         except Exception as e:
    20             if options.traceback or not isinstance(e, CommandError):
    21                 raise
    22 
    23             # SystemCheckError takes care of its own formatting.
    24             if isinstance(e, SystemCheckError):
    25                 self.stderr.write(str(e), lambda x: x)
    26             else:
    27                 self.stderr.write('%s: %s' % (e.__class__.__name__, e))
    28             sys.exit(1)
    29         finally:
    30             try:
    31                 connections.close_all()
    32             except ImproperlyConfigured:
    33                 # Ignore if connections aren't setup at this point (e.g. no
    34                 # configured settings).
    35                 pass

    在BaseCommand的execute方法

     1     def execute(self, *args, **options):
     2         """
     3         Try to execute this command, performing system checks if needed (as
     4         controlled by the ``requires_system_checks`` attribute, except if
     5         force-skipped).
     6         """
     7         if options['no_color']:  #做一些系统检查
     8             self.style = no_style()
     9             self.stderr.style_func = None
    10         if options.get('stdout'):
    11             self.stdout = OutputWrapper(options['stdout'])
    12         if options.get('stderr'):
    13             self.stderr = OutputWrapper(options['stderr'], self.stderr.style_func)
    14 
    15         if self.requires_system_checks and not options.get('skip_checks'):
    16             self.check()
    17         if self.requires_migrations_checks:
    18             self.check_migrations()
    19         output = self.handle(*args, **options) #在这里需要调用django.core.management.commands.runserver.Command.handle方法
    20         if output:
    21             if self.output_transaction:
    22                 connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
    23                 output = '%s
    %s
    %s' % (
    24                     self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
    25                     output,
    26                     self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
    27                 )
    28             self.stdout.write(output)
    29         return output

    在django.core.management.commands.runserver.Command.handle方法中

     1     def handle(self, *args, **options):
     2         if not settings.DEBUG and not settings.ALLOWED_HOSTS:
     3             raise CommandError('You must set settings.ALLOWED_HOSTS if DEBUG is False.')
     4 
     5         self.use_ipv6 = options['use_ipv6']
     6         if self.use_ipv6 and not socket.has_ipv6:
     7             raise CommandError('Your Python does not support IPv6.')
     8         self._raw_ipv6 = False
     9         if not options['addrport']:
    10             self.addr = ''
    11             self.port = self.default_port
    12         else:
    13             m = re.match(naiveip_re, options['addrport'])
    14             if m is None:
    15                 raise CommandError('"%s" is not a valid port number '
    16                                    'or address:port pair.' % options['addrport'])
    17             self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups()
    18             if not self.port.isdigit():
    19                 raise CommandError("%r is not a valid port number." % self.port)
    20             if self.addr:
    21                 if _ipv6:
    22                     self.addr = self.addr[1:-1]
    23                     self.use_ipv6 = True
    24                     self._raw_ipv6 = True
    25                 elif self.use_ipv6 and not _fqdn:
    26                     raise CommandError('"%s" is not a valid IPv6 address.' % self.addr)
    27         if not self.addr:
    28             self.addr = self.default_addr_ipv6 if self.use_ipv6 else self.default_addr
    29             self._raw_ipv6 = self.use_ipv6
    30         self.run(**options)

    在django.core.management.commands.runserver.Command.run方法中

    1     def run(self, **options):
    2         """Run the server, using the autoreloader if needed."""
    3         use_reloader = options['use_reloader']
    4 
    5         if use_reloader:
    6             autoreload.main(self.inner_run, None, options)  #这里会进入main函数
    7         else:
    8             self.inner_run(None, **options)

    这里会进入autoreload.main方法中调用python_reloader函数

     1 def python_reloader(main_func, args, kwargs):   #在这里环境里面的RUN_MAIN默认是false的所以会先进入restart_with_reloader,在里面把环境设置为true,再执行subprocess.call函数重新运行命令行参数
     2     if os.environ.get("RUN_MAIN") == "true":  
     3         _thread.start_new_thread(main_func, args, kwargs)
     4         try:
     5             reloader_thread()
     6         except KeyboardInterrupt:
     7             pass
     8     else:
     9         try:
    10             exit_code = restart_with_reloader()
    11             if exit_code < 0:
    12                 os.kill(os.getpid(), -exit_code)
    13             else:
    14                 sys.exit(exit_code)
    15         except KeyboardInterrupt:
    16             pass

    在django.core.management.commands.runserver.Command.inner_run方法中

     1     def inner_run(self, *args, **options):
     2         # If an exception was silenced in ManagementUtility.execute in order
     3         # to be raised in the child process, raise it now.
     4         autoreload.raise_last_exception()
     5 
     6         threading = options['use_threading']
     7         # 'shutdown_message' is a stealth option.
     8         shutdown_message = options.get('shutdown_message', '')
     9         quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'
    10 
    11         self.stdout.write("Performing system checks...
    
    ")
    12         self.check(display_num_errors=True)
    13         # Need to check migrations here, so can't use the
    14         # requires_migrations_check attribute.
    15         self.check_migrations()  #检查数据迁移
    16         now = datetime.now().strftime('%B %d, %Y - %X')
    17         self.stdout.write(now)
    18         self.stdout.write((
    19             "Django version %(version)s, using settings %(settings)r
    "
    20             "Starting development server at %(protocol)s://%(addr)s:%(port)s/
    "
    21             "Quit the server with %(quit_command)s.
    "
    22         ) % {
    23             "version": self.get_version(),
    24             "settings": settings.SETTINGS_MODULE,
    25             "protocol": self.protocol,
    26             "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
    27             "port": self.port,
    28             "quit_command": quit_command,
    29         })
    30 
    31         try:
    32             handler = self.get_handler(*args, **options) #在这个方法里调用get_internal_wsgi_application方法
    33             run(self.addr, int(self.port), handler,
    34                 ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
    35         except socket.error as e:
    36             # Use helpful error messages instead of ugly tracebacks.
    37             ERRORS = {
    38                 errno.EACCES: "You don't have permission to access that port.",
    39                 errno.EADDRINUSE: "That port is already in use.",
    40                 errno.EADDRNOTAVAIL: "That IP address can't be assigned to.",
    41             }
    42             try:
    43                 error_text = ERRORS[e.errno]
    44             except KeyError:
    45                 error_text = e
    46             self.stderr.write("Error: %s" % error_text)
    47             # Need to use an OS exit because sys.exit doesn't work in a thread
    48             os._exit(1)
    49         except KeyboardInterrupt:
    50             if shutdown_message:
    51                 self.stdout.write(shutdown_message)
    52             sys.exit(0)

    在get_internal_wsgi_application中

     1 def get_internal_wsgi_application():
     2     """
     3     Load and return the WSGI application as configured by the user in
     4     ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
     5     this will be the ``application`` object in ``projectname/wsgi.py``.
     6 
     7     This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
     8     for Django's internal server (runserver); external WSGI servers should just
     9     be configured to point to the correct application object directly.
    10 
    11     If settings.WSGI_APPLICATION is not set (is ``None``), return
    12     whatever ``django.core.wsgi.get_wsgi_application`` returns.
    13     """
    14     from django.conf import settings
    15     app_path = getattr(settings, 'WSGI_APPLICATION') #返回settings里面设置的路径
    16     if app_path is None:
    17         return get_wsgi_application()
    18 
    19     try:
    20         return import_string(app_path)  #获取WSGI对象
    21     except ImportError as err:
    22         raise ImproperlyConfigured(
    23             "WSGI application '%s' could not be loaded; "
    24             "Error importing module." % app_path
    25         ) from err

    在run函数中启动django web

     1 def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
     2     server_address = (addr, port)
     3     if threading:
     4         httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
     5     else:
     6         httpd_cls = server_cls
     7     httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
     8     if threading:
     9         # ThreadingMixIn.daemon_threads indicates how threads will behave on an
    10         # abrupt shutdown; like quitting the server by the user or restarting
    11         # by the auto-reloader. True means the server will not wait for thread
    12         # termination before it quits. This will make auto-reloader faster
    13         # and will prevent the need to kill the server manually if a thread
    14         # isn't terminating correctly.
    15         httpd.daemon_threads = True
    16     httpd.set_app(wsgi_handler)
    17     httpd.serve_forever()   #相当于socket的recv方法,等待客户端的连接
  • 相关阅读:
    《ASP.NET1200例》实现投票的用户控件
    《转》这些年这些感悟
    《转》不要过打折的生活,当你发现这些你有了,说明你开始成熟了
    HTML控件ID和NAME属性及在CS页面获得.ASPX页面中HTML控件的值
    逻辑回归(1)
    MySQL笔记5-----索引(覆盖索引等)
    MySQL笔记4------面试问题
    MySQL-----笔记3:存储引擎
    Python可视化数据------seaborn
    树(2)-----leetcode(层、深度、节点)
  • 原文地址:https://www.cnblogs.com/arrow-kejin/p/10359468.html
Copyright © 2020-2023  润新知