Merge lp:~vila/django-configglue/be-verbose into lp:django-configglue

Proposed by Vincent Ladeuil
Status: Merged
Approved by: Ricardo Kirkner
Approved revision: 73
Merged at revision: 69
Proposed branch: lp:~vila/django-configglue/be-verbose
Merge into: lp:django-configglue
Diff against target: 133 lines (+26/-60)
3 files modified
django_configglue/management/__init__.py (+13/-49)
django_configglue/tests/test_configglue.py (+12/-10)
django_configglue/tests/test_settings.py (+1/-1)
To merge this branch: bzr merge lp:~vila/django-configglue/be-verbose
Reviewer Review Type Date Requested Status
Ricardo Kirkner Approve
Review via email: mp+129439@code.launchpad.net

Commit message

Don't swallow standard django options like --verbosity.

Description of the change

In one project I'm working on which uses django_configglue, I came across
something weird, when running:

  django_project/manage.py test --noinput $APPS --verbosity=X

no matter which value I set X to, the output was the same.

Digging further, it happened that django_configglue was swallowing
--verbosity and the test command always received verbosity=1 (the default).

This patch fixes the issue by overriding GlueManagementUtility.execute() in
a narrower way.

I added a failing test prior to the fix and had to fix one other test as a
fallout (it's nice to have a test suite here that precisely caught them ;).

I tweaked another in test_settings for better output when it failed because
I had left the 'version=django.get_version()' when building the
LaxOptionParser (another nice catch of the test suite).

Now I can enjoy more verbosity when running my tests and I hope you will too ;)

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

l. 14: I'd use something like 'Override the base class to handle schema-related options.'
l. 24: is this really necessary? can't we just omit the param?
l. 65: depending on the branch taken by l. 25-34 either configglue_parser or parser is not defined
l. 64-68: this should only be invoked if configglue_parser is defined

