Missing required command-line options are reported using post-transformation names
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 45/100
Research direction
Start by tracing the parser entry points shown in the debug output, especially invoke.parser.init, parse_argv, complete_context, and invoke.program.run. Reproduce the missing-argument error and verify that its reported names match the command-line option spelling, including hyphens where applicable.
Written by the indexing model from the issue text.
Description
I noticed that when a command-line "option" is required, but not given in the task invocation, the reported names use underscores instead of hyphens.
This isn't a huge deal, but it does present a bit of an impediment to a user responding to what is wrong. If I saw that error message without existing knowledge of invoke, I would think that I need to add e.g. --param_1 asdf to my command-line. A straightforward workaround would be to assign a default value, test for it, and raise an appropriate error from the body of the task.
It would also be helpful to have the - or -- prefix in this case, but I imagine this is a harder problem to solve since not all ways to enter the task are necessarily from the command-line? Anyway, I couldn't find an existing report for this, so I figured I'd file one.
Reproduction
$ cat tasks.py
from invoke import task
@task
def dummy(ctx, param_1, param_2):
print(f"Got {param_1=} {param_2=}")
$ invoke dummy --param-1 asdf --param-2 1234
Got param_1='asdf' param_2='1234'
$ invoke dummy
'dummy' did not receive required positional arguments: 'param_1', 'param_2'
click for `invoke --debug dummy` output
$ invoke --debug dummy
invoke.program.parse_collection: No default namespace provided, trying to load one from disk
invoke.loader.find: FilesystemLoader find starting at '/tmp'
invoke.loader.find: Found module: ModuleSpec(name='tasks', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7c00493c8bb0>, origin='/tmp/tasks.py')
invoke.config._load_file: Didn't see any /tmp/invoke.yaml, skipping.
invoke.config._load_file: Didn't see any /tmp/invoke.yml, skipping.
invoke.config._load_file: Didn't see any /tmp/invoke.json, skipping.
invoke.config.merge: Merging config sources in order onto new empty _config...
invoke.config.merge: Defaults: {'run': {'asynchronous': False, 'disown': False, 'dry': False, 'echo': False, 'echo_stdin': None, 'encoding': None, 'env': {}, 'err_stream': None, 'fallback': True, 'hide': None, 'in_stream': None, 'out_stream': None, 'echo_format': '\x1b[1;37m{command}\x1b[0m', 'pty': False, 'replace_env': False, 'shell': '/bin/bash', 'warn': False, 'watchers': []}, 'runners': {'local': <class 'invoke.runners.Local'>}, 'sudo': {'password': None, 'prompt': '[sudo] password: ', 'user': None}, 'tasks': {'auto_dash_names': True, 'collection_name': 'tasks', 'dedupe': True, 'executor_class': None, 'ignore_unknown_help': False, 'search_root': None}, 'timeouts': {'command': None}}
invoke.config.merge: Collection-driven: {}
invoke.config._merge_file: System-wide config file (/etc/invoke.py): {}
invoke.config._merge_file: Per-user config file (/home/jgerity/.invoke.py): {}
invoke.config._merge_file: Per-project config file (/tmp/invoke.py): {}
invoke.config.merge: Environment variable config: {}
invoke.config._merge_file: Runtime config file has not been loaded yet, skipping
invoke.config.merge: Overrides: {}
invoke.config.merge: Modifications: {}
invoke.config.merge: Deletions: {}
invoke.parser.__init__: Adding <parser/Context 'dummy': {'param-1': <Argument: param_1 (p) *>, 'param-2': <Argument: param_2 (a) *>}>
invoke.program.parse_tasks: Parsing tasks against <Collection 'tasks': dummy>
invoke.parser.__init__: Initialized with context: <parser/Context: {'command-timeout': <Argument: command-timeout (T) [int]>, 'complete': <Argument: complete [bool]>, 'config': <Argument: config (f)>, 'debug': <Argument: debug (d) [bool]>, 'dry': <Argument: dry (R) [bool]>, 'echo': <Argument: echo (e) [bool]>, 'help': <Argument: help (h) ?>, 'hide': <Argument: hide>, 'list': <Argument: list (l) ?>, 'list-depth': <Argument: list-depth (D) [int]>, 'list-format': <Argument: list-format (F)>, 'print-completion-script': <Argument: print-completion-script>, 'prompt-for-sudo-password': <Argument: prompt-for-sudo-password [bool]>, 'pty': <Argument: pty (p) [bool]>, 'version': <Argument: version (V) [bool]>, 'warn-only': <Argument: warn-only (w) [bool]>, 'write-pyc': <Argument: write-pyc [bool]>, 'collection': <Argument: collection (c)>, 'no-dedupe': <Argument: no-dedupe [bool]>, 'search-root': <Argument: search-root (r)>}>
invoke.parser.__init__: Available contexts: {'dummy': <parser/Context 'dummy': {'param-1': <Argument: param_1 (p) *>, 'param-2': <Argument: param_2 (a) *>}>}
invoke.parser.complete_context: Wrapping up context None
invoke.parser.parse_argv: Starting argv: ['dummy']
invoke.parser.handle: Handling token: 'dummy'
invoke.parser.changing_state: ParseMachine: 'context' => 'context'
invoke.parser.complete_context: Wrapping up context None
invoke.parser.switch_to_context: Moving to context 'dummy'
invoke.parser.switch_to_context: Context args: {'param-1': <Argument: param_1 (p) *>, 'param-2': <Argument: param_2 (a) *>}
invoke.parser.switch_to_context: Context flags: {'--param-1': <Argument: param_1 (p) *>, '--param-2': <Argument: param_2 (a) *>}
invoke.parser.switch_to_context: Context inverse_flags: {}
invoke.parser.changing_state: ParseMachine: 'context' => 'end'
invoke.parser.complete_context: Wrapping up context 'dummy'
invoke.program.run: Received a possibly-skippable exception: ParseError("'dummy' did not receive required positional arguments: 'param_1', 'param_2'")
'dummy' did not receive required positional arguments: 'param_1', 'param_2'
- Dominant language
- Python
- Stars
- 4.8k
- Forks
- 412
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pyinvoke/invoke
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Similar issues
-
documentation help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
simonw/sqlite-utils#872 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100