Django源码分析之程序执行入口分析
一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了。 那django是如何从一个命令就启动整个server,启动的流程是如何的实现的呢? 首先我们来打开目录下的manage.
打开目录下的manage.py,内容是这样的:
#!/usr/bin/env python
import os
import sys if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_01.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
看来manage.py只是把命令行参数传给django.core.management模块中的execute_from_command_line 函数。
查看execute_from_command_line函数,可以发现实际执行的是ManagementUtility类的excute方法:
def execute(self):
"""
Given the command-line arguments, this figures out which subcommand is
being run, creates a parser appropriate to that command, and runs it.
"""
try:
subcommand = self.argv[1]
except IndexError:
subcommand = 'help' # Display help if no arguments were given. 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:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc if settings.configured: if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
except Exception: apps.all_models = defaultdict(OrderedDict)
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.ready = True _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() self.autocomplete() 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]) 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).run_from_argv(self.argv)
其中的
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.
CommandParser其实类似于Argparse的一个解析命令行参数的类,从代码里可以看出我们可以直接在命令行指定settings文件和pythonpath。
no_settings_commands = [
'help', 'version', '--help', '--version', '-h',
'compilemessages', 'makemessages',
'startapp', 'startproject',
]
try:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc if subcommand in no_settings_commands:
settings.configure()
这块代码就可以解释我们执行python manage.py start project 时django在背后会调用settings.configure方法,这里的settings是指django.conf.LazySettings的一个实例,configure方法其实就是使用django.conf.global_settings.py中的默认设置创建一份新的配置文件,作为我们新创建的project的settings.py
if settings.configured:
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
except Exception:
apps.all_models = defaultdict(OrderedDict)
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.ready = True
# In all other cases, django.setup() is required to succeed.
else:
django.setup()
autoreload.check_errors(django.setup)()其实也是调用django.setup方法,而django.setup方法
def setup():
"""
Configure the settings (this happens as a side effect of accessing the
first setting), configure logging and populate the app registry.
"""
from django.apps import apps
from django.conf import settings
from django.utils.log import configure_logging configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
apps.populate(settings.INSTALLED_APPS)
负责初始化日志模块以及所有应用.
抽丝剥茧
剩下的代码最重要的就是这一句:
self.fetch_command(subcommand).run_from_argv(self.argv)
fetch_command会根据subcommand(这是我们执行python manage.py rumserver时传入的第二个参数:runserver),去django.core.management.commands中查找对应的command类,然后把所有命令行参数传给run_from_argv方法并执行,在runserver这个示例中,最终会调用django.utils.autoreload中的python_reloader或者jython_reloader新开一个线程:
def python_reloader(main_func, args, kwargs):
if os.environ.get("RUN_MAIN") == "true":
thread.start_new_thread(main_func, args, kwargs)
try:
reloader_thread()
except KeyboardInterrupt:
pass
else:
try:
exit_code = restart_with_reloader()
if exit_code < 0:
os.kill(os.getpid(), -exit_code)
else:
sys.exit(exit_code)
except KeyboardInterrupt:
pass
这里的main_func是commands/runserver.py中的inner_run方法:
def inner_run(self, *args, **options):
# If an exception was silenced in ManagementUtility.execute in order
# to be raised in the child process, raise it now.
autoreload.raise_last_exception() threading = options.get('use_threading')
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)
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 http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
"port": self.port,
"quit_command": quit_command,
}) try:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading)
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)
最关键的是这两条语句:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler,ipv6=self.use_ipv6, threading=threading)
get_handler会返回django.core.servers.basehttp中定义的一个application(其实就是我们project下的wigs.py中定义的application)
这是run函数的内容
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
server_address = (addr, port)
if threading:
httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
else:
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
可以看出run函数其实就是启动一个WSGIServer实例(WSGIServer继承python内置类simple_server.WSGIServer),并把handler设置为前面get_handler的返回值
Django源码分析之程序执行入口分析的更多相关文章
- Django源码分析之启动wsgi发生的事
前言 好多人对技术的理解都停留在懂得使用即可,因而只会用而不会灵活用,俗话说好奇害死猫,不然我也不会在凌晨1.48的时候决定写这篇博客,好吧不啰嗦了 继续上一篇文章,后我有个问题(上文:&qu ...
- 2、Django源码分析之启动wsgi发生了哪些事
一 前言 Django是如何通过网络socket层接收数据并将请求转发给Django的urls层? 有的人张口就来:就是通过wsgi(Web Server Gateway Interface)啊! D ...
- Django 源码小剖: 应用程序入口 WSGIHandler
WSGI 有三个部分, 分别为服务器(server), 应用程序(application) 和中间件(middleware). 已经知道, 服务器方面会调用应用程序来处理请求, 在应用程序中有真正的处 ...
- django源码分析 python manage.py runserver
django是一个快速开发web应用的框架, 笔者也在django框架上开发不少web应用,闲来无事,就想探究一下django底层到底是如何实现的,本文记录了笔者对django源码的分析过程 I be ...
- spring源码系列(十): 读取xml入口类 ClassPathXmlApplicationContext 分析
环境准备: 使用spring5.1.6版本 1 xml配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...
- Django 源码小剖: 初探 WSGI
Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...
- Django 源码小剖: 响应数据 response 的返回
响应数据的返回 在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由 ...
- Django 源码小剖: 初探中间件(middleware)
因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddleware 就已经默认安装. BaseH ...
- django源码阅读
最近再看django-bootstrap-toolkit,一直困惑于静态文件的路径问题.所以只能从源码入手了. 从manage.py开始.manage.py 比较简单就几句话. #!/usr/bi ...
随机推荐
- 「mysql优化专题」什么是慢查询?如何通过慢查询日志优化?(10)
日志就跟人们写的日记一样,记录着过往的事情.但是人的日记是主观的(记自己想记的内容),而数据库的日志是客观的,根据记录内容分为以下好几种日志(技术文): a.错误日志:记录启动.运行或停止mysqld ...
- SLAM入门之视觉里程计(2):相机模型(内参数,外参数)
相机成像的过程实际是将真实的三维空间中的三维点映射到成像平面(二维空间)过程,可以简单的使用小孔成像模型来描述该过程,以了解成像过程中三维空间到二位图像空间的变换过程. 本文包含两部分内容,首先介绍小 ...
- 安装Oracle之后在DOS命令下不能使用sqlplus命令启动Oracle?
就像完成安装JDK后需要给系统配置环境变量JAVA_HOME和PATH,以便让系统认识在JDK里面的Javac命令.同样的,安装Oracle也需要这么一个过程.在正常情况下安装Oracle时系统安装程 ...
- bootstrap表格固定表头,表格内容滚动条滚动显示
直接贴代码--- <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...
- 解决jquery.zclip.js插件无法复制的问题
网页中需要用到点击复制,在使用webpack加载jquery以及jquery.zclip.js后,出现了以下情况: jquery顺利加载 zclip插件顺利加载 ZeroClipboard.swf顺利 ...
- scrollTop的兼容性
各浏览器下 scrollTop的差异 IE6/7/8: 对于没有doctype声明的页面里可以使用 document.body.scrollTop 来获取 scrollTop高度 : 对于有docty ...
- 转战Hexo
从之前的转战farbox,到现在又转战Hexo,真是不折腾不成魔.要说farbox有什么不好,可能唯一的不好就是它要求与Dropbox关联吧,这对于大部分国内用户来说是无法接受的,写文章之前一想到还要 ...
- 模板引擎(smarty)知识点总结五
---------重点知识:循环------------ /* smarty 循环之for循环 */ /* 基本的语法 {for $i=$start to $end step ...
- Jmeter中java.net.URISyntaxException错误
今天在做服务发布性能测试的时候,傻傻的犯了个错,没有对参数进行仔细的检查,直接从fiddler中copy到jmeter中了,业务流程配置好后执行测试报错... jmeter中的响应结果如下: java ...
- 关于python中的dir函数
dir函数用于查看python对象的属性,如果所查看的python对象已经定义了__dir__方法,则使用dir会返回定义的__dir__方法的返回值.如果没有定义__dir__方法,则会从__dic ...