From a8bf15b35874db5e80aefc3442a49a2ceb6dcb89 Mon Sep 17 00:00:00 2001 From: Benjamin ABEL Date: Sun, 25 Jan 2015 18:12:41 +0100 Subject: [PATCH 1/4] Use django 1.8 branch in `tox.ini` --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index f07ed66..e0c6665 100644 --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,8 @@ deps = dj15: https://github.com/django/django/archive/stable/1.5.x.zip#egg=django dj16: https://github.com/django/django/archive/stable/1.6.x.zip#egg=django dj17: https://github.com/django/django/archive/stable/1.7.x.zip#egg=django - dj18: https://github.com/django/django/archive/master.zip#egg=django + dj18: https://github.com/django/django/archive/stable/1.8.x.zip#egg=django + dj19: https://github.com/django/django/archive/master.zip#egg=django commands = py.test {posargs:} From d9b2815526f72fc6fff6a6c4114ae61f6759f854 Mon Sep 17 00:00:00 2001 From: Benjamin ABEL Date: Sun, 25 Jan 2015 18:13:47 +0100 Subject: [PATCH 2/4] Use CommandParser instead of LaxOptionParser in django1.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a django version check, and removed the `LaxOptionParser` import for django>=1.8 and used `CommandParser` instead as in Claude Paroz django [commit](https://github.com/django/django/commit/856863860352aa1f0288e6c9168a0e423c4f5184?diff=split#diff-860fce37924469764af399caaa365e00R275) Reference: [#19973 (Management commands migration to argparse) – Django](https://code.djangoproject.com/ticket/19973) --- configurations/importer.py | 67 +++++++++++++++++++++++++------------- configurations/utils.py | 62 ----------------------------------- 2 files changed, 44 insertions(+), 85 deletions(-) diff --git a/configurations/importer.py b/configurations/importer.py index a76e773..4e36be6 100644 --- a/configurations/importer.py +++ b/configurations/importer.py @@ -2,12 +2,12 @@ import imp import logging import os import sys -from optparse import make_option +from django import VERSION as DJ_VERSION from django.core.exceptions import ImproperlyConfigured from django.conf import ENVIRONMENT_VARIABLE as SETTINGS_ENVIRONMENT_VARIABLE -from .utils import uppercase_attributes, reraise, LaxOptionParser +from .utils import uppercase_attributes, reraise from .values import Value, setup_value installed = False @@ -15,22 +15,9 @@ installed = False CONFIGURATION_ENVIRONMENT_VARIABLE = 'DJANGO_CONFIGURATION' -configuration_options = ( - make_option('--configuration', - help='The name of the configuration class to load, e.g. ' - '"Development". If this isn\'t provided, the ' - 'DJANGO_CONFIGURATION environment variable will ' - 'be used.'),) - - def install(check_options=False): global installed if not installed: - from django.core.management import base - - # add the configuration option to all management commands - base.BaseCommand.option_list += configuration_options - importer = ConfigurationImporter(check_options=check_options) sys.meta_path.insert(0, importer) installed = True @@ -67,14 +54,48 @@ class ConfigurationImporter(object): return os.environ.get(self.namevar) def check_options(self): - parser = LaxOptionParser(option_list=configuration_options, - add_help_option=False) - try: - options, args = parser.parse_args(self.argv) - if options.configuration: - os.environ[self.namevar] = options.configuration - except: - pass # Ignore any option errors at this point. + # django switched to argparse in version 1.8 + if DJ_VERSION >= (1, 8): + from django.core.management.base import (CommandError, + CommandParser, + handle_default_options) + parser = CommandParser(None, + usage="%(prog)s subcommand [options] [args]", + add_help=False) + parser.add_argument('--settings') + parser.add_argument('--pythonpath') + parser.add_argument('--configuration', + help='The name of the configuration class to load, e.g. ' + '"Development". If this isn\'t provided, the ' + 'DJANGO_CONFIGURATION environment variable will ' + 'be used.') + + parser.add_argument('args', nargs='*') # catch-all + try: + options, args = parser.parse_known_args(self.argv[2:]) + if options.configuration: + os.environ[self.namevar] = options.configuration + handle_default_options(options) + except CommandError: + pass # Ignore any option errors at this point. + # django < 1.7 did use optparse + else: + from django.core.management import LaxOptionParser + from optparse import make_option + configuration_options = (make_option('--configuration', + help='The name of the configuration class to load, e.g. ' + '"Development". If this isn\'t provided, the ' + 'DJANGO_CONFIGURATION environment variable will ' + 'be used.'),) + + parser = LaxOptionParser(option_list=configuration_options, + add_help_option=False) + try: + options, args = parser.parse_args(self.argv) + if options.configuration: + os.environ[self.namevar] = options.configuration + except: + pass # Ignore any option errors at this point. def validate(self): if self.name is None: diff --git a/configurations/utils.py b/configurations/utils.py index ce5e598..6ac49b7 100644 --- a/configurations/utils.py +++ b/configurations/utils.py @@ -62,68 +62,6 @@ def reraise(exc, prefix=None, suffix=None): exc.args = ('{0} {1} {2}'.format(prefix, exc.args[0], suffix),) + args[1:] raise -try: - from django.core.management import LaxOptionParser -except ImportError: - from optparse import OptionParser - - class LaxOptionParser(OptionParser): - """ - An option parser that doesn't raise any errors on unknown options. - - This is needed because the --settings and --pythonpath options affect - the commands (and thus the options) that are available to the user. - - Backported from Django 1.7.x - - """ - def error(self, msg): - pass - - def print_help(self): - """Output nothing. - - The lax options are included in the normal option parser, so under - normal usage, we don't need to print the lax options. - """ - pass - - def print_lax_help(self): - """Output the basic options available to every command. - - This just redirects to the default print_help() behavior. - """ - OptionParser.print_help(self) - - def _process_args(self, largs, rargs, values): - """ - Overrides OptionParser._process_args to exclusively handle default - options and ignore args and other options. - - This overrides the behavior of the super class, which stop parsing - at the first unrecognized option. - """ - while rargs: - arg = rargs[0] - try: - if arg[0:2] == "--" and len(arg) > 2: - # process a single long option (possibly with value(s)) - # the superclass code pops the arg off rargs - self._process_long_opt(rargs, values) - elif arg[:1] == "-" and len(arg) > 1: - # process a cluster of short options (possibly with - # value(s) for the last one only) - # the superclass code pops the arg off rargs - self._process_short_opts(rargs, values) - else: - # it's either a non-default option or an arg - # either way, add it to the args list so we can keep - # dealing with options - del rargs[0] - raise Exception - except: # Needed because we might need to catch a SystemExit - largs.append(arg) - # Copied over from Sphinx if sys.version_info >= (3, 0): From ae767eaf2dbc8fd7823920ced641924faea04e61 Mon Sep 17 00:00:00 2001 From: Benjamin ABEL Date: Tue, 27 Jan 2015 22:36:59 +0100 Subject: [PATCH 3/4] Cleaned `importer` to be more DRY --- configurations/importer.py | 41 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/configurations/importer.py b/configurations/importer.py index 4e36be6..4192617 100644 --- a/configurations/importer.py +++ b/configurations/importer.py @@ -2,10 +2,12 @@ import imp import logging import os import sys +from optparse import make_option from django import VERSION as DJ_VERSION -from django.core.exceptions import ImproperlyConfigured from django.conf import ENVIRONMENT_VARIABLE as SETTINGS_ENVIRONMENT_VARIABLE +from django.core.exceptions import ImproperlyConfigured +from django.core.management import base from .utils import uppercase_attributes, reraise from .values import Value, setup_value @@ -13,11 +15,25 @@ from .values import Value, setup_value installed = False CONFIGURATION_ENVIRONMENT_VARIABLE = 'DJANGO_CONFIGURATION' +CONFIGURATION_ARGUMENT = '--configuration' +CONFIGURATION_ARGUMENT_HELP = ('The name of the configuration class to load, e.g. ' + '"Development". If this isn\'t provided, the ' + 'DJANGO_CONFIGURATION environment variable will ' + 'be used.') + + +configuration_options = (make_option(CONFIGURATION_ARGUMENT, + help=CONFIGURATION_ARGUMENT_HELP),) def install(check_options=False): global installed if not installed: + if DJ_VERSION >= (1, 8): + pass + else: + # add the configuration option to all management commands + base.BaseCommand.option_list += configuration_options importer = ConfigurationImporter(check_options=check_options) sys.meta_path.insert(0, importer) installed = True @@ -56,38 +72,25 @@ class ConfigurationImporter(object): def check_options(self): # django switched to argparse in version 1.8 if DJ_VERSION >= (1, 8): - from django.core.management.base import (CommandError, - CommandParser, - handle_default_options) - parser = CommandParser(None, + parser = base.CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False) parser.add_argument('--settings') parser.add_argument('--pythonpath') - parser.add_argument('--configuration', - help='The name of the configuration class to load, e.g. ' - '"Development". If this isn\'t provided, the ' - 'DJANGO_CONFIGURATION environment variable will ' - 'be used.') + parser.add_argument(CONFIGURATION_ARGUMENT, + help=CONFIGURATION_ARGUMENT_HELP) parser.add_argument('args', nargs='*') # catch-all try: options, args = parser.parse_known_args(self.argv[2:]) if options.configuration: os.environ[self.namevar] = options.configuration - handle_default_options(options) - except CommandError: + base.handle_default_options(options) + except base.CommandError: pass # Ignore any option errors at this point. # django < 1.7 did use optparse else: from django.core.management import LaxOptionParser - from optparse import make_option - configuration_options = (make_option('--configuration', - help='The name of the configuration class to load, e.g. ' - '"Development". If this isn\'t provided, the ' - 'DJANGO_CONFIGURATION environment variable will ' - 'be used.'),) - parser = LaxOptionParser(option_list=configuration_options, add_help_option=False) try: From 36a7061a61b5216499d3a284d6d41f1373b7e85e Mon Sep 17 00:00:00 2001 From: Benjamin ABEL Date: Tue, 27 Jan 2015 22:49:58 +0100 Subject: [PATCH 4/4] Add a test for configuration argument This test do not use mock, and only searches the `configuration` option in the help messages. --- tests/test_cli.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/test_cli.py diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..dade97c --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,17 @@ +import os +import subprocess + +PROJECT_DIR = os.getcwd() +TEST_PROJECT_DIR = os.path.join(PROJECT_DIR, 'test_project') + + +def test_configuration_argument_in_cli(): + """Verify that's configuration option has been added to managements commands""" + os.chdir(TEST_PROJECT_DIR) + p = subprocess.Popen(['python', 'manage.py', 'test', + '--help'], stdout=subprocess.PIPE) + assert '--configuration' in p.communicate()[0].decode('UTF-8') + p = subprocess.Popen(['python', 'manage.py', 'runserver', + '--help'], stdout=subprocess.PIPE) + assert '--configuration' in p.communicate()[0].decode('UTF-8') + os.chdir(PROJECT_DIR)