Django源代码-启动、接收、应用中间件链、路由匹配、完成请求
btikc 2024-09-16 13:03:08 技术文章 24 ℃ 0 评论
- if __name__ == "__main__":
- # 设置环境变量 export DJANGO_SETTINGS_MODULE="HelloWorld.settings"
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DebugApp.settings")
- try:
- from django.core.management import execute_from_command_line
- except ImportError:
- # The above import may fail for some other reason. Ensure that the
- # issue is really that Django is missing to avoid masking other
- # exceptions on Python 2.
- try:
- import django
- except ImportError:
- 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?"
- )
- raise
- execute_from_command_line(sys.argv)
- {
- utility = ManagementUtility(argv)
- {
- self.argv = argv or sys.argv[:]
- self.prog_name = os.path.basename(self.argv[0])
- self.settings_exception = None
- }
-
- utility.execute()
- {
- try:
- subcommand = self.argv[1]
- except IndexError:
- subcommand = 'help' # Display help if no arguments were given.
-
- # Preprocess options to extract --settings and --pythonpath.
- # These options could affect the commands that are available, so they
- # must be processed early.
- parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=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 # Ignore any option errors at this point.
-
- try:
- # 安装的 app 列表
- settings.INSTALLED_APPS
- except ImproperlyConfigured 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:
- # 包装 django.setup 并执行
- autoreload.check_errors(django.setup)()
- except Exception:
- # The exception will be raised later in the child process
- # started by the autoreloader. Pretend it didn't happen by
- # loading an empty list of applications.
- apps.all_models = defaultdict(OrderedDict)
- apps.app_configs = OrderedDict()
- apps.apps_ready = apps.models_ready = apps.ready = True
-
- # Remove options not compatible with the built-in runserver
- # (e.g. options for the contrib.staticfiles' runserver).
- # Changes here require manually testing as described in
- # #27522.
- _parser = self.fetch_command('runserver').create_parser('django', 'runserver')
- _options, _args = _parser.parse_known_args(self.argv[2:])
- for _arg in _args:
- self.argv.remove(_arg)
-
- # In all other cases, django.setup() is required to succeed.
- else:
- django.setup()
- {
- # 安装应用
- from django.apps import apps
- from django.conf import settings
- from django.urls import set_script_prefix
- from django.utils.encoding import force_text
- from django.utils.log import configure_logging
-
- # 配置日志
- configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
- {
- if logging_config:
- # First find the logging configuration function ...
- logging_config_func = import_string(logging_config)
-
- logging.config.dictConfig(DEFAULT_LOGGING)
-
- # ... then invoke it with the logging settings
- if logging_settings:
- logging_config_func(logging_settings)
- }
-
- # 设置脚本前缀
- if set_prefix:
- set_script_prefix(
- '/' if settings.FORCE_SCRIPT_NAME is None else force_text(settings.FORCE_SCRIPT_NAME)
- )
- {
- if not prefix.endswith('/'):
- prefix += '/'
- _prefixes.value = prefix
- }
-
- # 填充被安装的应用 -------------------- settings.INSTALLED_APPS ------------- 1 !!!
- apps.populate(settings.INSTALLED_APPS)
- {
- # installed_apps === settings.INSTALLED_APPS
-
- if self.ready:
- return
-
- # populate() might be called by two threads in parallel on servers
- # that create threads before initializing the WSGI callable.
- with self._lock:
- if self.ready:
- return
-
- # app_config should be pristine, otherwise the code below won't
- # guarantee that the order matches the order in INSTALLED_APPS.
- if self.app_configs:
- raise RuntimeError("populate() isn't reentrant")
-
- # 阶段 1: 初始化 app 配置 和 导入 app 模块
- # Phase 1: initialize app configs and import app modules.
- for entry in installed_apps:
- # 被安装的应用,如:entry === django.contrib.sessions
- if isinstance(entry, AppConfig):
- app_config = entry
- else:
- # 创建 app_config 对象
- app_config = AppConfig.create(entry)
- {
- try:
- # 导入模块,如:django.contrib.sessions
- # 文件:/Library/Python/2.7/site-packages/django/contrib/sessions/__init__.py ------module------- 1.1
- # If import_module succeeds, entry is a path to an app module,
- # which may specify an app config class with default_app_config.
- # Otherwise, entry is a path to an app config class or an error.
- module = import_module(entry)
-
- except ImportError:
- # Track that importing as an app module failed. If importing as an
- # app config class fails too, we'll trigger the ImportError again.
- module = None
-
- mod_path, _, cls_name = entry.rpartition('.')
-
- # Raise the original exception when entry cannot be a path to an
- # app config class.
- if not mod_path:
- raise
-
- else:
- try:
- # 模块默认app配置
- # 文件 /Library/Python/2.7/site-packages/django/contrib/sessions/__init__.py 中 default_app_config ------------- 1.3
- # 如:default_app_config = 'django.contrib.sessions.apps.SessionsConfig'
- # If this works, the app module specifies an app config class.
- entry = module.default_app_config
- except AttributeError:
- # Otherwise, it simply uses the default app config class.
- return cls(entry, module)
- else:
- # 模块路径,类名
- # 如:entry === django.contrib.sessions.apps.SessionsConfig ------------- 1.5
- mod_path, _, cls_name = entry.rpartition('.')
-
- # 导入默认的app模块
- # mod_path === django.contrib.sessions.apps
- # cls_name === SessionsConfig
- # If we're reaching this point, we must attempt to load the app config
- # class located at <mod_path>.<cls_name>
- # 导入文件 /Library/Python/2.7/site-packages/django/contrib/sessions/apps.py 中 ------ mod ------- 1.7
- mod = import_module(mod_path)
- try:
- # 获取类
- # 文件 /Library/Python/2.7/site-packages/django/contrib/sessions/apps.py 中 SessionsConfig ------------- 1.9
- cls = getattr(mod, cls_name)
- except AttributeError:
- if module is None:
- # If importing as an app module failed, that error probably
- # contains the most informative traceback. Trigger it again.
- import_module(entry)
- else:
- raise
-
- # 类必须是 AppConfig 的子类
- # Check for obvious errors. (This check prevents duck typing, but
- # it could be removed if it became a problem in practice.)
- if not issubclass(cls, AppConfig):
- raise ImproperlyConfigured(
- "'%s' isn't a subclass of AppConfig." % entry)
-
- # Obtain app name here rather than in AppClass.__init__ to keep
- # all error checking for entries in INSTALLED_APPS in one place.
- try:
- # 类的 app 名
- # 如:app_name = 'django.contrib.sessions' ------------- 1.11
- app_name = cls.name
- except AttributeError:
- raise ImproperlyConfigured(
- "'%s' must supply a name attribute." % entry)
-
- # Ensure app_name points to a valid module.
- try:
- # 导入 app
- # 如:app_name = 'django.contrib.sessions' ------ app_module ------- 1.13
- app_module = import_module(app_name)
- except ImportError:
- raise ImproperlyConfigured(
- "Cannot import '%s'. Check that '%s.%s.name' is correct." % (
- app_name, mod_path, cls_name,
- )
- )
- # 实例化类
- # !!! cls === django.contrib.sessions.apps.SessionsConfig
- # !!! 如:app_name ==== 'django.contrib.sessions'
- # !!! 如:app_module ==== django.contrib.sessions
- # Entry is a path to an app config class.
- return cls(app_name, app_module)
- {
- def __init__(self, app_name, app_module):
-
- # !!! 如:app_name ==== 'django.contrib.sessions'
- # Full Python path to the application eg. 'django.contrib.admin'.
- self.name = app_name
-
- # !!! 如:app_module ==== django.contrib.sessions
- # Root module for the application eg. <module 'django.contrib.admin'
- # from 'django/contrib/admin/__init__.pyc'>.
- self.module = app_module
-
- # Reference to the Apps registry that holds this AppConfig. Set by the
- # registry when it registers the AppConfig instance.
- self.apps = None
-
- # The following attributes could be defined at the class level in a
- # subclass, hence the test-and-set pattern.
-
- # !!! 如:app_name ==== 'django.contrib.sessions'
- # 如:self.label === sessions
- # Last component of the Python path to the application eg. 'admin'.
- # This value must be unique across a Django project.
- if not hasattr(self, 'label'):
- self.label = app_name.rpartition(".")[2]
-
- # 转成驼峰
- # Human-readable name for the application eg. "Admin".
- if not hasattr(self, 'verbose_name'):
- self.verbose_name = self.label.title()
-
- # 模块路径
- # Filesystem path to the application directory eg.
- # u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. Unicode on
- # Python 2 and a str on Python 3.
- if not hasattr(self, 'path'):
- self.path = self._path_from_module(app_module)
-
- # Module containing models eg. <module 'django.contrib.admin.models'
- # from 'django/contrib/admin/models.pyc'>. Set by import_models().
- # None if the application doesn't have a models module.
- self.models_module = None
-
- # Mapping of lower case model names to model classes. Initially set to
- # None to prevent accidental access before import_models() runs.
- self.models = None
- }
- }
-
- # 如:app_config === django.contrib.sessions.apps.SessionsConfig
- # 获取 app_config 对象的属性
- if app_config.label in self.app_configs:
- raise ImproperlyConfigured(
- "Application labels aren't unique, "
- "duplicates: %s" % app_config.label)
-
- # 保存 app_config 对象到 map 结构
- self.app_configs[app_config.label] = app_config
- # !!! 设置 app_config 对 django.apps.registry.Apps 的依赖 !!!
- app_config.apps = self
-
- # !!!!!!!!!!!! 至此,迭代完所有的 settings.INSTALLED_APPS !!!!!!!!!!!!
-
- # 所有应用的配置
- # Check for duplicate app names.
- counts = Counter(
- app_config.name for app_config in self.app_configs.values())
- duplicates = [
- name for name, count in counts.most_common() if count > 1]
- if duplicates: # 重复
- raise ImproperlyConfigured(
- "Application names aren't unique, "
- "duplicates: %s" % ", ".join(duplicates))
-
- self.apps_ready = True
-
- # 阶段2:迭代 app_config,导入 models 模块 ------------------ app_configs ------------------- 1.15
- # Phase 2: import models modules.
- for app_config in self.app_configs.values():
- # 如:app_config === django.contrib.sessions.apps.SessionsConfig
- app_config.import_models()
- {
- # 如:self.apps === django.apps.registry.Apps
- self.models = self.apps.all_models[self.label]
-
- # !!! 导入子模块
- # 如:self.module === django.contrib.sessions
- if module_has_submodule(self.module, MODELS_MODULE_NAME):
- # MODELS_MODULE_NAME === models
- # 如:django.contrib.sessions.models
- models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME)
- # 导入 django.contrib.sessions.models 模块
- self.models_module = import_module(models_module_name)
- }
-
- self.clear_cache()
-
- self.models_ready = True
-
- # 阶段 3: 迭代 app_config,调用 app_config 的 ready() 方法
- # Phase 3: run ready() methods of app configs.
- for app_config in self.get_app_configs():
- app_config.ready()
-
- self.ready = True
- }
-
- }
-
- self.autocomplete()
- {
- # Don't complete if user hasn't sourced bash_completion file.
- if 'DJANGO_AUTO_COMPLETE' not in os.environ:
- return
-
- ......
- }
-
- if subcommand == 'help':
- if '--commands' in args:
- sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
- elif len(options.args) < 1:
- sys.stdout.write(self.main_help_text() + '\n')
- else:
- self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
- # Special-cases: We want 'django-admin --version' and
- # 'django-admin --help' to work, for backwards compatibility.
- elif subcommand == 'version' or self.argv[1:] == ['--version']:
- sys.stdout.write(django.get_version() + '\n')
- elif self.argv[1:] in (['--help'], ['-h']):
- sys.stdout.write(self.main_help_text() + '\n')
- else:
- #
- self.fetch_command(subcommand){
- # 获取 commands 列表
- # Get commands outside of try block to prevent swallowing exceptions
- commands = get_commands()
- {
- commands = {name: 'django.core' for name in find_commands(upath(__path__[0])){
- # 如:management_dir === upath(__path__[0])
- # 如:management_dir === /Library/Python/2.7/site-packages/django/core/management
- # 如:command_dir === /Library/Python/2.7/site-packages/django/core/management/commands
- command_dir = os.path.join(management_dir, 'commands')
- return [name for _, name, is_pkg in pkgutil.iter_modules([npath(command_dir)])
- if not is_pkg and not name.startswith('_')]
- }}
-
- if not settings.configured:
- return commands
-
- # 迭代 app_config 列表中的 commands
- for app_config in reversed(list(apps.get_app_configs())):
- path = os.path.join(app_config.path, 'management')
- # 查找 app_config 中的 Command 对象
- commands.update({name: app_config.name for name in find_commands(path)})
-
- return commands
- }
-
- try:
- # 命中 Command 对象
- # commands === {
- # 'check': 'django.core',
- # 'compilemessages': 'django.core',
- # 'runserver': 'django.core'
- # ...
- # }
- app_name = commands[subcommand]
- except KeyError:
- if os.environ.get('DJANGO_SETTINGS_MODULE'):
- # If `subcommand` is missing due to misconfigured settings, the
- # following line will retrigger an ImproperlyConfigured exception
- # (get_commands() swallows the original one) so the user is
- # informed about it.
- settings.INSTALLED_APPS
- else:
- sys.stderr.write("No Django settings specified.\n")
- sys.stderr.write(
- "Unknown command: %r\nType '%s help' for usage.\n"
- % (subcommand, self.prog_name)
- )
- sys.exit(1)
-
- if isinstance(app_name, BaseCommand):
- # If the command is already loaded, use it directly.
- klass = app_name
- else:
- # 实例化 Command 对象
- # 如: app_name === 'django.core',subcommand === 'runserver'
- klass = load_command_class(app_name, subcommand)
- {
- # 如: module === django.core.management.commands.runserver
- # 导入模块 /Library/Python/2.7/site-packages/django/core/management/commands/runserver.py
- module = import_module('%s.management.commands.%s' % (app_name, name))
-
- # 实例化 Command 对象
- # 如:module.Command() === django.core.management.commands.runserver.Command()
- return module.Command()
- }
-
- # 如: klass === django.core.management.commands.runserver.Command()
- return klass
- }
- .run_from_argv(self.argv) # 根据参数运行
- {
- # !!!! 执行 django.core.management.commands.runserver.Command().run_from_argv(self.argv)
-
- self._called_from_command_line = True
-
- # 创建参数解析器
- parser = self.create_parser(argv[0], argv[1])
- {
- parser = CommandParser(
- self, prog="%s %s" % (os.path.basename(prog_name), subcommand),
- description=self.help or None,
- )
- parser.add_argument('--version', action='version', version=self.get_version())
- parser.add_argument(
- '-v', '--verbosity', action='store', dest='verbosity', default=1,
- type=int, choices=[0, 1, 2, 3],
- help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output',
- )
- parser.add_argument(
- '--settings',
- help=(
- '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.'
- ),
- )
- parser.add_argument(
- '--pythonpath',
- help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".',
- )
- parser.add_argument('--traceback', action='store_true', help='Raise on CommandError exceptions')
- parser.add_argument(
- '--no-color', action='store_true', dest='no_color', default=False,
- help="Don't colorize the command output.",
- )
- self.add_arguments(parser)
- return parser
- }
-
- # 解析参数
- options = parser.parse_args(argv[2:])
- cmd_options = vars(options)
- # Move positional args out of options to mimic legacy optparse
- args = cmd_options.pop('args', ())
-
- # 处理默认选项
- handle_default_options(options)
- {
- if options.settings:
- os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
- if options.pythonpath:
- sys.path.insert(0, options.pythonpath)
- }
-
- # !!!执行 !!!
- try:
- self.execute(*args, **cmd_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[str("DJANGO_COLORS")] = str("nocolor")
-
- # 调用父类 execute - begain
- super(Command, self).execute(*args, **options)
- {
- if options['no_color']:
- self.style = no_style()
- self.stderr.style_func = None
- if options.get('stdout'):
- self.stdout = OutputWrapper(options['stdout'])
- if options.get('stderr'):
- self.stderr = OutputWrapper(options['stderr'], self.stderr.style_func)
-
- saved_locale = None
- if not self.leave_locale_alone:
- # Deactivate translations, because django-admin creates database
- # content like permissions, and those shouldn't contain any
- # translations.
- from django.utils import translation
- saved_locale = translation.get_language()
- translation.deactivate_all()
-
- try:
- if self.requires_system_checks and not options.get('skip_checks'):
- self.check()
- if self.requires_migrations_checks:
- self.check_migrations()
-
- # !!! ------ 调用子类的 handle 方法 -------
- output = self.handle(*args, **options)
- {
- from django.conf import settings
-
- 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 = '::1' if self.use_ipv6 else '127.0.0.1'
- self._raw_ipv6 = self.use_ipv6
-
- # !!! ------ 调用 run() 方法 ------------
- self.run(**options)
- {
- # 是否使用自动加载器
- use_reloader = options['use_reloader']
-
- if use_reloader:
- # 包装 self.inner_run 函数
- autoreload.main(self.inner_run, None, options)
- {
- if args is None:
- args = ()
- if kwargs is None:
- kwargs = {}
- if sys.platform.startswith('java'):
- reloader = jython_reloader
- else:
- reloader = python_reloader
-
- wrapped_main_func = check_errors(main_func)
- {
- def wrapper(*args, **kwargs):
- global _exception
- try:
- # 执行 inner_run 函数
- fn(*args, **kwargs)
- except Exception:
- _exception = sys.exc_info()
-
- et, ev, tb = _exception
-
- if getattr(ev, 'filename', None) is None:
- # get the filename from the last item in the stack
- filename = traceback.extract_tb(tb)[-1][0]
- else:
- filename = ev.filename
-
- if filename not in _error_files:
- _error_files.append(filename)
-
- raise
-
- return wrapper
- }
-
- # 当 reloader = python_reloader
- reloader(wrapped_main_func, args, kwargs)
- {
- # main_func === wrapped_main_func
- if os.environ.get("RUN_MAIN") == "true":
- # !!!执行 wrapped_main_func 函数
- thread.start_new_thread(main_func, args, kwargs)
- # !!!启动监控,监控代码是否改变
- try:
- reloader_thread()
- {
- ensure_echo_on()
- if USE_INOTIFY:
- fn = inotify_code_changed
- else:
- fn = code_changed
- while RUN_RELOADER:
- # 文件改变
- change = fn()
- if change == FILE_MODIFIED:
- sys.exit(3) # force reload 强制重新启动,退出码=3
- elif change == I18N_MODIFIED:
- reset_translations()
- time.sleep(1)
- }
- except KeyboardInterrupt:
- pass
- else:
- try:
- exit_code = restart_with_reloader()
- {
- while True: # 无限循环
- args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] + sys.argv
- new_environ = os.environ.copy()
- if _win and six.PY2:
- # Environment variables on Python 2 + Windows must be str.
- encoding = get_system_encoding()
- for key in new_environ.keys():
- str_key = key.decode(encoding).encode('utf-8')
- str_value = new_environ[key].decode(encoding).encode('utf-8')
- del new_environ[key]
- new_environ[str_key] = str_value
- # 设置环境
- new_environ["RUN_MAIN"] = 'true'
- # 启动子进程,并等待 exit_code
- exit_code = subprocess.call(args, env=new_environ)
- if exit_code != 3: # 退出码!=3
- return exit_code
- }
- if exit_code < 0:
- os.kill(os.getpid(), -exit_code)
- else:
- sys.exit(exit_code)
- except KeyboardInterrupt:
- pass
- }
- }
- else:
- # ------------ !!! 运行 !!! ------------
- self.inner_run(None, **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...\n\n")
- 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')
- if six.PY2:
- now = now.decode(get_system_encoding())
- self.stdout.write(now)
- self.stdout.write((
- "Django version %(version)s, using settings %(settings)r\n"
- "Starting development server at %(protocol)s://%(addr)s:%(port)s/\n"
- "Quit the server with %(quit_command)s.\n"
- ) % {
- "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 === django.core.handlers.wsgi.WSGIHandler
- handler = self.get_handler(*args, **options)
- {
- return get_internal_wsgi_application()
- {
- from django.conf import settings
- app_path = getattr(settings, 'WSGI_APPLICATION')
- if app_path is None:
- return get_wsgi_application()
- {
- # 安装
- django.setup(set_prefix=False)
- {
- from django.apps import apps
- from django.conf import settings
- from django.urls import set_script_prefix
- from django.utils.encoding import force_text
- from django.utils.log import configure_logging
- # 日志
- configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
- if set_prefix:
- set_script_prefix(
- '/' if settings.FORCE_SCRIPT_NAME is None else force_text(settings.FORCE_SCRIPT_NAME)
- )
- # 填充 settings.INSTALLED_APPS
- apps.populate(settings.INSTALLED_APPS)
- }
-
- # 创建 django.core.handlers.wsgi.WSGIHandler 对象
- return WSGIHandler(){
- def __init__(self, *args, **kwargs):
- super(WSGIHandler, self).__init__(*args, **kwargs)
- {
- self._request_middleware = None
- self._view_middleware = None
- self._template_response_middleware = None
- self._response_middleware = None
- self._exception_middleware = None
- self._middleware_chain = None
- }
-
- # ------ 加载 中间件
- self.load_middleware()
- {
- self._request_middleware = []
- self._view_middleware = []
- self._template_response_middleware = []
- self._response_middleware = []
- self._exception_middleware = []
-
- # -------------------- 中间件 settings.MIDDLEWARE ------------- 1 !!!
- if settings.MIDDLEWARE is None:
- warnings.warn(
- "Old-style middleware using settings.MIDDLEWARE_CLASSES is "
- "deprecated. Update your middleware and use settings.MIDDLEWARE "
- "instead.", RemovedInDjango20Warning
- )
- handler = convert_exception_to_response(self._legacy_get_response)
-
- for middleware_path in settings.MIDDLEWARE_CLASSES:
- mw_class = import_string(middleware_path)
- try:
- mw_instance = mw_class()
- except MiddlewareNotUsed as exc:
- if settings.DEBUG:
- if six.text_type(exc):
- logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
- else:
- logger.debug('MiddlewareNotUsed: %r', middleware_path)
- continue
-
- if hasattr(mw_instance, 'process_request'):
- self._request_middleware.append(mw_instance.process_request)
- if hasattr(mw_instance, 'process_view'):
- self._view_middleware.append(mw_instance.process_view)
- if hasattr(mw_instance, 'process_template_response'):
- self._template_response_middleware.insert(0, mw_instance.process_template_response)
- if hasattr(mw_instance, 'process_response'):
- self._response_middleware.insert(0, mw_instance.process_response)
- if hasattr(mw_instance, 'process_exception'):
- self._exception_middleware.insert(0, mw_instance.process_exception)
- else:
- # !!! convert_exception_to_response 的用途是:包装一层 _get_response 函数,并添加捕获 _get_response 异常的代码
- handler = convert_exception_to_response(self._get_response)
- {
- # def convert_exception_to_response(get_response):
- @wraps(get_response, assigned=available_attrs(get_response))
- def inner(request):
- try:
- # get_response === self._get_response
- response = get_response(request)
- {
- # def _get_response(self, request):
- response = None
-
- # !!! 路由解析器
- if hasattr(request, 'urlconf'):
- urlconf = request.urlconf
- set_urlconf(urlconf)
- resolver = get_resolver(urlconf)
- else:
- resolver = get_resolver()
- {
- @lru_cache.lru_cache(maxsize=None)
- def get_resolver(urlconf=None):
- if urlconf is None:
- from django.conf import settings
- # ------------------------------------------------------------------- !!!获取 settings.ROOT_URLCONF 信息
- urlconf = settings.ROOT_URLCONF
- # urlconf === 'DebugApp.urls'
- return RegexURLResolver(r'^/', urlconf)
- {
- # RegexURLResolver 构造函数
- def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):
- LocaleRegexProvider.__init__(self, regex)
- {
- # LocaleRegexProvider 构造函数
- def __init__(self, regex):
- # regex is either a string representing a regular expression, or a
- # translatable string (using ugettext_lazy) representing a regular
- # expression.
- self._regex = regex
- self._regex_dict = {}
- regex = LocaleRegexDescriptor()
- }
- # urlconf_name is the dotted Python path to the module defining
- # urlpatterns. It may also be an object with an urlpatterns attribute
- # or urlpatterns itself.
- self.urlconf_name = urlconf_name
- self.callback = None
- self.default_kwargs = default_kwargs or {}
- self.namespace = namespace
- self.app_name = app_name
- self._reverse_dict = {}
- self._namespace_dict = {}
- self._app_dict = {}
- # set of dotted paths to all functions and classes that are used in
- # urlpatterns
- self._callback_strs = set()
- self._populated = False
- self._local = threading.local()
- }
- }
-
- # !!! ------------------------ 匹配出路由 ------------------------ !!!
- resolver_match = resolver.resolve(request.path_info)
- {
- path = force_text(path) # path may be a reverse_lazy object
- tried = []
- # 匹配 '/'
- match = self.regex.search(path)
- if match:
- new_path = path[match.end():]
- # self.url_patterns = {
- # @cached_property
- # def url_patterns(self):
- # # urlconf_module might be a valid set of patterns, so we default to it
- # # 导入模块
- # # self.urlconf_module = {
- # # @cached_property
- # # def urlconf_module(self):
- # # if isinstance(self.urlconf_name, six.string_types):
- # # return import_module(self.urlconf_name)
- # # else:
- # # return self.urlconf_name
- # # }
- # patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
- # try:
- # iter(patterns)
- # except TypeError:
- # msg = (
- # "The included URLconf '{name}' does not appear to have any "
- # "patterns in it. If you see valid patterns in the file then "
- # "the issue is probably caused by a circular import."
- # )
- # raise ImproperlyConfigured(msg.format(name=self.urlconf_name))
- # return patterns
- # }
- # 迭代所以配置的路由,进行匹配
- for pattern in self.url_patterns:
- try:
- # 匹配到路由
- sub_match = pattern.resolve(new_path)
- except Resolver404 as e:
- sub_tried = e.args[0].get('tried')
- if sub_tried is not None:
- tried.extend([pattern] + t for t in sub_tried)
- else:
- tried.append([pattern])
- else:
- if sub_match:
- # Merge captured arguments in match with submatch
- sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
- sub_match_dict.update(sub_match.kwargs)
-
- # If there are *any* named groups, ignore all non-named groups.
- # Otherwise, pass all non-named arguments as positional arguments.
- sub_match_args = sub_match.args
- if not sub_match_dict:
- sub_match_args = match.groups() + sub_match.args
-
- return ResolverMatch(
- sub_match.func, # 回调函数
- sub_match_args, # 参数
- sub_match_dict,
- sub_match.url_name,
- [self.app_name] + sub_match.app_names,
- [self.namespace] + sub_match.namespaces,
- )
- tried.append([pattern])
- raise Resolver404({'tried': tried, 'path': new_path})
- raise Resolver404({'path': path})
- }
- # callback = 要调用的函数
- # callback_args = 函数参数
- # callback_kwargs = 函数的动态参数
- callback, callback_args, callback_kwargs = resolver_match
- request.resolver_match = resolver_match
-
- # Apply view middleware
- # !!! 应用 _view_middleware 类型的中间件
- for middleware_method in self._view_middleware:
- response = middleware_method(request, callback, callback_args, callback_kwargs)
- if response:
- break
-
- if response is None:
- # ------------------------------ wrapped_callback === 用户定义的 action
- wrapped_callback = self.make_view_atomic(callback)
- try:
- # ------------------------------ 执行用户定义的 action
- response = wrapped_callback(request, *callback_args, **callback_kwargs)
- except Exception as e:
- response = self.process_exception_by_middleware(e, request)
-
- # Complain if the view returned None (a common error).
- if response is None:
- if isinstance(callback, types.FunctionType): # FBV
- view_name = callback.__name__
- else: # CBV
- view_name = callback.__class__.__name__ + '.__call__'
-
- raise ValueError(
- "The view %s.%s didn't return an HttpResponse object. It "
- "returned None instead." % (callback.__module__, view_name)
- )
-
- # 对象 response 有 render 方法,那么进行调用 render
- # If the response supports deferred rendering, apply template
- # response middleware and then render the response
- elif hasattr(response, 'render') and callable(response.render):
- # 应用 _template_response_middleware 类型的中间件
- for middleware_method in self._template_response_middleware:
- response = middleware_method(request, response)
- # Complain if the template response middleware returned None (a common error).
- if response is None:
- raise ValueError(
- "%s.process_template_response didn't return an "
- "HttpResponse object. It returned None instead."
- % (middleware_method.__self__.__class__.__name__)
- )
-
- try:
- # 调用 render 方法
- response = response.render()
- except Exception as e:
- response = self.process_exception_by_middleware(e, request)
-
- return response
- }
- except Exception as exc:
- response = response_for_exception(request, exc)
- return response
- return inner # ------------
- }
- # handler = inner(request){ ... }
-
- # -------------------- 中间件列表 settings.MIDDLEWARE ------------- 1 !!!
- # 反转 中间件列表,并包装 handler
- for middleware_path in reversed(settings.MIDDLEWARE):
- middleware = import_string(middleware_path)
- try:
- # 用 middleware 包裹 handler
- mw_instance = middleware(handler)
- except MiddlewareNotUsed as exc:
- if settings.DEBUG:
- if six.text_type(exc):
- logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
- else:
- logger.debug('MiddlewareNotUsed: %r', middleware_path)
- continue
-
- if mw_instance is None:
- raise ImproperlyConfigured(
- 'Middleware factory %s returned None.' % middleware_path
- )
-
- if hasattr(mw_instance, 'process_view'):
- # process_view 中间件
- self._view_middleware.insert(0, mw_instance.process_view)
- if hasattr(mw_instance, 'process_template_response'):
- # process_template_response 中间件
- self._template_response_middleware.append(mw_instance.process_template_response)
- if hasattr(mw_instance, 'process_exception'):
- # process_exception 中间件
- self._exception_middleware.append(mw_instance.process_exception)
- # !!! convert_exception_to_response 的用途是:包装一层 mw_instance 函数,并添加捕获 mw_instance 异常的代码
- handler = convert_exception_to_response(mw_instance)
-
- # -------------------- middleware 链条
- # We only assign to this when initialization is complete as it is used
- # as a flag for initialization being complete.
- self._middleware_chain = handler
- }
- }
- }
-
- try:
- return import_string(app_path)
- except ImportError as e:
- msg = (
- "WSGI application '%(app_path)s' could not be loaded; "
- "Error importing module: '%(exception)s'" % ({
- 'app_path': app_path,
- 'exception': e,
- })
- )
- six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
- sys.exc_info()[2])
-
- }
- }
-
- # handler === django.core.handlers.wsgi.WSGIHandler
- # 执行
- # default_port = '8000'
- # protocol = 'http'
- # server_cls = django.core.servers.basehttp.WSGIServer
- run(self.addr, int(self.port), handler,ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
- {
- server_address = (addr, port)
- if threading:
- # 多线程的支持
- # 让 socketserver.ThreadingMixIn 继承,并重新 server_cls 内的方法
- httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, server_cls), {})
- else:
- # 单线程
- # httpd_cls = django.core.servers.basehttp.WSGIServer
- httpd_cls = server_cls
-
- httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
- {
- def __init__(self, *args, **kwargs):
- if kwargs.pop('ipv6', False):
- self.address_family = socket.AF_INET6
- self.allow_reuse_address = kwargs.pop('allow_reuse_address', True)
- super(WSGIServer, self).__init__(*args, **kwargs)
- {
- # TCPServer
- def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
- BaseServer.__init__(self, server_address, RequestHandlerClass)
- {
- def __init__(self, server_address, RequestHandlerClass):
- self.server_address = server_address
- # ------- RequestHandlerClass ------- 处理类
- self.RequestHandlerClass = RequestHandlerClass
- self.__is_shut_down = threading.Event()
- self.__shutdown_request = False
- }
- # 创建 socket
- self.socket = socket.socket(self.address_family, self.socket_type)
- if bind_and_activate:
- try:
- self.server_bind()
- {
- # WSGIServer
- def server_bind(self):
- HTTPServer.server_bind(self)
- {
- # HTTPServer
- def server_bind(self):
- # TCPServer
- SocketServer.TCPServer.server_bind(self)
- {
- def server_bind(self):
- if self.allow_reuse_address:
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- # 设置监听地址
- self.socket.bind(self.server_address)
- self.server_address = self.socket.getsockname()
- }
- host, port = self.socket.getsockname()[:2]
- self.server_name = socket.getfqdn(host)
- self.server_port = port
- }
- # WSGIServer
- self.setup_environ()
- {
- def setup_environ(self):
- # Set up base environment
- env = self.base_environ = {}
- env['SERVER_NAME'] = self.server_name
- env['GATEWAY_INTERFACE'] = 'CGI/1.1'
- env['SERVER_PORT'] = str(self.server_port)
- env['REMOTE_HOST']=''
- env['CONTENT_LENGTH']=''
- env['SCRIPT_NAME'] = ''
- }
- }
- self.server_activate()
- {
- def server_activate(self):
- # 设置监听队列
- self.socket.listen(self.request_queue_size)
- }
- except:
- self.server_close()
- raise
-
- }
- }
- if threading:
- # ThreadingMixIn.daemon_threads indicates how threads will behave on an
- # abrupt shutdown; like quitting the server by the user or restarting
- # by the auto-reloader. True means the server will not wait for thread
- # termination before it quits. This will make auto-reloader faster
- # and will prevent the need to kill the server manually if a thread
- # isn't terminating correctly.
- httpd.daemon_threads = True
-
- # 设置 wsgi_handler 处理器
- # 如:httpd = django.core.servers.basehttp.WSGIServer
- # 如:wsgi_handler == django.core.handlers.wsgi.WSGIHandler
- httpd.set_app(wsgi_handler)
- {
- def set_app(self,application):
- # 如:application == django.core.handlers.wsgi.WSGIHandler
- self.application = application
- }
-
- # 执行监听
- # 如:httpd = django.core.servers.basehttp.WSGIServer
- httpd.serve_forever()
- {
- def serve_forever(self, poll_interval=0.5):
- self.__is_shut_down.clear()
- try:
- # 死循环
- while not self.__shutdown_request:
- # 通过 select.select 获取连接句柄
- # XXX: Consider using another file descriptor or
- # connecting to the socket to wake this up instead of
- # polling. Polling reduces our responsiveness to a
- # shutdown request and wastes cpu at all other times.
- r, w, e = _eintr_retry(select.select, [self], [], [],poll_interval)
- {
- def _eintr_retry(func, *args):
- # restart a system call interrupted by EINTR
- while True:
- try:
- return func(*args)
- except (OSError, select.error) as e:
- if e.args[0] != errno.EINTR:
- raise
- }
- # 读句柄
- if self in r:
- self._handle_request_noblock()
- {
- def _handle_request_noblock(self):
- # Handle one request, without blocking.
- # I assume that select.select has returned that the socket is
- # readable before this function was called, so there should be
- # no risk of blocking in get_request().
- try:
- # 获取连接 request === connection
- request, client_address = self.get_request()
- {
- def get_request(self):
- # Get the request and client address from the socket.
- # May be overridden.
- return self.socket.accept()
- }
- except socket.error:
- return
- # 连接 request === connection
- if self.verify_request(request, client_address):
- try:
- # 单进程/单线程方案,不会被重写
- # 多线程方案,socketserver.ThreadingMixIn 会重写 process_request(...) 方法
- # 多进程方案,socketserver.ForkingMixIn 会重写 process_request(...) 方法
- # 获取连接 request === connection
- self.process_request(request, client_address)
- {
- # 连接 request === connection
- def process_request(self, request, client_address):
- # Call finish_request.
- # Overridden by ForkingMixIn and ThreadingMixIn.
- self.finish_request(request, client_address)
- {
- def finish_request(self, request, client_address):
- # 执行请求 request === connection
- # RequestHandlerClass === django.core.servers.basehttp.WSGIRequestHandler
- # Finish one request by instantiating RequestHandlerClass.
- self.RequestHandlerClass(request, client_address, self)
- {
- # BaseRequestHandler
- def __init__(self, request, client_address, server):
- # request 对象 request == connection
- self.request = request
- self.client_address = client_address
- # 如: server == django.core.servers.basehttp.WSGIServer
- self.server = server
- # 安装请求 === django.core.servers.basehttp.WSGIRequestHandler.setup()
- self.setup()
- {
- # SocketServer.StreamRequestHandler
- def setup(self):
- # 连接 connection == request
- self.connection = self.request
- if self.timeout is not None:
- self.connection.settimeout(self.timeout)
- if self.disable_nagle_algorithm:
- self.connection.setsockopt(socket.IPPROTO_TCP,
- socket.TCP_NODELAY, True)
- # 读句柄
- self.rfile = self.connection.makefile('rb', self.rbufsize)
- # 写句柄
- self.wfile = self.connection.makefile('wb', self.wbufsize)
- }
- try:
- # 处理请求 === django.core.servers.basehttp.WSGIRequestHandler.handle()
- self.handle()
- {
- # django.core.servers.basehttp.WSGIRequestHandler.handle()
- def handle(self):
- # 获取 65537 行数据
- # Copy of WSGIRequestHandler, but with different ServerHandler
- self.raw_requestline = self.rfile.readline(65537)
- if len(self.raw_requestline) > 65536:
- self.requestline = ''
- self.request_version = ''
- self.command = ''
- self.send_error(414)
- return
-
- # 解析请求
- if not self.parse_request(){
- self.command = None # set in case of error on the first line
- self.request_version = version = self.default_request_version
- self.close_connection = 1
- requestline = self.raw_requestline
- requestline = requestline.rstrip('\r\n')
- self.requestline = requestline
- words = requestline.split()
- if len(words) == 3:
- command, path, version = words
- if version[:5] != 'HTTP/':
- self.send_error(400, "Bad request version (%r)" % version)
- return False
- try:
- # http 版本信息
- base_version_number = version.split('/', 1)[1]
- version_number = base_version_number.split(".")
- # RFC 2145 section 3.1 says there can be only one "." and
- # - major and minor numbers MUST be treated as
- # separate integers;
- # - HTTP/2.4 is a lower version than HTTP/2.13, which in
- # turn is lower than HTTP/12.3;
- # - Leading zeros MUST be ignored by recipients.
- if len(version_number) != 2:
- raise ValueError
- version_number = int(version_number[0]), int(version_number[1])
- except (ValueError, IndexError):
- self.send_error(400, "Bad request version (%r)" % version)
- return False
- if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
- self.close_connection = 0
- if version_number >= (2, 0):
- self.send_error(505,
- "Invalid HTTP Version (%s)" % base_version_number)
- return False
- elif len(words) == 2:
- command, path = words
- self.close_connection = 1
- if command != 'GET':
- self.send_error(400,
- "Bad HTTP/0.9 request type (%r)" % command)
- return False
- elif not words:
- return False
- else:
- self.send_error(400, "Bad request syntax (%r)" % requestline)
- return False
- self.command, self.path, self.request_version = command, path, version
-
- # 获取头部信息
- # Examine the headers and look for a Connection directive
- self.headers = self.MessageClass(self.rfile, 0)
-
- conntype = self.headers.get('Connection', "")
- if conntype.lower() == 'close':
- self.close_connection = 1
- elif (conntype.lower() == 'keep-alive' and
- self.protocol_version >= "HTTP/1.1"):
- self.close_connection = 0
- return True
-
- }: # An error code has been sent, just exit
- return
-
- # 处理请求
- # handler === django.core.servers.basehttp.ServerHandler
- handler = ServerHandler(
- self.rfile, self.wfile, self.get_stderr(), self.get_environ()
- )
- {
- def __init__(self,stdin,stdout,stderr,environ,
- multithread=True, multiprocess=False
- ):
- self.stdin = stdin # 输入句柄
- self.stdout = stdout # 输出句柄
- self.stderr = stderr
- self.base_env = environ # 环境变量
- self.wsgi_multithread = multithread
- self.wsgi_multiprocess = multiprocess
- }
-
- # handler.request_handler === django.core.servers.basehttp.WSGIRequestHandler
- handler.request_handler = self # backpointer for logging
-
- # !!! django.core.servers.basehttp.ServerHandler.run(self.server.get_app())
- # self.server === django.core.servers.basehttp.WSGIServer
- # self.server.get_app() == django.core.handlers.wsgi.WSGIHandler
- handler.run(self.server.get_app())
- {
- def run(self, application):
- # Invoke the application
- # Note to self: don't move the close()! Asynchronous servers shouldn't
- # call close() from finish_response(), so if you close() anywhere but
- # the double-error branch here, you'll break asynchronous servers by
- # prematurely closing. Async servers must return from 'run()' without
- # closing if there might still be output to iterate over.
- try:
- # 安装环境变量
- # wsgiref.handlers.BaseHandler.setup_environ()
- self.setup_environ()
- {
- def setup_environ(self):
- # Set up the environment for one request
- # 拷贝系统环境变量
- env = self.environ = self.os_environ.copy()
- # 添加 cgi 变量
- self.add_cgi_vars()
- {
- # SimpleHandler
- def add_cgi_vars(self):
- self.environ.update(self.base_env)
- }
-
- env['wsgi.input'] = self.get_stdin()
- env['wsgi.errors'] = self.get_stderr()
- env['wsgi.version'] = self.wsgi_version
- env['wsgi.run_once'] = self.wsgi_run_once
- env['wsgi.url_scheme'] = self.get_scheme()
- env['wsgi.multithread'] = self.wsgi_multithread
- env['wsgi.multiprocess'] = self.wsgi_multiprocess
-
- if self.wsgi_file_wrapper is not None:
- env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
-
- if self.origin_server and self.server_software:
- env.setdefault('SERVER_SOFTWARE',self.server_software)
- }
-
- # 执行结果放入 self.result
- # application === django.core.handlers.wsgi.WSGIHandler
- self.result = application(self.environ, self.start_response)
- {
- # 调用
- # django.core.handlers.wsgi.WSGIHandler.__call__()
- def __call__(self, environ, start_response):
- # 设置脚本前缀
- set_script_prefix(get_script_name(environ))
- signals.request_started.send(sender=self.__class__, environ=environ)
-
- # 创建 request 对象
- # request_class = django.core.handlers.wsgi.WSGIRequest
- request = self.request_class(environ)
- {
- def __init__(self, environ):
- # 获取 script_name 信息
- script_name = get_script_name(environ)
- # 获取 path_info 信息
- path_info = get_path_info(environ)
- if not path_info:
- # Sometimes PATH_INFO exists, but is empty (e.g. accessing
- # the SCRIPT_NAME URL without a trailing slash). We really need to
- # operate as if they'd requested '/'. Not amazingly nice to force
- # the path like this, but should be harmless.
- path_info = '/'
- self.environ = environ
- self.path_info = path_info
- # be careful to only replace the first slash in the path because of
- # http://test/something and http://test//something being different as
- # stated in http://www.ietf.org/rfc/rfc2396.txt
- self.path = '%s/%s' % (script_name.rstrip('/'),
- path_info.replace('/', '', 1))
- self.META = environ
- self.META['PATH_INFO'] = path_info
- self.META['SCRIPT_NAME'] = script_name
- self.method = environ['REQUEST_METHOD'].upper()
- self.content_type, self.content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
- if 'charset' in self.content_params:
- try:
- codecs.lookup(self.content_params['charset'])
- except LookupError:
- pass
- else:
- self.encoding = self.content_params['charset']
- self._post_parse_error = False
- try:
- content_length = int(environ.get('CONTENT_LENGTH'))
- except (ValueError, TypeError):
- content_length = 0
- self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
- self._read_started = False
- self.resolver_match = None
- }
-
- # 执行中间件链条,响应 response 对象
- response = self.get_response(request)
- {
- # Setup default url resolver for this thread
- set_urlconf(settings.ROOT_URLCONF)
-
- # ------------ 传入中间件链条,并执行 ------------
- response = self._middleware_chain(request)
-
- # This block is only needed for legacy MIDDLEWARE_CLASSES; if
- # MIDDLEWARE is used, self._response_middleware will be empty.
- try:
- # !!! 应用 _response_middleware 类的中间件
- # Apply response middleware, regardless of the response
- for middleware_method in self._response_middleware:
- # 应用 _response_middleware 类的中间件
- response = middleware_method(request, response)
- # Complain if the response middleware returned None (a common error).
- if response is None:
- raise ValueError(
- "%s.process_response didn't return an "
- "HttpResponse object. It returned None instead."
- % (middleware_method.__self__.__class__.__name__))
- except Exception: # Any exception should be gathered and handled
- signals.got_request_exception.send(sender=self.__class__, request=request)
- response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
-
-
- response._closable_objects.append(request)
-
- # --------- response 对象中有 render 方法 ---------
- # If the exception handler returns a TemplateResponse that has not
- # been rendered, force it to be rendered.
- if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
- response = response.render() # 调用 render 方法
-
- # 响应状态码
- if response.status_code == 404:
- logger.warning(
- 'Not Found: %s', request.path,
- extra={'status_code': 404, 'request': request},
- )
-
- return response
- }
- # response._handler_class === django.core.handlers.wsgi.WSGIHandler
- response._handler_class = self.__class__
-
- # 响应状态信息
- status = '%d %s' % (response.status_code, response.reason_phrase)
- # 响应头
- response_headers = [(str(k), str(v)) for k, v in response.items()]
- # 在响应头中添加cookie信息
- for c in response.cookies.values():
- response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
-
- # !!! django.core.handlers.wsgi.WSGIHandler.start_response(...)
- start_response(force_str(status), response_headers)
- {
- def start_response(self, status, headers,exc_info=None):
- # 'start_response()' callable as specified by PEP 333
-
- if exc_info:
- try:
- if self.headers_sent:
- # Re-raise original exception if headers sent
- raise exc_info[0], exc_info[1], exc_info[2]
- finally:
- exc_info = None # avoid dangling circular ref
- elif self.headers is not None:
- raise AssertionError("Headers already set!")
-
- assert type(status) is StringType,"Status must be a string"
- assert len(status)>=4,"Status must be at least 4 characters"
- assert int(status[:3]),"Status message must begin w/3-digit code"
- assert status[3]==" ", "Status message must have a space after code"
- if __debug__:
- for name,val in headers:
- assert type(name) is StringType,"Header names must be strings"
- assert type(val) is StringType,"Header values must be strings"
- assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
- self.status = status
- # !!! 响应头 headers_class === Headers
- self.headers = self.headers_class(headers)
- return self.write
- }
- if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
- response = environ['wsgi.file_wrapper'](response.file_to_stream)
- return response
-
- }
-
- # django.core.servers.basehttp.ServerHandler.finish_response()
- self.finish_response()
- {
- def finish_response(self):
- # Send any iterable data, then close self and the iterable
- # Subclasses intended for use in asynchronous servers will
- # want to redefine this method, such that it sets up callbacks
- # in the event loop to iterate over the data, and to call
- # 'self.close()' once the response is finished.
- try:
- if not self.result_is_file() or not self.sendfile():
- # 处理执行结果 self.result
- for data in self.result:
- self.write(data)
- {
- assert type(data) is StringType,"write() argument must be string"
-
- if not self.status:
- raise AssertionError("write() before start_response()")
-
- elif not self.headers_sent:
- # Before the first output, send the stored headers
- self.bytes_sent = len(data) # make sure we know content-length
- self.send_headers()
- {
- def send_headers(self):
- # Transmit headers to the client, via self._write()
- self.cleanup_headers()
- self.headers_sent = True
- if not self.origin_server or self.client_is_modern():
- self.send_preamble()
- # 发送 header 数据
- self._write(str(self.headers))
- {
- def _write(self,data):
- self.stdout.write(data)
- self._write = self.stdout.write
- }
- }
- else:
- self.bytes_sent += len(data)
-
- # 发送 body 数据
- # XXX check Content-Length and truncate if too many bytes written?
- self._write(data)
- {
- def _write(self,data):
- self.stdout.write(data)
- self._write = self.stdout.write
- }
- self._flush()
-
- }
- self.finish_content()
- finally:
- self.close()
- }
- except:
- try:
- self.handle_error()
- except:
- # If we get an error handling an error, just give up already!
- self.close()
- raise # ...and let the actual server figure it out.
- }
- }
- finally:
- # 完成请求 === django.core.servers.basehttp.WSGIRequestHandler.finish()
- self.finish()
- {
- # SocketServer.StreamRequestHandler
- def finish(self):
- if not self.wfile.closed:
- try:
- self.wfile.flush()
- except socket.error:
- # An final socket error may have occurred here, such as
- # the local error ECONNABORTED.
- pass
- self.wfile.close()
- self.rfile.close()
- }
- }
- }
- self.shutdown_request(request)
- }
- except:
- self.handle_error(request, client_address)
- self.shutdown_request(request)
- }
- finally:
- self.__shutdown_request = False
- self.__is_shut_down.set()
- }
- }
- 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 = force_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)
- }
- }
- }
-
- if output:
- if self.output_transaction:
- connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
- output = '%s\n%s\n%s' % (
- self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
- output,
- self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
- )
- self.stdout.write(output)
- finally:
- if saved_locale is not None:
- translation.activate(saved_locale)
- return output
- }
-
- # 调用父类 execute - end
- }
- except Exception as e:
- if options.traceback or not isinstance(e, CommandError):
- raise
-
- # SystemCheckError takes care of its own formatting.
- if isinstance(e, SystemCheckError):
- self.stderr.write(str(e), lambda x: x)
- else:
- self.stderr.write('%s: %s' % (e.__class__.__name__, e))
- sys.exit(1)
- finally:
- try:
- connections.close_all()
- except ImproperlyConfigured:
- # Ignore if connections aren't setup at this point (e.g. no
- # configured settings).
- pass
- }
- }
- }
本文暂时没有评论,来添加一个吧(●'◡'●)