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 .
# 导入文件 /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. .
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. . 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
}
}
}