def run (self): """ Run the given command """ try: subcommand = self.argv[1] except IndexError: subcommand = 'help' 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. no_settings_commands = [ 'help', 'version', '--help', '--version', '-h', 'startapp' ] try: settings.INSTALLED_SERVICES except ImproperlyConfigured as exc: self.setting_exception = exc if subcommand in no_settings_commands: settings.configure() if settings.configured: if subcommand == 'runapp' not in self.argv: try: check_errors(molnframework.setup)() except Exception: apps.ready = True else: molnframework.setup() #server.start() # TODO: activate command system once the framework is stable 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(molnframework.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)
def test_check_errors_catches_all_exceptions(self): """ Since Python may raise arbitrary exceptions when importing code, check_errors() must catch Exception, not just some subclasses. """ filename = self.temporary_file('test_exception.py') filename.write_text('raise Exception') with extend_sys_path(str(filename.parent)): with self.assertRaises(Exception): autoreload.check_errors(import_module)('test_exception') self.assertFileFound(filename)
def test_check_errors(self): """ When a file containing an error is imported in a function wrapped by check_errors(), gen_filenames() returns it. """ filename = self.temporary_file('test_syntax_error.py') filename.write_text("Ceci n'est pas du Python.") with extend_sys_path(str(filename.parent)): with self.assertRaises(SyntaxError): autoreload.check_errors(import_module)('test_syntax_error') self.assertFileFound(filename)
def test_check_errors_only_new(self): """ When a file containing an error is imported in a function wrapped by check_errors(), gen_filenames(only_new=True) returns it. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_syntax_error.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w') as f: f.write("Ceci n'est pas du Python.") with extend_sys_path(dirname): with self.assertRaises(SyntaxError): autoreload.check_errors(import_module)('test_syntax_error') self.assertFileFoundOnlyNew(filename)
def test_check_errors_catches_all_exceptions(self): """ Since Python may raise arbitrary exceptions when importing code, check_errors() must catch Exception, not just some subclasses. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_exception.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w') as f: f.write("raise Exception") with extend_sys_path(dirname): with self.assertRaises(Exception): autoreload.check_errors(import_module)('test_exception') self.assertFileFound(filename)
def test_mutates_error_files(self): fake_method = mock.MagicMock(side_effect=RuntimeError()) wrapped = autoreload.check_errors(fake_method) with mock.patch.object(autoreload, '_error_files') as mocked_error_files: with self.assertRaises(RuntimeError): wrapped() self.assertEqual(mocked_error_files.append.call_count, 1)
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. # 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: 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: 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() 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]) # 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).run_from_argv(self.argv)
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. # 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. no_settings_commands = [ 'help', 'version', '--help', '--version', '-h', 'compilemessages', 'makemessages', 'startapp', 'startproject', ] try: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc # A handful of built-in management commands work without settings. # Load the default settings -- where INSTALLED_APPS is empty. if subcommand in no_settings_commands: settings.configure() 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: autoreload.check_errors(django.setup)() except Exception: # The exception will be raised later in the child process # started by the autoreloader. pass # 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]) # 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).run_from_argv(self.argv)
from __future__ import unicode_literals
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. !!! OVERRIDED !!! Adding new user defined command-line arguments. """ 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: 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: 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() 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]) # Special-cases: We want 'django-admin --version', # 'django-admin --help' to work, for backwards compatibility and # !!! USER DEFINED !!! # 'django-admin --importdirs' to pre-populate directories database. elif subcommand == 'version' or self.argv[1:] == ['--version']: sys.stdout.write(django.get_version() + '\n') elif subcommand == 'importdirs' or subcommand == '--importdirs': check = False continue_bool = False try: args.index("--check") check = True except ValueError: pass try: args.index("--continue") continue_bool = True except ValueError: pass populate_db(settings.ROOT_DIRECTORY, settings.THUMBNAILS_DIRECTORIES, check, continue_bool) 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)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. 1、命令行参数解析 2、 """ try: # 如果在调用django-admin 的时候没有给出任何的 命令 & 选项 # 那么slef.argv 列表只有一项 它就是slef.argv[0] 它的值为django-admin # 所以这种情况下正好会报 IndexError subcommand = self.argv[1] except IndexError: # 如果没有给出任何命令的情况下 把subcommand设置为help # 也就是说不给出命令的情况下 就当做是看django-admin的帮助. 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. # django 在标准库的argparser上作出了一些自定义 # CommandParser 继承了argparser.ArgumentParser # CommandParser 位于 jango.core.management.base 这个包中. # usage & add_help 都是argparser.ArgumentParser.__init__中的参数 # CommandParser.__init__(self, cmd, **kwargs): 由这里可以看了cmd=None 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 是在django.conf包中LazySettings类的实例 # 这里有个问题,就是在LazySettings类的代码中没有找到INSTALLED_APPS 的定义 # settings.INSTALLED_APPS 像这样的语句又有什么意义呢?难道它这样干就单单是为了引发异常? 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: 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() 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]) # 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).run_from_argv(self.argv)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. """ # 1 Subcommand: 子程序名, 默认为help, 见上面定义 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. # 2 处理和解析参数 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. MY(1.0, '解析后的参数:', '\n\toptions:', options, '\n\targs:', args) # 3 LazySetting对象, __getattr__/__setattr__方法,完成INSTALLED_APPS赋值 # 懒加载. try: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc # django.setup() # 4.1 配置日志 # 4.2 加载自定义模块 # 4.3 加载model模块 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. # a 重新导入所有配置信息 if subcommand == 'runserver' and '--noreload' not in self.argv: try: 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. # b 配置日志, 加载自定义模块, models模块, 使用Application else: django.setup() self.autocomplete() # 5 fetch_command, 返回不同的Command对象, 根据不同的参数运行不同的后台任务, # 5.1 调用各个 BaseCommand 子类的方法来完成对应的业务, 例如: # runserver.py.Command # flush.py.Command # and so on # 5.2 不同Command流程(handle相当于钩子): # run_from_argv, 进行参数解析和配置, 调用execute # execute, 设置环境变量,运行handle, 在父类实现大部分逻辑; # handle, 进入run, 由各个子类rewrite, (主要处理逻辑) # run_from_argv, 处理善后 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']: # Special-cases: We want 'django-admin --version' and # 'django-admin --help' to work, for backwards compatibility. sys.stdout.write(django.get_version() + '\n') elif self.argv[1:] in (['--help'], ['-h']): sys.stdout.write(self.main_help_text() + '\n') else: MY(1, '\n\tShell subcommand:', subcommand, ' Argv:', self.argv) # 执行一个django-admin命令, 当然也可以自定义该命令 # 该函数: django.core.management.base self.fetch_command(subcommand).run_from_argv(self.argv)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. 给定命令行参数后,找出正在运行的子命令,创建适合该命令的解析器,并运行它。 """ try: # 子命令,例如 runserver 、migrate subcommand = self.argv[1] except IndexError: subcommand = 'help' # Preprocess options to extract --settings and --pythonpath. # These options could affect the commands that are available, so they # must be processed early. parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=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: # 此对象是 django.conf.__init__.LazySettings 类的实例 # 此对象的全部属性都来自应用对象的 settings.py 配置文件 settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc except ImportError as exc: self.settings_exception = exc if settings.configured: #print('【django.core.management.__init__.ManagementUtility.execute】subcommand:', subcommand) #print('【django.core.management.__init__.ManagementUtility.execute】self.argv:', self.argv) if subcommand == 'runserver' and '--noreload' not in self.argv: try: # 参数 django.setup 是定义在 django.__init__ 模块中的函数 # 而 check_errors 是作检测异常之用 # 这里会调用 django.setup 函数 # 将 settings.INSTALLED_APPS 列表中的应用程序放到 apps.app_configs 字典中 # apps 对象可以看做一个应用对象收集器 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(dict) apps.app_configs = {} 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() self.autocomplete() if subcommand == 'help': if '--commands' in args: sys.stdout.write( self.main_help_text(commands_only=True) + '\n') elif not options.args: 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: # 下面的 fetch_command 方法根据命令行参数找到对应的模块中的 Command 类的实例并返回 # 它通过一个字典来找,每个命令都是字典中的一个 key # 根据命令找到对应的 value ,它是一个包的字符串,再据此找到包下面的 management/commands 子目录 # 然后在这个子目录下面找到与命令同名的文件 # 以 python manage.py makemigrate 命令为例 # 这个方法就会返回 .../management/commands/makemigrate.py 文件中的 Command 类的实例 # 下面的 cmd 我们称之为「命令处理对象」,以 runserver 命令为例 # 此对象的父类是 django.contrib.staticfiles.management.commands.runserver.Command 类 # 后者的父类是 django.core.management.commands.runserver.Command 类 # 后者的父类是 django.core.management.base.BaseCommand 类 cmd = self.fetch_command(subcommand) # 不论终端命令是啥 # 下面这个方法都定义在 django.core.management.base.BaseCommand 父类中 # 参数 self.argv 是终端命令参数列表,等同于 sys.argv # 这个方法会调用「命令处理对象」自身的 handle 方法控制其它对象启动线程和创建套接字啥的 cmd.run_from_argv(self.argv)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. 给定命令行参数,找出正在执行的子命令 运行,创建一个适合该命令的解析器,然后运行它。 """ # 1、是否取到位置1的参数, 否则显示帮助信息 try: subcommand = self.argv[1] except IndexError: subcommand = 'help' # Display help if no arguments were given. # 2、参数预处理 # Preprocess options to extract --settings and --pythonpath. 提取一些 settings 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 # 3、 try: options, args = parser.parse_known_args(self.argv[2:]) handle_default_options(options) except CommandError: pass # Ignore any option errors at this point. # 4、是否配置INSTALLED_APPS try: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc # 5、判断是否已配置设置 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. # 5.1 命令== runserver 并且 noreload 不在传入的参数里面 if subcommand == 'runserver' and '--noreload' not in self.argv: try: #5.1.1 检查django 项目的完整性 - 装饰器 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. # 5.2 其他的命令说明django 已经被安装成功 else: django.setup() # 6、输出启动完成信息 self.autocomplete() # 7 判断命令,最终并执行 if subcommand == 'help': # 7.1 帮助 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']: # 7.2 django版本获得 sys.stdout.write(django.get_version() + '\n') elif self.argv[1:] in (['--help'], ['-h']): # 7.3 获取帮助主文件 sys.stdout.write(self.main_help_text() + '\n') else: # 7.3 直接执行命令 - 链式调用 self.fetch_command(subcommand).run_from_argv(self.argv)
def execute_from_command_line(*args, **kwargs): ''' This is called if people use manage.py, or if people use the otree script. script_file is no longer used, but we need it for compat Given the command-line arguments, this figures out which subcommand is being run, creates a parser appropriate to that command, and runs it. ''' argv = sys.argv # so that we can patch it easily settings = django.conf.settings if len(argv) == 1: # default command argv.append('help') subcommand = argv[1] if subcommand == 'runserver': sys.stdout.write( "Suggestion: use 'otree devserver' instead of 'otree runserver'. " "devserver automatically syncs your database.\n") # Add the current directory to sys.path so that Python can find # the settings module. # when using "python manage.py" this is not necessary because # the entry-point script's dir is automatically added to sys.path. # but the 'otree' command script is located outside of the project # directory. if os.getcwd() not in sys.path: sys.path.insert(0, os.getcwd()) # to match manage.py # make it configurable so i can test it # note: we will never get ImproperlyConfigured, # because that only happens when DJANGO_SETTINGS_MODULE is not set os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') DJANGO_SETTINGS_MODULE = os.environ['DJANGO_SETTINGS_MODULE'] # some commands don't need settings.INSTALLED_APPS try: configure_settings(DJANGO_SETTINGS_MODULE) except ImportSettingsError: if subcommand in [ 'startproject', 'help', 'version', '--help', '--version', '-h', 'compilemessages', 'makemessages', 'upgrade_my_code', 'update_my_code' ]: if not settings.configured: settings.configure(**get_default_settings({})) # need to differentiate between an ImportError because settings.py # was not found, vs. ImportError because settings.py imports another # module that is not found. elif os.path.isfile('{}.py'.format(DJANGO_SETTINGS_MODULE)): raise else: print_settings_not_found_error() return runserver_or_devserver = subcommand in ['runserver', 'devserver'] if runserver_or_devserver: # apparently required by restart_with_reloader # otherwise, i get: # python.exe: can't open file 'C:\oTree\venv\Scripts\otree': # [Errno 2] No such file or directory # this doesn't work if you start runserver from another dir # like python my_project/manage.py runserver. but that doesn't seem # high-priority now. sys.argv = ['manage.py'] + argv[1:] # previous solution here was using subprocess.Popen, # but changing it to modifying sys.argv changed average # startup time on my machine from 2.7s to 2.3s. # 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. block_use_of_random() if runserver_or_devserver and '--noreload' not in argv: try: autoreload.check_errors(do_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 else: do_django_setup() unblock_use_of_random() if subcommand in ['help', '--help', '-h'] and len(argv) == 2: sys.stdout.write(main_help_text() + '\n') elif subcommand == 'help' and len(argv) >= 3: command_to_explain = argv[2] fetch_command(command_to_explain).print_help('otree', command_to_explain) elif subcommand in ("version", "--version"): sys.stdout.write(otree.__version__ + '\n') try: pypi_updates_cli() except: pass else: fetch_command(subcommand).run_from_argv(argv)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. """ # 先找出二级指令,没有二级指令就打印帮助信息 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) # 这里是指定settings文件 parser.add_argument('--settings') # 这里是指定python路径 parser.add_argument('--pythonpath') parser.add_argument('args', nargs='*') # catch-all try: options, args = parser.parse_known_args(self.argv[2:]) # 修改环境settings和pythonpath handle_default_options(options) except CommandError: pass # Ignore any option errors at this point. try: # django在启动时会创建settings = LazySettings() # 通过读取os.environ.get('DJANGO_SETTINGS_MODULE'),来初始化settings对象 # settings是一个lazy对象,使用self._wrapped保存Settings对象,当读取属性时,lazy对象会先进行_setup,然后会调用getattr(self._wrapped,key) # 当要读取INSTALLED_APPS时,会先进行_setup # 说白了就是LazySetting对象读取属性时,会真正初始化Settings对象,并赋值给LazySetting._wrapped=Settings() settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc if settings.configured: # 就是返回self._wrapped is not empty,当前是否已经初始化好了settings # 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 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]) # 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: # 这里执行fetch_command('runserver').run_from_argv(['/Users/mering/Proje...manage.py', 'runserver', '--noreload', '--nothreading']) # runserver 对应的是django.contrib.staticfiles # 加载该class import_module('%s.management.commands.%s' % (app_name, name)) # import_module('django.contrib.staticfiles.management.commands.runserver') # 放回的fetch_command(subcommand)=django.contrib.staticfiles.management.commands.runserver.Command() # django.contrib.staticfiles.management.commands.runserver.Command继承自django.core.management.Command, # run_from_argv会调用django.core.management.Command.excute-->django.core.management.BaseCommand.excute # django.core.management.commands.runserver.Command.handle # django.core.management.commands.runserver.Command.run # django.core.management.commands.runserver.Command.inner_run # django.contrib.staticfiles.management.commands.runserver.Command.get_handler 获得wsgiapp # Debug模式下返回StaticFilesHandler # django.core.servers.run # def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): # server_address = (addr, port) # if threading: # # 创建一个新类型继承自socketserver.ThreadingMixIn和WSGIServer # httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {}) # else: # httpd_cls = server_cls # httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) # 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 # httpd.set_app(wsgi_handler) # httpd.serve_forever() self.fetch_command(subcommand).run_from_argv(self.argv)
def execute(self): """ TODO:[-] 20-02-12 由入口函数调用的 Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. """ 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(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=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:]) # 设置系统参数添加至 os.environ 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 except ImportError as exc: self.settings_exception = exc if settings.configured: # Start the auto-reloading dev server even if the code is broken. # The hardcoded condition is a code smell but we can't rely on a # flag on the command class because we haven't located it yet. if subcommand == 'runserver' and '--noreload' not in self.argv: try: 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(dict) apps.app_configs = {} 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() self.autocomplete() if subcommand == 'help': if '--commands' in args: sys.stdout.write( self.main_help_text(commands_only=True) + '\n') elif not options.args: 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).run_from_argv(self.argv)
def execute_from_command_line(*args, **kwargs): ''' This is called if people use manage.py, or if people use the otree script. script_file is no longer used, but we need it for compat Given the command-line arguments, this figures out which subcommand is being run, creates a parser appropriate to that command, and runs it. ''' argv = sys.argv # so that we can patch it easily settings = django.conf.settings if len(argv) == 1: # default command argv.append('help') subcommand = argv[1] if subcommand == 'runserver': sys.stdout.write( "Suggestion: use 'otree devserver' instead of 'otree runserver'. " "devserver automatically syncs your database.\n" ) # Add the current directory to sys.path so that Python can find # the settings module. # when using "python manage.py" this is not necessary because # the entry-point script's dir is automatically added to sys.path. # but the 'otree' command script is located outside of the project # directory. if os.getcwd() not in sys.path: sys.path.insert(0, os.getcwd()) # to match manage.py # make it configurable so i can test it # note: we will never get ImproperlyConfigured, # because that only happens when DJANGO_SETTINGS_MODULE is not set os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') DJANGO_SETTINGS_MODULE = os.environ['DJANGO_SETTINGS_MODULE'] # some commands don't need settings.INSTALLED_APPS try: configure_settings(DJANGO_SETTINGS_MODULE) except ImportSettingsError: if subcommand in [ 'startproject', 'help', 'version', '--help', '--version', '-h', 'compilemessages', 'makemessages', 'upgrade_my_code', 'update_my_code' ]: if not settings.configured: settings.configure(**get_default_settings({})) # need to differentiate between an ImportError because settings.py # was not found, vs. ImportError because settings.py imports another # module that is not found. elif os.path.isfile('{}.py'.format(DJANGO_SETTINGS_MODULE)): raise else: print_settings_not_found_error() return runserver_or_devserver = subcommand in ['runserver', 'devserver'] if runserver_or_devserver: # apparently required by restart_with_reloader # otherwise, i get: # python.exe: can't open file 'C:\oTree\venv\Scripts\otree': # [Errno 2] No such file or directory # this doesn't work if you start runserver from another dir # like python my_project/manage.py runserver. but that doesn't seem # high-priority now. sys.argv = ['manage.py'] + argv[1:] # previous solution here was using subprocess.Popen, # but changing it to modifying sys.argv changed average # startup time on my machine from 2.7s to 2.3s. # 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 runserver_or_devserver and '--noreload' not in argv: try: autoreload.check_errors(do_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 else: do_django_setup() if subcommand in ['help', '--help', '-h'] and len(argv) == 2: sys.stdout.write(main_help_text() + '\n') elif subcommand == 'help' and len(argv) >= 3: command_to_explain = argv[2] fetch_command(command_to_explain).print_help('otree', command_to_explain) elif subcommand in ("version", "--version"): sys.stdout.write(otree.__version__ + '\n') try: pypi_updates_cli() except: pass else: fetch_command(subcommand).run_from_argv(argv)
def execute_from_command_line(*args, **kwargs): ''' Top-level entry point. - figures out which subcommand is being run - sets up django & configures settings - runs the subcommand We have to ignore the args to this function. If the user runs "python manage.py [subcommand]", then argv is indeed passed, but if they run "otree [subcommand]", it executes the autogenerated console_scripts shim, which does not pass any args to this function, just: load_entry_point('otree', 'console_scripts', 'otree')() This is called if people use manage.py, or if people use the otree script. script_file is no longer used, but we need it for compat ''' if len(argv) == 1: # default command argv.append('help') subcommand = argv[1] if subcommand == 'runserver': sys.stdout.write( "Suggestion: use 'otree devserver' instead of 'otree runserver'. " "devserver automatically syncs your database.\n") if subcommand == 'runzip': runzip.main(argv[2:]) # better to return than sys.exit because testing is complicated # with sys.exit -- if you mock it, then the function keeps executing. return # Add the current directory to sys.path so that Python can find # the settings module. # when using "python manage.py" this is not necessary because # the entry-point script's dir is automatically added to sys.path. # but the 'otree' command script is located outside of the project # directory. if os.getcwd() not in sys.path: sys.path.insert(0, os.getcwd()) # to match manage.py # make it configurable so i can test it # note: we will never get ImproperlyConfigured, # because that only happens when DJANGO_SETTINGS_MODULE is not set os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') DJANGO_SETTINGS_MODULE = os.environ['DJANGO_SETTINGS_MODULE'] # help and --help must have settings configured, so that otree can be in # INSTALLED_APPS, so those management commands are available. if subcommand in [ 'startproject', 'version', '--version', 'compilemessages', 'makemessages', 'unzip', 'zip', ]: django_settings.configure(**get_default_settings({})) else: try: configure_settings(DJANGO_SETTINGS_MODULE) except ImportSettingsError: # need to differentiate between an ImportError because settings.py # was not found, vs. ImportError because settings.py imports another # module that is not found. if os.path.isfile('{}.py'.format(DJANGO_SETTINGS_MODULE)): raise else: msg = ("Cannot find oTree settings. " "Please 'cd' to your oTree project folder, " "which contains a settings.py file.") logger.warning(msg) return warning = check_update_needed( Path('.').resolve().joinpath('requirements_base.txt')) if warning: logger.warning(warning) runserver_or_devserver = subcommand in ['runserver', 'devserver'] if runserver_or_devserver: # apparently required by restart_with_reloader # otherwise, i get: # python.exe: can't open file 'C:\oTree\venv\Scripts\otree': # [Errno 2] No such file or directory # this doesn't work if you start runserver from another dir # like python my_project/manage.py runserver. but that doesn't seem # high-priority now. sys.argv = ['manage.py'] + argv[1:] # previous solution here was using subprocess.Popen, # but changing it to modifying sys.argv changed average # startup time on my machine from 2.7s to 2.3s. # 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 runserver_or_devserver and '--noreload' not in argv: try: autoreload.check_errors(do_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 else: do_django_setup() if subcommand in ['help', '--help', '-h'] and len(argv) == 2: sys.stdout.write(MAIN_HELP_TEXT) elif subcommand == 'help' and len(argv) >= 3: command_to_explain = argv[2] fetch_command(command_to_explain).print_help('otree', command_to_explain) elif subcommand in ("version", "--version"): sys.stdout.write(__version__ + '\n') else: fetch_command(subcommand).run_from_argv(argv)
def execute(self): """ Given the command-line arguments, figure out which subcommand is being run, create a parser appropriate to that command, and run it. 给出命令行参数,找出当前的子命令 运行, 创建一个适合该命令的解析器,然后运行它 """ try: subcommand = self.argv[1] # self.argv = sys.argv[:] # subcommand = "runserver" 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(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False) """ usage : 帮助 文档 add_help : 是否显示命令帮助 allow_abbrev: 可选参数 键 时候可以 简写 """ parser.add_argument('--settings') parser.add_argument('--pythonpath') parser.add_argument('args', nargs='*') # catch-all # nargs : * 代表 可 连写 多个参数, 集合在一个列表中 try: options, args = parser.parse_known_args(self.argv[2:]) # options=== Namespace(args=['0.0.0.0:8888'], pythonpath=None, settings=None) # options : Namespace() , 所设定的 各选项 所对应的 值 # args : [] 除了 设定的 自定义选项, 在最后添加的 多余的 参数, handle_default_options(options) except CommandError: pass # Ignore any option errors at this point. try: # 1.0 # 加载 默认全局的配置文件 global_settings.py 和 项目 自定义的 settings.py 文件 # 所有配置 都存储 在 settings._wrapped 属性中,值为 类实例 <class django.conf.Settings objects > settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc except ImportError as exc: self.settings_exception = exc # sys.argv == ['E:/DjangoProject/刘江/login-register-master/manage.py', 'runserver', '0.0.0.0:9999'] if settings.configured: # True, 代表 settings 配置 已 加载完毕 # 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: # 2.0 autoreload.check_errors( django.setup)() # check_errors 为一个装饰器 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. # 在所有其他情况下,需要 django.setup() 才能成功。 else: django.setup() # 配置设置 self.autocomplete() if subcommand == 'help': if '--commands' in args: sys.stdout.write( self.main_help_text(commands_only=True) + '\n') elif not options.args: 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).run_from_argv(self.argv) # 提取命令
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. # 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. no_settings_commands = [ 'help', 'version', '--help', '--version', '-h', 'compilemessages', 'makemessages', 'startapp', 'startproject', ] try: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc # A handful of built-in management commands work without settings. # Load the default settings -- where INSTALLED_APPS is empty. if subcommand in no_settings_commands: settings.configure() 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: 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 # 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]) # 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).run_from_argv(self.argv)
def execute_from_command_line(*args, **kwargs): ''' Top-level entry point. - figures out which subcommand is being run - sets up django & configures settings - runs the subcommand We have to ignore the args to this function. If the user runs "python manage.py [subcommand]", then argv is indeed passed, but if they run "otree [subcommand]", it executes the autogenerated console_scripts shim, which does not pass any args to this function, just: load_entry_point('otree', 'console_scripts', 'otree')() This is called if people use manage.py, or if people use the otree script. script_file is no longer used, but we need it for compat ''' if len(argv) == 1: # default command argv.append('help') subcommand = argv[1] if subcommand in ['runzip', 'zipserver']: zipserver.main(argv[2:]) # better to return than sys.exit because testing is complicated # with sys.exit -- if you mock it, then the function keeps executing. return # Add the current directory to sys.path so that Python can find # the settings module. # when using "python manage.py" this is not necessary because # the entry-point script's dir is automatically added to sys.path. # but the 'otree' command script is located outside of the project # directory. if os.getcwd() not in sys.path: sys.path.insert(0, os.getcwd()) # to match manage.py: # make it configurable so i can test it. # and it must be an env var, because # note: we will never get ImproperlyConfigured, # because that only happens when DJANGO_SETTINGS_MODULE is not set os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') DJANGO_SETTINGS_MODULE = os.environ['DJANGO_SETTINGS_MODULE'] if subcommand in ['help', '--help', '-h'] and len(argv) == 2: sys.stdout.write(MAIN_HELP_TEXT) return # this env var is necessary because if the botworker submits a wait page, # it needs to broadcast to redis channel layer, not in-memory. # this caused an obscure bug on 2019-09-21. # prodserver1of2, 2of2, etc # we now require REDIS_URL to be defined even if using default localhost:6379 # that is to avoid piling up stuff in redis if it's not being used. if ('prodserver' in subcommand or 'webandworkers' in subcommand or 'timeoutworker' in subcommand) and os.environ.get('REDIS_URL'): os.environ['OTREE_USE_REDIS'] = '1' if subcommand in [ 'startproject', 'version', '--version', 'compilemessages', 'makemessages', 'unzip', 'zip', ]: django_settings.configure(**get_default_settings({})) else: try: configure_settings(DJANGO_SETTINGS_MODULE) except ModuleNotFoundError as exc: if exc.name == DJANGO_SETTINGS_MODULE.split('.')[-1]: msg = ("Cannot find oTree settings. " "Please 'cd' to your oTree project folder, " "which contains a settings.py file.") logger.warning(msg) return raise warning = check_update_needed( Path('.').resolve().joinpath('requirements_base.txt')) if warning: logger.warning(warning) is_devserver = subcommand == 'devserver' if is_devserver: # apparently required by restart_with_reloader # otherwise, i get: # python.exe: can't open file 'C:\oTree\venv\Scripts\otree': # [Errno 2] No such file or directory # this doesn't work if you start runserver from another dir # like python my_project/manage.py runserver. but that doesn't seem # high-priority now. sys.argv = ['manage.py'] + argv[1:] # previous solution here was using subprocess.Popen, # but changing it to modifying sys.argv changed average # startup time on my machine from 2.7s to 2.3s. # 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 is_devserver and '--noreload' not in argv: try: autoreload.check_errors(do_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 else: do_django_setup() if subcommand == 'help' and len(argv) >= 3: command_to_explain = argv[2] fetch_command(command_to_explain).print_help('otree', command_to_explain) elif subcommand in ("version", "--version"): sys.stdout.write(__version__ + '\n') else: fetch_command(subcommand).run_from_argv(argv)