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

Default value for an option is always there even if flag isn't used #1394

Closed
simonwep opened this issue Nov 10, 2020 · 9 comments
Closed

Default value for an option is always there even if flag isn't used #1394

simonwep opened this issue Nov 10, 2020 · 9 comments

Comments

@simonwep
Copy link

First of all, great library!

Consider this code:

const {program} = require('commander');

program
    .option('-p, --pizza [type]', 'Pizza name', 'pepperoni')
    .parse(process.argv)


console.log({pizza: program.pizza});

Usage:

$ script.js 
# Output: { pizza: 'pepperoni' }

$ script.js --pizza
# Output: { pizza: 'pepperoni' }

$ script.js --pizza salami
# Output: { pizza: 'salami' }

Is there a way to see if the --pizza flag is actually used? Currently the default-value is always present and used, even if the flag itself is not even present in the arguments. This makes it impossible to pre-process values and making the flag optional as at this point the flag is neither optional or mandatory, it is always there, even if not specified.

Related: #440 #1036

Do I miss something or how can I achieve that using commanderjs?

@shadowspawn
Copy link
Collaborator

Related: #1135

@shadowspawn
Copy link
Collaborator

shadowspawn commented Nov 11, 2020

You didn't miss something. If you need to be able to tell if the option was on the command-line, don't specify a default value for the option. You can then handle the value for "missing" and "optional" option in code.

I am not sure what you have in mind with "pre-process values"? If you can't see how to achieve what you want, would you please explain a little more about your desired workflow?

@simonwep
Copy link
Author

Thank you for the quick reply! I ended up doing the following as suggested:

program
    [...]
    .option('-p, --prettify [number|tab]', 'Prettify files (default: 4 spaces)', parseIndentation)
    .option('--duplicates [strict|loose]', 'Find duplicates (default: loose)', parseMode('--duplicates'))
    .option('--diff [strict|loose]', 'Find differences and conflicts (default: strict)', parseMode('--diff'))
    .action((args, cmd) => {

        // Inject fallback values, true will be an option without a value specified
        cmd.prettify = cmd.prettify === true ? 4 : cmd.prettify;
        cmd.duplicates = cmd.duplicates === true ? 'loose' : cmd.duplicates;
        cmd.diff = cmd.diff === true ? 'strict' : cmd.diff;

        ...
    })
    .parse();

parseMode and parseIndentation are rather booring, this solution just seems rather repetitive to me and I also had to manually add a default-value-hint for the user to the option-description.

Now, let's take --diff as example, we get the following results:

  1. script: cmd.diff is undefined, not set / used. (This would be strict with strict as default value)
  2. script --diff: cmd.diff is strict as default value.
  3. script --diff loose: cmd.diff is loose as the value is specified.

What I could do is adding a --...-mode option for --diff and --duplicates and a --indentation for --prettify. But why adding more options if you can use the flag itself and the value you need to pass is mandatory information.

Sorry if my first command did not properly explain my use-case, hope this cleared things up a little bit. The code snipped is taken from here.

@shadowspawn
Copy link
Collaborator

shadowspawn commented Nov 12, 2020

That is the two approaches I had in mind: handling the boolean option usage in code, or splitting the boolean and value uses of the option into two options.

(Thanks for expanding!)

@shadowspawn
Copy link
Collaborator

I have wondered about changing the default value behaviour for boolean|value option type, but it has not come up often so far to justify what would be a big breaking change. This option type was used in the examples through Commander v2, even in cases where probably the boolean usage was not even intended, and the missing flag cases would change behaviour.

@shadowspawn
Copy link
Collaborator

shadowspawn commented Dec 13, 2020

argparse has separate values for the "default" option value, and the "const" value used when the option is specified on the command-line without an option-value. I think making the two purposes separate like this is the non-breaking way forward.

https://docs.python.org/2.7/library/argparse.html#nargs

'?'. One argument will be consumed from the command line if possible, and produced as a single item. If no command-line argument is present, the value from default will be produced. Note that for optional arguments, there is an additional case - the option string is present but not followed by a command-line argument. In this case the value from const will be produced.

@shadowspawn
Copy link
Collaborator

A work-around was reached, and no further activity in a month. Closing this as resolved.

Feel free to open a new issue if it comes up again, with new information and renewed interest.

@shadowspawn
Copy link
Collaborator

shadowspawn commented Dec 19, 2021

I discovered recently that options with optional arguments are actually broken when there is a default value, making the requested behaviour difficult to achieve. The option is ignored when supplied without a value.

I have PR #1652 open to fix that, and add .preset(), which works well for the requested behaviour.

program
  .addOption(new Option('-p, --pizza [type]'', 'Pizza name').preset('pepperoni'));

@shadowspawn
Copy link
Collaborator

Is there a way to see if the --pizza flag is actually used?

Another related feature added after the original question, Commander v8.3 added .getOptionValueSource().

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

2 participants