#33205 closed Bug (fixed)
call_command() fails when required mutually exclusive arguments use the same `dest`.
Reported by: | Peter Law | Owned by: | Hasan Ramezani |
---|---|---|---|
Component: | Core (Management commands) | Version: | 3.2 |
Severity: | Normal | Keywords: | |
Cc: | Hasan Ramezani | 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
I have a command which accepts two different ways to specify a time -- either as a timestamp or as a duration in the future:
pause (--for duration | --until time)
class Command(BaseCommand): def add_arguments(self, parser) -> None: group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--for', dest='until', action='store', type=parse_duration_to_time) group.add_argument('--until', action='store', type=parse_time) def handle(self, until: datetime, **_): pass
This works fine on the command line, however there doesn't seem to be a way to make this work through call_command
. Specifically there are two sides to the failure:
- while I can provide an
until
value (as a string, which is processed byparse_time
) there is no mechanism to pass afor
value if that's how I want to spell the input - the
for
value is always required and attempts to parse the (string)until
value passed, which then errors since the input formats are very different
Change History (11)
comment:1 by , 3 years ago
Cc: | added |
---|---|
Component: | Uncategorized → Core (Management commands) |
Summary: | call_command fails when required mutually exclusive group arguments use the same `dest` → call_command() fails when required mutually exclusive arguments use the same `dest`. |
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
follow-up: 4 comment:2 by , 3 years ago
I can create a patch to fix the two above-mentioned issues but using the command with both options together:
management.call_command('pause', **{'for': '1', 'util': '1'})
won't raise an error because both options use the same dest
. I will investigate it more.
I don't know do we have to support passing both dest and arg name as keyword arguments? in the example of this ticket, if we call management.call_command('pause', until='1')
, it should be considered as until
arg or for
(because dest
of for
is until
as well)
comment:3 by , 3 years ago
Ah, interesting, I wasn't aware that those other spellings worked! I'd be happy to switch to using those spellings instead (I've confirmed they work for my original case).
Perhaps just documenting this limitation (and the fact that those spellings work as alternatives) and/or improving the related error messages could be a way to go here?
follow-up: 5 comment:4 by , 3 years ago
Replying to Hasan Ramezani:
I don't know do we have to support passing both dest and arg name as keyword arguments? in the example of this ticket, if we call
management.call_command('pause', until='1')
, it should be considered asuntil
arg orfor
(becausedest
offor
isuntil
as well)
We should support option names as documented:
** options
named options accepted on the command-line.
so
management.call_command('pause', until='1')
should work the same as callingpause --until 1
management.call_command('pause', **{'for': '1'})
should work the same as callingpause --for 1
management.call_command('pause', **{'for': '1', 'until': '1'})
should work the same as callingpause --for 1 --until 1
and raise an exception
comment:5 by , 3 years ago
Replying to Mariusz Felisiak:
I am not sure about your second example:
management.call_command('pause', **{'for': '1'})
should work the same as callingpause --for 1
Based on the documentation, it seems we have to pass dest
as keyword argument name when we define dest
for arguments.
Some command options have different names when using call_command() instead of django-admin or manage.py. For example, django-admin createsuperuser --no-input translates to call_command('createsuperuser', interactive=False). To find what keyword argument name to use for call_command(), check the command’s source code for the dest argument passed to parser.add_argument().
Also, when Django adds required arguments in call command, it search for dest
in options.
comment:6 by , 3 years ago
You're right, sorry, I missed "... check the command’s source code for the dest argument passed to parser.add_argument().". In that case I would raise an error that passing dest
with multiple arguments via **options
is not supported.
comment:7 by , 3 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:9 by , 3 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
Thanks for the report. The following calls work as expected for me :
however I confirmed an issue when passing arguments in keyword arguments:
This is caused by using
dest
for mapping**options
to arguments, see call_command().