Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing required command-line options are reported using post-transformation names #1017

Open
SnoopJ opened this issue Jan 3, 2025 · 0 comments

Comments

@SnoopJ
Copy link

SnoopJ commented Jan 3, 2025

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'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant