def parse_args( self, parse_args_request: ParseArgsRequest) -> OptionValueContainer: """Set values for this parser's options on the namespace object. :raises: :class:`ParseError` if any flags weren't recognized. """ flag_value_map = parse_args_request.flag_value_map namespace = parse_args_request.namespace get_all_scoped_flag_names = parse_args_request.get_all_scoped_flag_names levenshtein_max_distance = parse_args_request.levenshtein_max_distance mutex_map: DefaultDict[str, List[str]] = defaultdict(list) for args, kwargs in self._unnormalized_option_registrations_iter(): if kwargs.get("passive" ) and not parse_args_request.include_passive_options: continue self._validate(args, kwargs) name, dest = self.parse_name_and_dest(*args, **kwargs) # Compute the values provided on the command line for this option. Note that there may be # multiple values, for any combination of the following reasons: # - The user used the same flag multiple times. # - The user specified a boolean flag (--foo) and its inverse (--no-foo). # - The option has multiple names, and the user used more than one of them. # # We also check if the option is deprecated, but we only do so if the option is explicitly # specified as a command-line flag, so we don't spam users with deprecated option values # specified in config, which isn't something they control. implicit_value = kwargs.get("implicit_value") if implicit_value is None and kwargs.get("type") == bool: implicit_value = True # Allows --foo to mean --foo=true. flag_vals: List[Union[int, float, bool, str]] = [] def add_flag_val( v: Optional[Union[int, float, bool, str]]) -> None: if v is None: if implicit_value is None: raise ParseError( f"Missing value for command line flag {arg} in {self._scope_str()}" ) flag_vals.append(implicit_value) else: flag_vals.append(v) for arg in args: # If the user specified --no-foo on the cmd line, treat it as if the user specified # --foo, but with the inverse value. if kwargs.get("type") == bool: inverse_arg = self._inverse_arg(arg) if inverse_arg in flag_value_map: flag_value_map[arg] = [ self._invert(v) for v in flag_value_map[inverse_arg] ] implicit_value = self._invert(implicit_value) del flag_value_map[inverse_arg] if arg in flag_value_map: for v in flag_value_map[arg]: add_flag_val(v) del flag_value_map[arg] # Get the value for this option, falling back to defaults as needed. try: val = self._compute_value(dest, kwargs, flag_vals) except ParseError as e: # Reraise a new exception with context on the option being processed at the time of error. # Note that other exception types can be raised here that are caught by ParseError (e.g. # BooleanConversionError), hence we reference the original exception type as type(e). raise type( e )("Error computing value for {} in {} (may also be from PANTS_* environment variables)." "\nCaused by:\n{}".format(", ".join(args), self._scope_str(), traceback.format_exc())) # If the option is explicitly given, check deprecation and mutual exclusion. if val.rank > RankedValue.HARDCODED: self._check_deprecated(name, kwargs) mutex_dest = kwargs.get("mutually_exclusive_group") if mutex_dest: mutex_map[mutex_dest].append(dest) dest = mutex_dest else: mutex_map[dest].append(dest) if len(mutex_map[dest]) > 1: raise MutuallyExclusiveOptionError( f"Can only provide one of the mutually exclusive options {mutex_map[dest]}" ) setattr(namespace, dest, val) # See if there are any unconsumed flags remaining, and if so, raise a ParseError. if flag_value_map: self._raise_error_for_invalid_flag_names( flag_value_map, all_scoped_flag_names=get_all_scoped_flag_names(), levenshtein_max_distance=levenshtein_max_distance, ) return namespace
def parse_args(self, parse_args_request: ParseArgsRequest) -> OptionValueContainer: """Set values for this parser's options on the namespace object. :raises: :class:`ParseError` if any flags weren't recognized. """ flag_value_map = parse_args_request.flag_value_map namespace = parse_args_request.namespace mutex_map: DefaultDict[str, List[str]] = defaultdict(list) for args, kwargs in self._unnormalized_option_registrations_iter(): self._validate(args, kwargs) dest = self.parse_dest(*args, **kwargs) # Compute the values provided on the command line for this option. Note that there may be # multiple values, for any combination of the following reasons: # - The user used the same flag multiple times. # - The user specified a boolean flag (--foo) and its inverse (--no-foo). # - The option has multiple names, and the user used more than one of them. # # We also check if the option is deprecated, but we only do so if the option is explicitly # specified as a command-line flag, so we don't spam users with deprecated option values # specified in config, which isn't something they control. implicit_value = kwargs.get("implicit_value") if implicit_value is None and kwargs.get("type") == bool: implicit_value = True # Allows --foo to mean --foo=true. flag_vals: list[int | float | bool | str] = [] def add_flag_val(v: int | float | bool | str | None) -> None: if v is None: if implicit_value is None: raise ParseError( f"Missing value for command line flag {arg} in {self._scope_str()}" ) flag_vals.append(implicit_value) else: flag_vals.append(v) for arg in args: # If the user specified --no-foo on the cmd line, treat it as if the user specified # --foo, but with the inverse value. if kwargs.get("type") == bool: inverse_arg = self._inverse_arg(arg) if inverse_arg in flag_value_map: flag_value_map[arg] = [self._invert(v) for v in flag_value_map[inverse_arg]] implicit_value = self._invert(implicit_value) del flag_value_map[inverse_arg] if arg in flag_value_map: for v in flag_value_map[arg]: add_flag_val(v) del flag_value_map[arg] # Get the value for this option, falling back to defaults as needed. try: value_history = self._compute_value( dest, kwargs, flag_vals, parse_args_request.passthrough_args ) self._history[dest] = value_history val = value_history.final_value except ParseError as e: # Reraise a new exception with context on the option being processed at the time of error. # Note that other exception types can be raised here that are caught by ParseError (e.g. # BooleanConversionError), hence we reference the original exception type as type(e). args_str = ", ".join(args) raise type(e)( f"Error computing value for {args_str} in {self._scope_str()} (may also be from PANTS_* environment variables).\nCaused by:\n{traceback.format_exc()}" ) # If the option is explicitly given, check deprecation and mutual exclusion. if val.rank > Rank.HARDCODED: self._check_deprecated(dest, kwargs) mutex_dest = kwargs.get("mutually_exclusive_group") if mutex_dest: mutex_map[mutex_dest].append(dest) dest = mutex_dest else: mutex_map[dest].append(dest) if len(mutex_map[dest]) > 1: raise MutuallyExclusiveOptionError( f"Can only provide one of the mutually exclusive options {mutex_map[dest]}" ) setattr(namespace, dest, val) if not parse_args_request.allow_unknown_flags and flag_value_map: # There were unconsumed flags. raise UnknownFlagsError(tuple(flag_value_map.keys()), self.scope) return namespace.build()