Opened 5 weeks ago
Closed 5 weeks ago
#35926 closed New feature (wontfix)
Support capturing the remainder of a command-line arguments in BaseCommand
Reported by: | Daniel Quinn | Owned by: | |
---|---|---|---|
Component: | Core (Management commands) | Version: | dev |
Severity: | Normal | Keywords: | unknown args |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I wanted to write a management command that would accept a few known arguments and then capture the remaining arbitrary arguments passed as a separate value. For example:
./manage.py mycommand --expected=argument --something-arbitrary -x -y -z
So --expected-argument
would be defined in .add_arguments()
, but the other args would just be available as something like options["remainder"]
or whatever.
This is usually done by calling parse_known_args()
rather than the more common .parse_args()
, but unfortunately there's currently no way to cleanly indicate this with BaseCommand. The only way seems to be to copy/paste the entirety of BaseCommand.run_from_argv()
and then change where.parse_args()
is invoked:
def run_from_argv(self, argv): """ Copypasta from the parent class, so I can change `.parse_args()` to `.parse_known_args()`. """ self._called_from_command_line = True parser = self.create_parser(argv[0], argv[1]) # Change -------------------------------------------------------------- options, remainder = parser.parse_known_args(argv[2:]) cmd_options = vars(options) cmd_options["remainder"] = remainder # /Change ------------------------------------------------------------- # Move positional args out of options to mimic legacy optparse args = cmd_options.pop("args", ()) handle_default_options(options) try: self.execute(*args, **cmd_options) except CommandError as e: if options.traceback: raise # SystemCheckError takes care of its own formatting. if isinstance(e, SystemCheckError): self.stderr.write(str(e), lambda x: x) else: self.stderr.write("%s: %s" % (e.__class__.__name__, e)) sys.exit(e.returncode) finally: try: connections.close_all() except ImproperlyConfigured: # Ignore if connections aren't setup at this point (e.g. no # configured settings). pass
Obviously that's not ideal.
One option might be to just use .parse_known_args()
and then allow the user to indicate whether they want to capture the remainder or not, and if so, as what attribute, but I have no strong feelings about implementation.
Change History (1)
comment:1 by , 5 weeks ago
Keywords: | unknown args added; management command remainder removed |
---|---|
Resolution: | → wontfix |
Status: | new → closed |
Version: | 5.1 → dev |
Hello Daniel, thank you for taking the time to create this ticket. I see two sides in your request:
Side A
As a Django Fellow, I think that the provided example seems a very specific need arising from a niche use case. I don't think this applies to the broader ecosystem, and Django is a framework designed to offer robust and accurate solutions for common scenarios. Furthermore, as a seasoned developer, I think that allowing any amount of unknown named params in a command is a bad programming pattern, so I would advice against that pattern.
What you could do instead, is to allow any number of arguments associated with a single argument name that is defined in your parser, something similar to what the docs shows for custom management commands when defining nargs for
polls_ids
:(Yes, this limits the type of the nargs but I think that is a good thing!)
Side B
Assuming that we consider the provided use case and want to build a solution, I think the best approach would be, in your code, to monkeypatch
CommandParser
to be replaced with a custom class that reimplementsparse_args
and stores the unknown args to do something with them when needed:django/core/management/base.py
Given the above, I'll close the ticket accordingly, but if you disagree, you can consider starting a new conversation on the Django Forum, where you'll reach a wider audience and likely get extra feedback. More information in the documented guidelines for requesting features.