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

Global arguments with a requires clause don't work under subcommands when specified through environment variables #5020

Open
2 tasks done
Xophmeister opened this issue Jul 19, 2023 · 1 comment
Labels
C-bug Category: Updating dependencies

Comments

@Xophmeister
Copy link

Xophmeister commented Jul 19, 2023

Please complete the following tasks

Rust Version

rustc 1.70.0 (90c541806 2023-05-31) (built from a source tarball)

Clap Version

4.3.13

Minimal reproducible code

use clap::{Parser, Subcommand};

#[derive(Parser, Debug)]
struct TestCli {
    #[arg(short, env = "FOO", global = true)]
    foo: Option<String>,

    #[arg(short, env = "BAR", requires = "foo", global = true)]
    bar: Option<String>,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Debug, Subcommand)]
enum Commands {
    Quux,
}

fn main() {
    let cli = TestCli::parse();
    println!("{:?}", cli);
}

Steps to reproduce the bug with the above code

BAR=123 cargo run -- quux -f abc

Actual Behaviour

Complains about the -f FOO argument being missing, even though it isn't:

error: the following required arguments were not provided:
  -f <FOO>

Usage: example quux -f <FOO> -b <BAR>

Expected Behaviour

Outputs the following:

TestCli { global: GlobalArgs { foo: Some("abc"), bar: Some("123") }, command: Quux }

Additional Context

This only appears to happen within the subcommand. If the subcommands are optional, this works as expected (but only at the top-level, when no subcommand is specified):

BAR=123 cargo run -- -f abc

With non-optional subcommands, it does work as expected if the required argument is also specified through its environment variable:

FOO=abc BAR=123 cargo run -- quux

Debug Output

