What can be going on with your custom management commands in Django 1.10
If you update a relatively old Django project from 1.9 to 1.10 or
later and you have added your own custom management commands
that take positional arguments, it's possible that those commands
will abruptly stop working (well, stop accepting positional arguments).
Also, although it is not explicitly documented, the *args
parameter of your Command's handle()
function is now often
completely meaningless and you won't see anything passed in it
(although when this happens is fairly obscure).
If you were still using the optparse
based approach to argument
parsing in your custom management commands, this is sort of expected;
the 1.10 release notes mention,
in the large 'features removed in 1.10' section, that 'support for
optparse
is dropped for custom management commands' (and you'll
have been getting deprecation warnings about that in 1.9). However
this happens even if you had already switched over to using argparse
based argument parsing (ie, your custom management command class
has an add_arguments
function). Your code worked fine in Django
1.9 and failed in Django 1.10, despite no deprecation warning.
So, here is what is going on. In Django 1.9 and earlier, code in
django.core.management.base's BaseCommand class hierarchy silently
introspected your command class to see if it had an args
member. If it
did and you were already using argparse
, BaseCommand silently added
an extra args
argument to your argument parser that swept up all
positional arguments:
if not self.use_argparse: ... old optparse code ... else: .... if self.args: # Keep compatibility and always accept # positional arguments, like optparse when # args is set parser.add_argument('args', nargs='*')
Later, other code in BaseCommand would silently take the value of
the args
argument from argparse
's results and turn it into the
args
parameter for your handle()
function (removing it from
the argparse results in the process):
# Move positional args out of options to # mimic legacy optparse args = cmd_options.pop('args', ())
Note that this specifically happened when you had switched over to
using argparse
. That this catch-all argument was added (without real
documentation) meant that in Django 1.9, if you still had an args
member, you could not add your own argument to collect some or all of
the positional arguments because it would clash with the automatically
added argument here. And really confusing things might happen if you
called some argparse
argument 'args', because it would be stolen as
the args
parameter for your handle()
function.
In Django 1.10, the first chunk of this code was changed so that an
extra args
argument is no longer automatically added to your argument
parser if you still had an args
member in your command class. Now
such custom management commands could not get their extra positional
arguments (and I believe would fail with a 'cannot parse command line
arguments' error if you tried to supply some). However, watch out,
because the second chunk of code is still there. If you have an argparse
argument called 'args
', Django will silently remove it from the
argparse result and pass it to your handle()
command as the args
parameter.
(Of course this is kind of a feature. As it stands now, you can just add
your own copy of the old Django 1.9 parser.add_argument
call and
nothing else in your code has to change. But it smells like a hack to me
and I wouldn't be surprised if Django made this magic behavior disappear
at some point.)
In my opinion, this automatic addition of an args
argument should
have been explicitly deprecated, meaning both a deprecation warning
in Django 1.9 and an explicit mention in Django 1.10's documentation.
Probably the magic behavior of converting an args
argparse argument
in the handle()
args
parameter should have been deprecated at the
same time, but I don't have strong opinions.
(This is the kind of entry I write because we stumbled over this and then I went and dug the details out of the Django revision history, so I'm certainly not going to waste all that work.)
|
|