Opened 6 years ago

Closed 6 years ago

#29295 closed Bug (fixed)

BaseCommand.add_arguments crashes when using parser.add_subparsers().add_parser("subcommand")

Reported by: Lutz Prechelt Owned by: Lutz Prechelt
Component: Core (Management commands) Version: 1.11
Severity: Normal Keywords: BaseCommand argparse subparser add_subparsers add_parser
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Background

Python's argparse allows to 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.)

Attachments (1)

29295.diff (2.8 KB ) - added by Tim Graham 6 years ago.

Download all attachments as: .zip

Change History (6)

comment:1 by Tim Graham, 6 years ago

Summary: BaseCommand.add_arguments does not allow parser.add_subparsers().add_parser("subcommand")BaseCommand.add_arguments crashes when using parser.add_subparsers().add_parser("subcommand")
Triage Stage: UnreviewedAccepted

While investigating the issue, I wrote a patch that I think should solve the issue. What remains is to add a test, probably in tests/user_commands. What you like to try that?

by Tim Graham, 6 years ago

Attachment: 29295.diff added

comment:2 by Lutz Prechelt, 6 years ago

Yes, will try.
I have started to work my way into Django development (the new-developer docs appear to be very good), but have a lot of the way left to go. Will take a while, but I'll get to it eventually.

comment:3 by Lutz Prechelt, 6 years ago

Owner: changed from nobody to Lutz Prechelt
Status: newassigned

comment:4 by Tim Graham, 6 years ago

Has patch: set
Triage Stage: AcceptedReady for checkin

comment:5 by Tim Graham <timograham@…>, 6 years ago

Resolution: fixed
Status: assignedclosed

In dd68b51e:

Fixed #29295 -- Fixed management command crash when using subparsers.

Thanks Tim Graham for the fix.

Note: See TracTickets for help on using tickets.
Back to Top