[clap_builder::builder::command]Command::_do_parse
[clap_builder::builder::command]Command::_build: name="example"
[clap_builder::builder::command]Command::_propagate:example
[clap_builder::builder::command]Command::_check_help_and_version:example expand_help_tree=false
[clap_builder::builder::command]Command::long_help_exists
[clap_builder::builder::command]Command::_check_help_and_version: Building default --help
[clap_builder::builder::command]Command::_check_help_and_version: Building help subcommand
[clap_builder::builder::command]Command::_propagate_global_args:example
[clap_builder::builder::command]Command::_propagate pushing "foo" to quux
[clap_builder::builder::command]Command::_propagate pushing "bar" to quux
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:foo
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:bar
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:help
[clap_builder::builder::debug_asserts]Command::_verify_positionals
[clap_builder::parser::parser]Parser::get_matches_with
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"quux"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("quux")
[clap_builder::parser::parser]Parser::get_matches_with: sc=Some("quux")
[clap_builder::parser::parser]Parser::parse_subcommand
[ clap_builder::output::usage]Usage::get_required_usage_from: incls=[], matcher=false, incl_last=true
[ clap_builder::output::usage]Usage::get_required_usage_from: unrolled_reqs=[]
[ clap_builder::output::usage]Usage::get_required_usage_from: ret_val=[]
[clap_builder::builder::command]Command::_build_subcommand Setting bin_name of quux to "example quux"
[clap_builder::builder::command]Command::_build_subcommand Setting display_name of quux to "example-quux"
[clap_builder::builder::command]Command::_build: name="quux"
[clap_builder::builder::command]Command::_propagate:quux
[clap_builder::builder::command]Command::_check_help_and_version:quux expand_help_tree=false
[clap_builder::builder::command]Command::long_help_exists
[clap_builder::builder::command]Command::_check_help_and_version: Building default --help
[clap_builder::builder::command]Command::_propagate_global_args:quux
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:foo
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:bar
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:help
[clap_builder::builder::debug_asserts]Command::_verify_positionals
[clap_builder::parser::parser]Parser::parse_subcommand: About to parse sc=quux
[clap_builder::parser::parser]Parser::get_matches_with
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-f"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("-f")
[clap_builder::parser::parser]Parser::get_matches_with: sc=None
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "f", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['f']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:f
[clap_builder::parser::parser]Parser::parse_short_arg:iter:f: Found valid opt or flag
[clap_builder::parser::parser]Parser::parse_short_arg:iter:f: val="", short_arg=ShortFlags { inner: "f", utf8_prefix: CharIndices { front_offset: 1, iter: Chars([]) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_opt_value; arg=foo, val=None, has_eq=false
[clap_builder::parser::parser]Parser::parse_opt_value; arg.settings=ArgFlags(2)
[clap_builder::parser::parser]Parser::parse_opt_value; Checking for val...
[clap_builder::parser::parser]Parser::parse_opt_value: More arg vals required...
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg Opt("foo")
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"x"'
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=foo, pending=1
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1, actual=1
[clap_builder::parser::parser]Parser::resolve_pending: id="foo"
[clap_builder::parser::parser]Parser::react action=Set, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: cur_idx:=1
[clap_builder::parser::parser]Parser::remove_overrides: id="foo"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="foo", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="foo"
[clap_builder::parser::parser]Parser::push_arg_values: ["x"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=2
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=foo, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::add_env
[clap_builder::parser::parser]Parser::add_env: Skipping existing arg `-f <FOO>`
[clap_builder::parser::parser]Parser::add_env: Checking arg `-b <BAR>`
[clap_builder::parser::parser]Parser::add_env: Found an opt with value="123"
[clap_builder::parser::parser]Parser::react action=Set, identifier=None, source=EnvVariable
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="bar", source=EnvVariable
[clap_builder::builder::command]Command::groups_for_arg: id="bar"
[clap_builder::parser::parser]Parser::push_arg_values: ["123"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=3
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=bar, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::add_env: Checking arg `--help`
[clap_builder::parser::parser]Parser::add_defaults
[clap_builder::parser::parser]Parser::add_defaults:iter:foo:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:foo: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:bar:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:bar: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:help:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:help: doesn't have default vals
[clap_builder::parser::validator]Validator::validate
[clap_builder::builder::command]Command::groups_for_arg: id="foo"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="foo", conflicts=[]
[clap_builder::builder::command]Command::groups_for_arg: id="bar"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="bar", conflicts=[]
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_exclusive:iter:"foo"
[clap_builder::parser::validator]Validator::validate_exclusive:iter:"bar"
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="foo"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="foo"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="bar"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="bar"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator]Validator::validate_required: required=ChildGraph([])
[clap_builder::parser::validator]Validator::gather_requires
[clap_builder::parser::validator]Validator::gather_requires:iter:"foo"
[clap_builder::parser::validator]Validator::gather_requires:iter:"bar"
[clap_builder::parser::validator]Validator::validate_required: is_exclusive_present=false
[clap_builder::parser::parser]Parser::add_env
[clap_builder::parser::parser]Parser::add_env: Checking arg `-f <FOO>`
[clap_builder::parser::parser]Parser::add_env: Checking arg `-b <BAR>`
[clap_builder::parser::parser]Parser::add_env: Found an opt with value="123"
[clap_builder::parser::parser]Parser::react action=Set, identifier=None, source=EnvVariable
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="bar", source=EnvVariable
[clap_builder::builder::command]Command::groups_for_arg: id="bar"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="GlobalArgs", source=EnvVariable
[clap_builder::parser::parser]Parser::push_arg_values: ["123"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=1
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=bar, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::add_env: Checking arg `--help`
[clap_builder::parser::parser]Parser::add_defaults
[clap_builder::parser::parser]Parser::add_defaults:iter:foo:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:foo: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:bar:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:bar: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:help:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:help: doesn't have default vals
[clap_builder::parser::validator]Validator::validate
[clap_builder::builder::command]Command::groups_for_arg: id="bar"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="bar", conflicts=[]
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="GlobalArgs", conflicts=[]
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="bar"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="bar"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator]Validator::validate_required: required=ChildGraph([])
[clap_builder::parser::validator]Validator::gather_requires
[clap_builder::parser::validator]Validator::gather_requires:iter:"bar"
[clap_builder::parser::validator]Validator::gather_requires:iter:"GlobalArgs"
[clap_builder::parser::validator]Validator::gather_requires:iter:"GlobalArgs":group
[clap_builder::parser::validator]Validator::validate_required: is_exclusive_present=false
[clap_builder::parser::validator]Validator::validate_required:iter:aog="foo"
[clap_builder::parser::validator]Validator::validate_required:iter: This is an arg
[clap_builder::parser::validator]Validator::is_missing_required_ok: foo
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="foo"
[clap_builder::builder::command]Command::groups_for_arg: id="foo"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="foo", conflicts=[]
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::builder::command]Command::groups_for_arg: id="foo"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="GlobalArgs"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator]Validator::validate_required:iter: Missing "foo"
[clap_builder::parser::validator]Validator::missing_required_error; incl=["foo"]
[clap_builder::parser::validator]Validator::missing_required_error: reqs=ChildGraph([Child { id: "foo", children: [] }])
[ clap_builder::output::usage]Usage::get_required_usage_from: incls=["foo"], matcher=true, incl_last=true
[ clap_builder::output::usage]Usage::get_required_usage_from: unrolled_reqs=["foo"]
[ clap_builder::output::usage]Usage::get_required_usage_from:iter:"foo" arg is_present=false
[ clap_builder::output::usage]Usage::get_required_usage_from:iter:"foo" arg is_present=false
[ clap_builder::output::usage]Usage::get_required_usage_from: ret_val=[StyledStr("\u{1b}[1m-f\u{1b}[0m <FOO>")]
[clap_builder::parser::validator]Validator::missing_required_error: req_args=[
    "-f <FOO>",
]
[ clap_builder::output::usage]Usage::create_usage_with_title
[ clap_builder::output::usage]Usage::create_usage_no_title
[ clap_builder::output::usage]Usage::create_smart_usage
[ clap_builder::output::usage]Usage::get_args: incls=["bar", "foo"]
[ clap_builder::output::usage]Usage::get_args: unrolled_reqs=["foo"]
[ clap_builder::output::usage]Usage::get_args: ret_val=[StyledStr("\u{1b}[1m-f\u{1b}[0m <FOO>"), StyledStr("\u{1b}[1m-b\u{1b}[0m <BAR>")]
[clap_builder::builder::command]Command::color: Color setting...
[clap_builder::builder::command]Auto
[clap_builder::builder::command]Command::color: Color setting...
[clap_builder::builder::command]Auto
@Xophmeister Xophmeister added the C-bug Category: Updating dependencies label Jul 19, 2023
Xophmeister added a commit to tweag/topiary that referenced this issue Jul 19, 2023
@epage
Copy link
Member

epage commented Jul 19, 2023

This is tied very closely to #1546 except we aren't verifying other required-related fields.

Xophmeister added a commit to tweag/topiary that referenced this issue Aug 8, 2023
As there are several potential sources of configuration, the collation
mode is relevant whether a invocation-specific configuration file is
specified, or not. This has the cheery consequence of sidestepping
the issue described in clap-rs/clap#5020.
Xophmeister added a commit to tweag/topiary that referenced this issue Aug 14, 2023
* Add env feature to clap and cargo update

* Dead Code: WIP CLI changes

* Dead Code: CLI feature parity

Although see clap-rs/clap#5020

* Note about not (yet) using infer_subcommands

See clap-rs/clap#5021

* Use clap/wrap_help

Although see clap-rs/clap#5022

* WIP: Strip out previous CLI argument parser setup

[skip ci]

* WIP: Normalise arguments for caller

[skip ci]

* Don't check for file-ness in the CLI argument parser

* File and directory canonicalisation for the argument parser

* Expose CLI types so they can be used downstream
Xophmeister added a commit to tweag/topiary that referenced this issue Aug 14, 2023
* Add env feature to clap and cargo update

* Dead Code: WIP CLI changes

* Dead Code: CLI feature parity

Although see clap-rs/clap#5020

* Note about not (yet) using infer_subcommands

See clap-rs/clap#5021

* Use clap/wrap_help

Although see clap-rs/clap#5022

* WIP: Strip out previous CLI argument parser setup

[skip ci]

* WIP: Normalise arguments for caller

[skip ci]

* Don't check for file-ness in the CLI argument parser

* File and directory canonicalisation for the argument parser

* Expose CLI types so they can be used downstream
Xophmeister added a commit to tweag/topiary that referenced this issue Aug 14, 2023
As there are several potential sources of configuration, the collation
mode is relevant whether a invocation-specific configuration file is
specified, or not. This has the cheery consequence of sidestepping
the issue described in clap-rs/clap#5020.
Xophmeister added a commit to tweag/topiary that referenced this issue Aug 15, 2023
* Implement Display for Configuration: pretty-print TOML

* Comment out old code, stub out new interface implementation

* Additional collation mode

* Some light refactoring

* More refactoring...

* Make the config collation independent of a specified config file

As there are several potential sources of configuration, the collation
mode is relevant whether a invocation-specific configuration file is
specified, or not. This has the cheery consequence of sidestepping
the issue described in clap-rs/clap#5020.

* Add useful annotations to the computed TOML output

* Refactor 'revise' collation mode from original

* Remove unnecessary trailing \n from TOML output

* Syntax tweak
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Updating dependencies
Projects
None yet
Development

No branches or pull requests

2 participants