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

Prioritizing full parameter validation before prompting #1369

Closed
sphuber opened this issue Aug 15, 2019 · 3 comments
Closed

Prioritizing full parameter validation before prompting #1369

sphuber opened this issue Aug 15, 2019 · 3 comments
Labels
f:prompt feature: prompt for input

Comments

@sphuber
Copy link
Contributor

sphuber commented Aug 15, 2019

I am faced with the following problem. Imagine the following dummy script:

#!/usr/bin/env python
import click

@click.command()
@click.option('--name', prompt=True)
def main(name):
    click.echo(name)

if __name__ == '__main__':
    main()

When invoked with an unexpected argument, e.g. test.py extra_argument instead of failing straight away, first the prompt is invoked. When the prompt is responded to, only then does the command fail:

Name: test
Usage: test_argument.py [OPTIONS]
Try "test_argument.py --help" for help.

Error: Got unexpected extra argument (extra)

Now for a single option, this is not a big deal. However, in our application we have a command with many options with prompt. If the user accidentally pass an argument, they first have to respond to all the prompts only for the command to error out straight after and they have to start all over again.

Is there any way to prioritize parsing of all parameters before hitting prompts of incomplete ones?

@jraygauthier
Copy link

Prompting (on groups) is also performed even tough user only requests for help to be printed:

$ my-program subgroup command --help
Username: 
# ..
Usage: my-program subgroup command [OPTIONS]
# ..

@heeplr
Copy link

heeplr commented Jul 14, 2021

I believe my issue is related to this:

I have a lot of pompt=True options aswell for my main group and subcommands. When invoking my-app --help no prompt is asked as expected. But if I invoke my-app subcommand --help all prompts from "my-app" are asked before the help of "subcommand" is printed.

Expected behaviour would be to not prompt and just show the "--help" text.

@davidism
Copy link
Member

davidism commented Feb 27, 2022

Here's how Click's processing pipeline works:

  • The command line tokens are parsed, matching known params with values and collecting extra tokens.
  • In the order that params were provided, with eager first and not provided last, each param processes its value from the matched tokens and can modify the list of extra tokens.
  • If the parameter didn't get a value but needs one, other sources such as default, env, and prompt are tried.
  • After all params are processed, if there are any remaining tokens and extra args are not allowed, then the error is raised.

An argument with nargs=-1 isn't required to capture unknown arguments. This is required so that a group can pass on any remaining tokens to a subcommand for further parsing.

The order of processing matters, because each parameter and command can affect the values that later processing sees. Each subcommand does its own parsing after the previous parent command, so you could still end up in a situation where the parent command doesn't have extra args, does a prompt, then the subcommand errors due to an extra arg. This behavior is the same thing that leads to the confusion over --help still requiring valid parent command parameters and prompts.

The request in the title is different that the request in the description as well. The title asks that all parameters that don't require a different source for their value be processed before all that do, specifically with all prompts happening last. That might be doable, but it would be a significant change to the processing pipeline.

The usual advice here is to move prompting to the command callback instead of the parameter parsing. This allows the parsing and processing to finish (and error) first, then allows more complex prompting/processing in user code.

import click

@click.command()
@click.option('--name')
def cli(name):
    if name is None:
        name = click.prompt("Name: ")

    click.echo(name)

cli()

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
f:prompt feature: prompt for input
Projects
None yet
Development

No branches or pull requests

5 participants