Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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()