Opened 4 months ago

Closed 3 months ago

Last modified 3 months ago

#33041 closed Bug (invalid)

call_command() returns different values for options than manage.py when nargs is specified.

Reported by: Kevin Follstad Owned by: nobody
Component: Core (Management commands) Version: 3.1
Severity: Normal Keywords: call_command nargs
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Maybe I am not understanding something in the documentation, but this seems like a bug (tested on 3.1.7).

When nargs is specified for an option, according to the argparse documentation, the parsed option should always be a list containing the option's values. However that is not the case if options are specified as kw="the-kw-option" when passed to call_command. For example:

main/test/management/commands/commandbug.py:

from django.core.management.base import BaseCommand
from django.core.management import call_command

# Broken: kw is not a list
def call_commandbug1():
    call_command("commandbug", "the-positional-arg", kw="the-kw-arg")

# Works: kw is a list
def call_commandbug2():
    call_command("commandbug", "the-positional-arg", "--kw=the-kw-arg")


class Command(BaseCommand):
    help = "Minimal call_command bug demo"

    def add_arguments(self, parser):
        parser.add_argument(
            "positional", nargs=1, type=str, help="Positional arg"
        )
        parser.add_argument(
            "--kw",
            nargs=1,
            type=str,
            help="Keyword arg",
        )
        super().add_arguments(parser)

    def handle(self, *args, **options):
        print(options)

When run from call_command1 the value of kw is not a list:

$> ./manage.py shell
Python 3.9.6 (default, Jun 30 2021, 10:22:16) 
In [1]: from main.test.management.commands.commandbug import call_commandbug1, call_commandbug2
In [2]: call_commandbug1()
{'verbosity': 1, 'settings': None, 'pythonpath': None, 'traceback': False, 'no_color': False, 'force_color': False, 'skip_checks': True, 'positional': ['the-positional-arg'], 'kw': 'the-kw-arg'}

When run from call_command2, the value is kw is a list matching argparse:

In [3]: call_commandbug2()
{'verbosity': 1, 'settings': None, 'pythonpath': None, 'traceback': False, 'no_color': False, 'force_color': False, 'skip_checks': True, 'positional': ['the-positional-arg'], 'kw': ['the-kw-arg']}

When run via manage.py it also works as expected / per argparse:

$> ./manage.py commandbug "the-positional-arg" --kw "the-kw-arg"
{'verbosity': 1, 'settings': None, 'pythonpath': None, 'traceback': False, 'no_color': False, 'force_color': False, 'skip_checks': False, 'positional': ['the-positional-arg'], 'kw': ['the-kw-arg']}

Hopefully this makes sense, and is a useful observation.
On an unrelated note, thanks for all you do! Django has been a life changing framework for me.

Change History (3)

comment:1 Changed 4 months ago by Kevin Follstad

Component: UncategorizedCore (Management commands)

comment:2 Changed 3 months ago by Mariusz Felisiak

Resolution: invalid
Status: newclosed

Thanks for the ticket, however it's documented that named options are passed without "triggering the argument parser, which means you’ll need to pass the correct type".

On an unrelated note, thanks for all you do! Django has been a life changing framework for me.

💗

comment:3 Changed 3 months ago by Mariusz Felisiak

Summary: Call_command returns different values for options than manage.py (and itself) when nargs is specifiedcall_command() returns different values for options than manage.py when nargs is specified.
Note: See TracTickets for help on using tickets.
Back to Top