#29295: BaseCommand.add_arguments does not allow
parser.add_subparsers().add_parser("subcommand")
-------------------------------------+-------------------------------------
Reporter: Lutz | Owner: nobody
Prechelt |
Type: Bug | Status: new
Component: Core | Version: 1.11
(Management commands) | Keywords: BaseCommand
Severity: Normal | argparse subparser add_subparsers
Triage Stage: | add_parser
Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
== Background
Python's [https://docs.python.org/3/library/argparse.html argparse] allows
to [https://docs.python.org/3/library/argparse.html?highlight=argparse
#sub-commands define subparsers].
The following works (but does nothing observable) in plain Python:
{{{
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
a_parser = subparsers.add_parser("A")
}}}
== Setup
The equivalent does ''not'' work in a `manage.py` custom command.
When I put this in file **`mycommand.py`**:
{{{
import django.core.management.base as djcmb
class Command(djcmb.BaseCommand):
def add_arguments(self, parser):
subparsers = parser.add_subparsers()
a_parser = subparsers.add_parser("A")
}}}
== Symptom
...and then run **`python manage.py mycommand A`**,
what I get is `TypeError: __init__() missing 1 required positional
argument: 'cmd'` with the following stacktrace:
{{{
File "manage.py", line 8, in <module>
execute_from_command_line(sys.argv)
File "C:\venv\rqc36\lib\site-
packages\django\core\management\__init__.py", line 364, in
execute_from_command_line
utility.execute()
File "C:\venv\rqc36\lib\site-
packages\django\core\management\__init__.py", line 356, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\venv\rqc36\lib\site-packages\django\core\management\base.py",
line 275, in run_from_argv
parser = self.create_parser(argv[0], argv[1])
File "C:\venv\rqc36\lib\site-packages\django\core\management\base.py",
line 249, in create_parser
self.add_arguments(parser)
File "C:\ws\bb\rqc\rqc\management\commands\mycommand.py", line 7, in
add_arguments
a_parser = subparsers.add_parser("A")
File "C:\sw\Python36-32\lib\argparse.py", line 1097, in add_parser
parser = self._parser_class(**kwargs)
}}}
== Diagnosis
The reason is that Django does not use a plain `ArgumentParser` but rather
its own `django.core.management.base.CommandParser`, the constructor of
which requires a positional argument:
{{{
def __init__(self, cmd, **kwargs): # ...
}}}
where `cmd` is supposed to contain the `mycommand.Command` object which is
then stored as `self.cmd` in the parser.
The following **kludge** helps the poor user trying to get subparsers to
work:
{{{
import argparse
import django.core.management.base as djcmb
class Command(djcmb.BaseCommand):
def add_arguments(self, parser):
subparsers = parser.add_subparsers()
subparsers._parser_class = argparse.ArgumentParser # circumvent
Django 1.11 bug
a_parser = subparsers.add_parser("A")
}}}
== Repair suggestions
**S1**: The most straightforward repair I see would be to make the
argument optional (`*argv`) and use a dummy object for `self.cmd` in case
of subparsers. `self.cmd` is used only three times. Is this good enough?
**S2**: Alternatively, one could simply document the problem and the
kludge. (BTW: The custom commands how-to should become more explicit
regarding the use of `argparse`; it is currently only hinted at, never
mentioned.)
--
Ticket URL: <https://code.djangoproject.com/ticket/29295>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/051.cf6f2493e64fc81ff1935538d090b7f8%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.