given the simplified approach, maybe we can completely ditch the custom parser defined here (we're not really adding any value with it -- schemaconfigglue will create an empty parse if op is None -- or not passed in).

l. 84: no need to use a tuple for this case, but it's ok
l. 95-96: I'd use the standard patch('module.path') syntax to avoid having to import the module here
l. 101-107: as discussed on irc, with the simplified version, this test make no sense anymore (as all code paths will end up in a SystemExit, disregarding whether an exception was raised or not).
l. 117: we prefer singulars (assertEqual, not assertEquals)

review: Needs Fixing
71. By Vincent Ladeuil

Take review comments into account and move the added test to clarify the resulting diff.

Revision history for this message
Vincent Ladeuil (vila) wrote :

> l. 14: I'd use something like 'Override the base class to handle schema-
> related options.'

Done.

> l. 24: is this really necessary? can't we just omit the param?

Done with better explanations in the comment.

> l. 65: depending on the branch taken by l. 25-34 either configglue_parser or
> parser is not defined
> l. 64-68: this should only be invoked if configglue_parser is defined

No. The diff makes it hard to realize but l. 65 is the else clause for the try so both configglue_parser and parser *are* defined

>
> given the simplified approach, maybe we can completely ditch the custom parser
> defined here (we're not really adding any value with it -- schemaconfigglue
> will create an empty parse if op is None -- or not passed in)

It seems that the LaxOptionParser is still needed there (I haven't dug fully to confirm why but *trying* to rely on the one provided by configglue has a weird side-effect on running the test suite: 6 tests are run instead of 50. I suspect the 'lax' to be required and probably a SystemExit raised by some test otherwise;)

And here we reach a funny paradox: if --verbosity was supported, the guilty test will be obvious, but if we don't use a lax parser, --verbosity is not recognized either :-D

So, I left the lax parser in for this proposal, may be another mp will be more appropriate to dig your suggestion ?

>
> l. 84: no need to use a tuple for this case, but it's ok

Right. It's prophylactic. When I know I will interpolate a single arg, I don't rely on python special casing the expected scalar-or-tuple-or-dict, I remove the ambiguity myself by always passing either a tuple or a dict. I've been bitten in the past by not doing so.

I left it as is, yell if you disagree with the rationale above ;)

> l. 95-96: I'd use the standard patch('module.path') syntax to avoid having to
> import the module here

Done.

> l. 101-107: as discussed on irc, with the simplified version, this test make
> no sense anymore (as all code paths will end up in a SystemExit, disregarding
> whether an exception was raised or not).

Test deleted.

> l. 117: we prefer singulars (assertEqual, not assertEquals)

Done.

72. By Vincent Ladeuil

Not all equals are equal...

73. By Vincent Ladeuil

Simplify the logic, configglue use can be detected more directly.

Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

Neat! Much cleaner

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'django_configglue/management/__init__.py'
--- django_configglue/management/__init__.py 2012-01-13 15:08:52 +0000
+++ django_configglue/management/__init__.py 2012-10-15 14:32:20 +0000
@@ -67,58 +67,22 @@
6767
6868
69class GlueManagementUtility(ManagementUtility):69class GlueManagementUtility(ManagementUtility):
70 # This function was mostly taken from the django project.
71 # Please see the license file in the third-party/django directory.
72 def execute(self):70 def execute(self):
73 """71 """Override the base class to handle the schema-related options. """
74 Given the command-line arguments, this figures out which subcommand is72 configglue_parser = getattr(settings, '__CONFIGGLUE_PARSER__', None)
75 being run, creates a parser appropriate to that command, and runs it.73 if configglue_parser is not None:
76 """74 # We need a lax option parser that:
77 # Preprocess options to extract --settings and --pythonpath.75 # - allows the '%prog subcommand [options] [args]' format
78 # These options could affect the commands that are available, so they76 # - will receive the schema options from configglue
79 # must be processed early.77 # - doesn't attempt to recognize django options
80 parser = LaxOptionParser(usage="%prog subcommand [options] [args]",78 lax_parser = LaxOptionParser(
81 version=django.get_version(),79 usage="%prog subcommand [options] [args]")
82 option_list=BaseCommand.option_list)80 parser, options, args = schemaconfigglue(
83 try:81 configglue_parser, op=lax_parser, argv=self.argv)
84 configglue_parser = settings.__CONFIGGLUE_PARSER__82 utils.update_settings(configglue_parser, settings)
85 parser, options, args = schemaconfigglue(configglue_parser,
86 op=parser, argv=self.argv)
87 # remove schema-related options from the argv list83 # remove schema-related options from the argv list
88 self.argv = args84 self.argv = args
89 utils.update_settings(configglue_parser, settings)85 super(GlueManagementUtility, self).execute()
90 except AttributeError:
91 # no __CONFIGGLUE_PARSER__ found, fall back to standard django
92 # options parsing
93 options, args = parser.parse_args(self.argv)
94 handle_default_options(options)
95 except:
96 # Ignore any option errors at this point.
97 args = self.argv
98
99 try:
100 subcommand = self.argv[1]
101 except IndexError:
102 sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
103 sys.exit(1)
104
105 if subcommand == 'help':
106 if len(args) > 2:
107 self.fetch_command(args[2]).print_help(self.prog_name, args[2])
108 else:
109 parser.print_lax_help()
110 sys.stderr.write(self.main_help_text() + '\n')
111 sys.exit(1)
112 # Special-cases: We want 'django-admin.py --version' and
113 # 'django-admin.py --help' to work, for backwards compatibility.
114 elif self.argv[1:] == ['--version']:
115 # LaxOptionParser already takes care of printing the version.
116 pass
117 elif self.argv[1:] == ['--help']:
118 parser.print_lax_help()
119 sys.stderr.write(self.main_help_text() + '\n')
120 else:
121 self.fetch_command(subcommand).run_from_argv(self.argv)
12286
12387
124# We're going to go ahead and use our own ManagementUtility here, thank you.88# We're going to go ahead and use our own ManagementUtility here, thank you.
12589
=== modified file 'django_configglue/tests/test_configglue.py'
--- django_configglue/tests/test_configglue.py 2012-01-13 01:33:25 +0000
+++ django_configglue/tests/test_configglue.py 2012-10-15 14:32:20 +0000
@@ -352,8 +352,9 @@
352 def test_execute_no_args(self):352 def test_execute_no_args(self):
353 self.util.argv = ['']353 self.util.argv = ['']
354 self.assertRaises(SystemExit, self.execute)354 self.assertRaises(SystemExit, self.execute)
355 self.assertEqual(self.capture['stderr'],355 self.assertIn(
356 "Type '%s help' for usage.\n" % self.util.prog_name)356 "Type '%s help <subcommand>' for help" % (self.util.prog_name,),
357 self.capture['stderr'])
357358
358 def test_execute_help(self):359 def test_execute_help(self):
359 self.util.argv = ['', 'help']360 self.util.argv = ['', 'help']
@@ -395,19 +396,20 @@
395 finally:396 finally:
396 wrapped.__CONFIGGLUE_PARSER__ = old_CONFIGGLUE_PARSER397 wrapped.__CONFIGGLUE_PARSER__ = old_CONFIGGLUE_PARSER
397398
398 @patch('django_configglue.utils.update_settings')
399 def test_execute_configglue_exception(self, mock_update_settings):
400 mock_update_settings.side_effect = Exception()
401
402 self.util.argv = ['', 'help']
403 self.assertRaises(SystemExit, self.execute)
404 self.assertTrue(self.util.main_help_text() in self.capture['stderr'])
405
406 def test_execute_with_schema_options(self):399 def test_execute_with_schema_options(self):
407 self.util.argv = ['', '--django_debug=False', 'help', 'settings']400 self.util.argv = ['', '--django_debug=False', 'help', 'settings']
408 self.execute()401 self.execute()
409 self.assertTrue('Show settings attributes' in self.capture['stdout'])402 self.assertTrue('Show settings attributes' in self.capture['stdout'])
410403
404 def test_verbosity_is_preserved(self):
405 self.util.argv = ['', 'settings', '--verbosity=2']
406 handle_path = ('django_configglue.management.commands.settings.'
407 'Command.handle')
408 with patch(handle_path) as mock_handle:
409 self.execute()
410 args, options = mock_handle.call_args
411 self.assertEqual('2', options['verbosity'])
412
411413
412class LaxOptionParserTestCase(TestCase):414class LaxOptionParserTestCase(TestCase):
413415
414416
=== modified file 'django_configglue/tests/test_settings.py'
--- django_configglue/tests/test_settings.py 2011-09-16 23:10:48 +0000
+++ django_configglue/tests/test_settings.py 2012-10-15 14:32:20 +0000
@@ -212,7 +212,7 @@
212 finally:212 finally:
213 self.end_capture()213 self.end_capture()
214 expected = get_version()214 expected = get_version()
215 self.assertTrue(self.capture['stdout'].count(expected) == 1)215 self.assertEqual(1, self.capture['stdout'].count(expected))
216216
217 def test_noargs_doesnt_error(self):217 def test_noargs_doesnt_error(self):
218 args = ['manage.py']218 args = ['manage.py']

Subscribers

People subscribed via source and target branches