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
1=== modified file 'django_configglue/management/__init__.py'
2--- django_configglue/management/__init__.py 2012-01-13 15:08:52 +0000
3+++ django_configglue/management/__init__.py 2012-10-15 14:32:20 +0000
4@@ -67,58 +67,22 @@
5
6
7 class GlueManagementUtility(ManagementUtility):
8- # This function was mostly taken from the django project.
9- # Please see the license file in the third-party/django directory.
10 def execute(self):
11- """
12- Given the command-line arguments, this figures out which subcommand is
13- being run, creates a parser appropriate to that command, and runs it.
14- """
15- # Preprocess options to extract --settings and --pythonpath.
16- # These options could affect the commands that are available, so they
17- # must be processed early.
18- parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
19- version=django.get_version(),
20- option_list=BaseCommand.option_list)
21- try:
22- configglue_parser = settings.__CONFIGGLUE_PARSER__
23- parser, options, args = schemaconfigglue(configglue_parser,
24- op=parser, argv=self.argv)
25+ """Override the base class to handle the schema-related options. """
26+ configglue_parser = getattr(settings, '__CONFIGGLUE_PARSER__', None)
27+ if configglue_parser is not None:
28+ # We need a lax option parser that:
29+ # - allows the '%prog subcommand [options] [args]' format
30+ # - will receive the schema options from configglue
31+ # - doesn't attempt to recognize django options
32+ lax_parser = LaxOptionParser(
33+ usage="%prog subcommand [options] [args]")
34+ parser, options, args = schemaconfigglue(
35+ configglue_parser, op=lax_parser, argv=self.argv)
36+ utils.update_settings(configglue_parser, settings)
37 # remove schema-related options from the argv list
38 self.argv = args
39- utils.update_settings(configglue_parser, settings)
40- except AttributeError:
41- # no __CONFIGGLUE_PARSER__ found, fall back to standard django
42- # options parsing
43- options, args = parser.parse_args(self.argv)
44- handle_default_options(options)
45- except:
46- # Ignore any option errors at this point.
47- args = self.argv
48-
49- try:
50- subcommand = self.argv[1]
51- except IndexError:
52- sys.stderr.write("Type '%s help' for usage.\n" % self.prog_name)
53- sys.exit(1)
54-
55- if subcommand == 'help':
56- if len(args) > 2:
57- self.fetch_command(args[2]).print_help(self.prog_name, args[2])
58- else:
59- parser.print_lax_help()
60- sys.stderr.write(self.main_help_text() + '\n')
61- sys.exit(1)
62- # Special-cases: We want 'django-admin.py --version' and
63- # 'django-admin.py --help' to work, for backwards compatibility.
64- elif self.argv[1:] == ['--version']:
65- # LaxOptionParser already takes care of printing the version.
66- pass
67- elif self.argv[1:] == ['--help']:
68- parser.print_lax_help()
69- sys.stderr.write(self.main_help_text() + '\n')
70- else:
71- self.fetch_command(subcommand).run_from_argv(self.argv)
72+ super(GlueManagementUtility, self).execute()
73
74
75 # We're going to go ahead and use our own ManagementUtility here, thank you.
76
77=== modified file 'django_configglue/tests/test_configglue.py'
78--- django_configglue/tests/test_configglue.py 2012-01-13 01:33:25 +0000
79+++ django_configglue/tests/test_configglue.py 2012-10-15 14:32:20 +0000
80@@ -352,8 +352,9 @@
81 def test_execute_no_args(self):
82 self.util.argv = ['']
83 self.assertRaises(SystemExit, self.execute)
84- self.assertEqual(self.capture['stderr'],
85- "Type '%s help' for usage.\n" % self.util.prog_name)
86+ self.assertIn(
87+ "Type '%s help <subcommand>' for help" % (self.util.prog_name,),
88+ self.capture['stderr'])
89
90 def test_execute_help(self):
91 self.util.argv = ['', 'help']
92@@ -395,19 +396,20 @@
93 finally:
94 wrapped.__CONFIGGLUE_PARSER__ = old_CONFIGGLUE_PARSER
95
96- @patch('django_configglue.utils.update_settings')
97- def test_execute_configglue_exception(self, mock_update_settings):
98- mock_update_settings.side_effect = Exception()
99-
100- self.util.argv = ['', 'help']
101- self.assertRaises(SystemExit, self.execute)
102- self.assertTrue(self.util.main_help_text() in self.capture['stderr'])
103-
104 def test_execute_with_schema_options(self):
105 self.util.argv = ['', '--django_debug=False', 'help', 'settings']
106 self.execute()
107 self.assertTrue('Show settings attributes' in self.capture['stdout'])
108
109+ def test_verbosity_is_preserved(self):
110+ self.util.argv = ['', 'settings', '--verbosity=2']
111+ handle_path = ('django_configglue.management.commands.settings.'
112+ 'Command.handle')
113+ with patch(handle_path) as mock_handle:
114+ self.execute()
115+ args, options = mock_handle.call_args
116+ self.assertEqual('2', options['verbosity'])
117+
118
119 class LaxOptionParserTestCase(TestCase):
120
121
122=== modified file 'django_configglue/tests/test_settings.py'
123--- django_configglue/tests/test_settings.py 2011-09-16 23:10:48 +0000
124+++ django_configglue/tests/test_settings.py 2012-10-15 14:32:20 +0000
125@@ -212,7 +212,7 @@
126 finally:
127 self.end_capture()
128 expected = get_version()
129- self.assertTrue(self.capture['stdout'].count(expected) == 1)
130+ self.assertEqual(1, self.capture['stdout'].count(expected))
131
132 def test_noargs_doesnt_error(self):
133 args = ['manage.py']

Subscribers

People subscribed via source and target branches