Voice et bot modif

This commit is contained in:
pi 2026-06-16 17:09:34 +00:00
parent 189d56026b
commit 7333a22bcd
10774 changed files with 634644 additions and 933308 deletions

View file

@ -7,6 +7,8 @@ import inspect
import os
import sys
import typing as t
from abc import ABC
from abc import abstractmethod
from collections import abc
from collections import Counter
from contextlib import AbstractContextManager
@ -27,6 +29,7 @@ from .exceptions import ClickException
from .exceptions import Exit
from .exceptions import MissingParameter
from .exceptions import NoArgsIsHelpError
from .exceptions import NoSuchCommand
from .exceptions import UsageError
from .formatting import HelpFormatter
from .formatting import join_options
@ -90,6 +93,23 @@ def _check_nested_chain(
raise RuntimeError(message)
def _format_deprecated_label(deprecated: bool | str) -> str:
"""Return the parenthesized deprecation label shown in help text."""
label = _("deprecated").upper()
if isinstance(deprecated, str):
return f"({label}: {deprecated})"
return f"({label})"
def _format_deprecated_suffix(deprecated: bool | str) -> str:
"""Return the trailing reason for a ``DeprecationWarning`` message,
prefixed with a space, or an empty string when no reason was given.
"""
if isinstance(deprecated, str):
return f" {deprecated}"
return ""
def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]:
return list(zip(*repeat(iter(iterable), batch_size), strict=False))
@ -140,13 +160,26 @@ def iter_params_for_processing(
return sorted(declaration_order, key=sort_key)
class ParameterSource(enum.Enum):
"""This is an :class:`~enum.Enum` that indicates the source of a
class ParameterSource(enum.IntEnum):
"""This is an :class:`~enum.IntEnum` that indicates the source of a
parameter's value.
Use :meth:`click.Context.get_parameter_source` to get the
source for a parameter by name.
Members are ordered from most explicit to least explicit source.
This allows comparison to check if a value was explicitly provided:
.. code-block:: python
source = ctx.get_parameter_source("port")
if source < click.ParameterSource.DEFAULT_MAP:
... # value was explicitly set
.. versionchanged:: 8.3.3
Use :class:`~enum.IntEnum` and reorder members from most to
least explicit. Supports comparison operators.
.. versionchanged:: 8.0
Use :class:`~enum.Enum` and drop the ``validate`` method.
@ -154,16 +187,16 @@ class ParameterSource(enum.Enum):
Added the ``PROMPT`` value.
"""
PROMPT = enum.auto()
"""Used a prompt to confirm a default or provide a value."""
COMMANDLINE = enum.auto()
"""The value was provided by the command line args."""
ENVIRONMENT = enum.auto()
"""The value was provided with an environment variable."""
DEFAULT = enum.auto()
"""Used the default specified by the parameter."""
DEFAULT_MAP = enum.auto()
"""Used a default provided by :attr:`Context.default_map`."""
PROMPT = enum.auto()
"""Used a prompt to confirm a default or provide a value."""
DEFAULT = enum.auto()
"""Used the default specified by the parameter."""
class Context:
@ -183,7 +216,7 @@ class Context:
:param info_name: the info name for this invocation. Generally this
is the most descriptive name for the script or
command. For the toplevel script it is usually
the name of the script, for commands below it it's
the name of the script, for commands below that it's
the name of the script.
:param obj: an arbitrary object of user data.
:param auto_envvar_prefix: the prefix to use for automatic environment
@ -438,6 +471,12 @@ class Context:
self._close_callbacks: list[t.Callable[[], t.Any]] = []
self._depth = 0
self._parameter_source: dict[str, ParameterSource] = {}
# Tracks whether the option that currently owns each parameter slot in
# :attr:`params` had its ``default`` set explicitly by the user. Used
# to tie-break feature-switch groups where multiple options share a
# parameter name and both fall back to their default value.
# Refs: https://github.com/pallets/click/issues/3403
self._param_default_explicit: dict[str, bool] = {}
self._exit_stack = ExitStack()
@property
@ -685,6 +724,20 @@ class Context:
self.obj = rv = object_type()
return rv
def _default_map_has(self, name: str | None) -> bool:
"""Check if :attr:`default_map` contains a real value for ``name``.
Returns ``False`` when the key is absent, the map is ``None``,
``name`` is ``None``, or the stored value is the internal
:data:`UNSET` sentinel.
"""
return (
name is not None
and self.default_map is not None
and name in self.default_map
and self.default_map[name] is not UNSET
)
@t.overload
def lookup_default(
self, name: str, call: t.Literal[True] = True
@ -705,15 +758,17 @@ class Context:
.. versionchanged:: 8.0
Added the ``call`` parameter.
"""
if self.default_map is not None:
value = self.default_map.get(name, UNSET)
if not self._default_map_has(name):
return None
if call and callable(value):
return value()
# Assert to make the type checker happy.
assert self.default_map is not None
value = self.default_map[name]
return value
if call and callable(value):
return value()
return UNSET
return value
def fail(self, message: str) -> t.NoReturn:
"""Aborts the execution of the program with a specific error
@ -809,9 +864,7 @@ class Context:
# https://github.com/pallets/click/pull/3068
if default_value is UNSET:
default_value = None
kwargs[param.name] = param.type_cast_value( # type: ignore
ctx, default_value
)
kwargs[param.name] = param.type_cast_value(ctx, default_value)
# Track all kwargs as params, so that forward() will pass
# them on in subsequent calls.
@ -1066,7 +1119,7 @@ class Command:
# Cache the help option object in private _help_option attribute to
# avoid creating it multiple times. Not doing this will break the
# callback odering by iter_params_for_processing(), which relies on
# callback ordering by iter_params_for_processing(), which relies on
# object comparison.
if self._help_option is None:
# Avoid circular import.
@ -1106,14 +1159,7 @@ class Command:
text = ""
if self.deprecated:
deprecated_message = (
f"(DEPRECATED: {self.deprecated})"
if isinstance(self.deprecated, str)
else "(DEPRECATED)"
)
text = _("{text} {deprecated_message}").format(
text=text, deprecated_message=deprecated_message
)
text = f"{_(text)} {_format_deprecated_label(self.deprecated)}"
return text.strip()
@ -1143,14 +1189,7 @@ class Command:
text = ""
if self.deprecated:
deprecated_message = (
f"(DEPRECATED: {self.deprecated})"
if isinstance(self.deprecated, str)
else "(DEPRECATED)"
)
text = _("{text} {deprecated_message}").format(
text=text, deprecated_message=deprecated_message
)
text = f"{_(text)} {_format_deprecated_label(self.deprecated)}"
if text:
formatter.write_paragraph()
@ -1257,12 +1296,12 @@ class Command:
in the right way.
"""
if self.deprecated:
extra_message = (
f" {self.deprecated}" if isinstance(self.deprecated, str) else ""
)
message = _(
"DeprecationWarning: The command {name!r} is deprecated.{extra_message}"
).format(name=self.name, extra_message=extra_message)
).format(
name=self.name,
extra_message=_format_deprecated_suffix(self.deprecated),
)
echo(style(message, fg="red"), err=True)
if self.callback is not None:
@ -1291,7 +1330,7 @@ class Command:
or param.hidden
or (
not param.multiple
and ctx.get_parameter_source(param.name) # type: ignore
and ctx.get_parameter_source(param.name)
is ParameterSource.COMMANDLINE
)
):
@ -1908,7 +1947,6 @@ class Group(Command):
self, ctx: Context, args: list[str]
) -> tuple[str | None, Command | None, list[str]]:
cmd_name = make_str(args[0])
original_cmd_name = cmd_name
# Get the command
cmd = self.get_command(ctx, cmd_name)
@ -1928,7 +1966,7 @@ class Group(Command):
if cmd is None and not ctx.resilient_parsing:
if _split_opt(cmd_name)[0]:
self.parse_args(ctx, args)
ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name))
raise NoSuchCommand(cmd_name, possibilities=self.commands, ctx=ctx)
return cmd_name if cmd else None, cmd, args[1:]
def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]:
@ -2024,7 +2062,7 @@ def _check_iter(value: t.Any) -> cabc.Iterator[t.Any]:
return iter(value)
class Parameter:
class Parameter(ABC):
r"""A parameter to a command comes in two versions: they are either
:class:`Option`\s or :class:`Argument`\s. Other subclasses are currently
not supported by design as some of the internals for parsing are
@ -2120,7 +2158,7 @@ class Parameter:
def __init__(
self,
param_decls: cabc.Sequence[str] | None = None,
type: types.ParamType | t.Any | None = None,
type: types.ParamType[t.Any] | t.Any | None = None,
required: bool = False,
# XXX The default historically embed two concepts:
# - the declaration of a Parameter object carrying the default (handy to
@ -2146,13 +2184,13 @@ class Parameter:
| None = None,
deprecated: bool | str = False,
) -> None:
self.name: str | None
self.name: str
self.opts: list[str]
self.secondary_opts: list[str]
self.name, self.opts, self.secondary_opts = self._parse_decls(
param_decls or (), expose_value
)
self.type: types.ParamType = types.convert_type(type, default)
self.type: types.ParamType[t.Any] = types.convert_type(type, default)
# Default nargs to what the type tells us if we have that
# information available.
@ -2168,6 +2206,12 @@ class Parameter:
self.multiple = multiple
self.expose_value = expose_value
self.default: t.Any | t.Callable[[], t.Any] | None = default
# Whether the user passed ``default`` explicitly to the constructor.
# Captured before any auto-derived default (like ``False`` for boolean
# flags in :class:`Option`) replaces the :data:`UNSET` sentinel, so it
# remains ``False`` when the default was inferred rather than chosen.
# Refs: https://github.com/pallets/click/issues/3403
self._default_explicit: bool = default is not UNSET
self.is_eager = is_eager
self.metavar = metavar
self.envvar = envvar
@ -2219,17 +2263,17 @@ class Parameter:
def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.name}>"
@abstractmethod
def _parse_decls(
self, decls: cabc.Sequence[str], expose_value: bool
) -> tuple[str | None, list[str], list[str]]:
raise NotImplementedError()
) -> tuple[str, list[str], list[str]]: ...
@property
def human_readable_name(self) -> str:
"""Returns the human readable name of this parameter. This is the
same as the name for options, but the metavar for arguments.
"""
return self.name # type: ignore
return self.name
def make_metavar(self, ctx: Context) -> str:
if self.metavar is not None:
@ -2278,9 +2322,9 @@ class Parameter:
.. versionchanged:: 8.0
Added the ``call`` parameter.
"""
value = ctx.lookup_default(self.name, call=False) # type: ignore
value = ctx.lookup_default(self.name, call=False)
if value is UNSET:
if value is None and not ctx._default_map_has(self.name):
value = self.default
if call and callable(value):
@ -2288,8 +2332,8 @@ class Parameter:
return value
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
raise NotImplementedError()
@abstractmethod
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: ...
def consume_value(
self, ctx: Context, opts: cabc.Mapping[str, t.Any]
@ -2305,7 +2349,7 @@ class Parameter:
:meta private:
"""
# Collect from the parse the value passed by the user to the CLI.
value = opts.get(self.name, UNSET) # type: ignore
value = opts.get(self.name, UNSET)
# If the value is set, it means it was sourced from the command line by the
# parser, otherwise it left unset by default.
source = (
@ -2321,11 +2365,16 @@ class Parameter:
source = ParameterSource.ENVIRONMENT
if value is UNSET:
default_map_value = ctx.lookup_default(self.name) # type: ignore
if default_map_value is not UNSET:
default_map_value = ctx.lookup_default(self.name)
if default_map_value is not None or ctx._default_map_has(self.name):
value = default_map_value
source = ParameterSource.DEFAULT_MAP
# A string from default_map must be split for multi-value
# parameters, matching value_from_envvar behavior.
if isinstance(value, str) and self.nargs != 1:
value = self.type.split_envvar_value(value)
if value is UNSET:
default_value = self.get_default(ctx)
if default_value is not UNSET:
@ -2549,27 +2598,35 @@ class Parameter:
:meta private:
"""
# Capture the slot's existing state before we mutate
# ``_parameter_source`` so the write decision below can compare our
# incoming source against the source of the option that already wrote
# the slot (if any).
existing_value = ctx.params.get(self.name, UNSET)
existing_source = ctx.get_parameter_source(self.name)
existing_default_explicit = ctx._param_default_explicit.get(self.name, False)
with augment_usage_errors(ctx, param=self):
value, source = self.consume_value(ctx, opts)
ctx.set_parameter_source(self.name, source) # type: ignore
# Record the source before processing so eager callbacks and type
# conversion can inspect it. Restored after arbitration if this
# option loses a feature-switch group.
ctx.set_parameter_source(self.name, source)
# Display a deprecation warning if necessary.
if (
self.deprecated
and value is not UNSET
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
and source < ParameterSource.DEFAULT_MAP
):
extra_message = (
f" {self.deprecated}" if isinstance(self.deprecated, str) else ""
)
message = _(
"DeprecationWarning: The {param_type} {name!r} is deprecated."
"{extra_message}"
).format(
param_type=self.param_type_name,
name=self.human_readable_name,
extra_message=extra_message,
extra_message=_format_deprecated_suffix(self.deprecated),
)
echo(style(message, fg="red"), err=True)
@ -2584,32 +2641,49 @@ class Parameter:
# to UNSET, which will be interpreted as a missing value.
value = UNSET
# Add parameter's value to the context.
if (
self.expose_value
# We skip adding the value if it was previously set by another parameter
# targeting the same variable name. This prevents parameters competing for
# the same name to override each other.
and (self.name not in ctx.params or ctx.params[self.name] is UNSET)
):
# Click is logically enforcing that the name is None if the parameter is
# not to be exposed. We still assert it here to please the type checker.
assert self.name is not None, (
f"{self!r} parameter's name should not be None when exposing value."
)
ctx.params[self.name] = value
# Arbitrate the slot when several parameters target the same variable
# name (feature-switch groups). See: https://github.com/pallets/click/issues/3403
slot_empty = existing_value is UNSET
more_explicit = existing_source is not None and source < existing_source
same_source = existing_source is not None and source == existing_source
auto_would_downgrade_explicit = (
same_source
and source == ParameterSource.DEFAULT
and existing_default_explicit
and not self._default_explicit
)
is_winner = (
slot_empty
or more_explicit
or (same_source and not auto_would_downgrade_explicit)
)
if is_winner:
if self.expose_value:
ctx.params[self.name] = value
ctx._param_default_explicit[self.name] = self._default_explicit
elif existing_source is not None:
# Lost arbitration; restore the winning option's source.
ctx.set_parameter_source(self.name, existing_source)
# else: ctx.params[self.name] was populated by code that bypassed
# handle_parse_result (from another option's callback for example). Keep
# the provisional source recorded before process_value so downstream
# lookups don't return ``None``.
return value, args
def get_help_record(self, ctx: Context) -> tuple[str, str] | None:
pass
return None
def get_usage_pieces(self, ctx: Context) -> list[str]:
return []
def get_error_hint(self, ctx: Context) -> str:
def get_error_hint(self, ctx: Context | None) -> str:
"""Get a stringified version of the param for use in error messages to
indicate which param caused the error.
.. versionchanged:: 8.4.0
``ctx`` can be ``None``.
"""
hint_list = self.opts or [self.human_readable_name]
return " / ".join(f"'{x}'" for x in hint_list)
@ -2618,7 +2692,7 @@ class Parameter:
"""Return a list of completions for the incomplete value. If a
``shell_complete`` function was given during init, it is used.
Otherwise, the :attr:`type`
:meth:`~click.types.ParamType.shell_complete` function is used.
:meth:`~click.types.ParamType[t.Any].shell_complete` function is used.
:param ctx: Invocation context for this command.
:param incomplete: Value being completed. May be empty.
@ -2684,6 +2758,11 @@ class Option(Parameter):
:param hidden: hide this option from help outputs.
:param attrs: Other command arguments described in :class:`Parameter`.
.. versionchanged:: 8.4.0
Non-basic ``flag_value`` types (not ``str``, ``int``, ``float``, or
``bool``) are passed through unchanged instead of being stringified.
Previously, ``type=click.UNPROCESSED`` was required to preserve them.
.. versionchanged:: 8.2
``envvar`` used with ``flag_value`` will always use the ``flag_value``,
previously it would use the value of the environment variable.
@ -2701,7 +2780,8 @@ class Option(Parameter):
default value is ``False``.
.. versionchanged:: 8.0.1
``type`` is detected from ``flag_value`` if given.
``type`` is detected from ``flag_value`` if given, for basic Python
types (``str``, ``int``, ``float``, ``bool``).
"""
param_type_name = "option"
@ -2719,7 +2799,7 @@ class Option(Parameter):
multiple: bool = False,
count: bool = False,
allow_from_autoenv: bool = True,
type: types.ParamType | t.Any | None = None,
type: types.ParamType[t.Any] | t.Any | None = None,
help: str | None = None,
hidden: bool = False,
show_choices: bool = True,
@ -2735,7 +2815,7 @@ class Option(Parameter):
)
if prompt is True:
if self.name is None:
if not self.name:
raise TypeError("'name' is required with 'prompt=True'.")
prompt_text: str | None = self.name.replace("_", " ").capitalize()
@ -2745,12 +2825,8 @@ class Option(Parameter):
prompt_text = prompt
if deprecated:
deprecated_message = (
f"(DEPRECATED: {deprecated})"
if isinstance(deprecated, str)
else "(DEPRECATED)"
)
help = help + deprecated_message if help is not None else deprecated_message
label = _format_deprecated_label(deprecated)
help = f"{help} {label}" if help is not None else label
self.prompt = prompt_text
self.confirmation_prompt = confirmation_prompt
@ -2777,10 +2853,13 @@ class Option(Parameter):
# Implicitly a flag because secondary options names were given.
elif self.secondary_opts:
is_flag = True
# The option is explicitly not a flag. But we do not know yet if it needs a
# value or not. So we look at the default value to determine it.
# The option is explicitly not a flag, but to determine whether or not it needs
# value, we need to check if `flag_value` or `default` was set. Either one is
# sufficient.
# Ref: https://github.com/pallets/click/issues/3084
elif is_flag is False and not self._flag_needs_value:
self._flag_needs_value = self.default is UNSET
self._flag_needs_value = flag_value is not UNSET or self.default is UNSET
if is_flag:
# Set missing default for flags if not explicitly required or prompted.
@ -2792,13 +2871,26 @@ class Option(Parameter):
if type is None:
# A flag without a flag_value is a boolean flag.
if flag_value is UNSET:
self.type: types.ParamType = types.BoolParamType()
self.type: types.ParamType[t.Any] = types.BoolParamType()
# If the flag value is a boolean, use BoolParamType.
elif isinstance(flag_value, bool):
self.type = types.BoolParamType()
# Otherwise, guess the type from the flag value.
else:
self.type = types.convert_type(None, flag_value)
guessed = types.convert_type(None, flag_value)
if (
isinstance(guessed, types.StringParamType)
and not isinstance(flag_value, str)
and flag_value is not None
):
# The flag_value type couldn't be auto-detected
# (not str, int, float, or bool). Since flag_value
# is a programmer-provided Python object, not CLI
# input, pass it through unchanged instead of
# stringifying it.
self.type = types.UNPROCESSED
else:
self.type = guessed
self.is_flag: bool = bool(is_flag)
self.is_bool_flag: bool = bool(
@ -2811,14 +2903,12 @@ class Option(Parameter):
if self.default is UNSET and not self.required:
self.default = False
# Support the special case of aligning the default value with the flag_value
# for flags whose default is explicitly set to True. Note that as long as we
# have this condition, there is no way a flag can have a default set to True,
# and a flag_value set to something else. Refs:
# The alignment of default to the flag_value is resolved lazily in
# get_default() to prevent callable flag_values (like classes) from
# being instantiated. Refs:
# https://github.com/pallets/click/issues/3121
# https://github.com/pallets/click/issues/3024#issuecomment-3146199461
# https://github.com/pallets/click/pull/3030/commits/06847da
if self.default is True and self.flag_value is not UNSET:
self.default = self.flag_value
# Set the default flag_value if it is not set.
if self.flag_value is UNSET:
@ -2882,7 +2972,40 @@ class Option(Parameter):
)
return info_dict
def get_error_hint(self, ctx: Context) -> str:
def get_default(
self, ctx: Context, call: bool = True
) -> t.Any | t.Callable[[], t.Any] | None:
"""Return the default value for this option.
For non-boolean flag options, ``default=True`` is treated as a sentinel
meaning "activate this flag by default" and is resolved to
:attr:`flag_value`. For example, with ``--upper/--lower`` feature
switches where ``flag_value="upper"`` and ``default=True``, the default
resolves to ``"upper"``.
.. caution::
This substitution only applies to non-boolean flags
(:attr:`is_bool_flag` is ``False``). For boolean flags, ``True`` is
a legitimate Python value and ``default=True`` is returned as-is.
.. versionchanged:: 8.3.3
``default=True`` is no longer substituted with ``flag_value`` for
boolean flags, fixing negative boolean flags like
``flag_value=False, default=True``.
"""
value = super().get_default(ctx, call=False)
# Resolve default=True to flag_value lazily (here instead of
# __init__) to prevent callable flag_values (like classes) from
# being instantiated by the callable check below.
if value is True and self.is_flag and not self.is_bool_flag:
value = self.flag_value
elif call and callable(value):
value = value()
return value
def get_error_hint(self, ctx: Context | None) -> str:
result = super().get_error_hint(ctx)
if self.show_envvar and self.envvar is not None:
result += f" (env var: '{self.envvar}')"
@ -2890,7 +3013,7 @@ class Option(Parameter):
def _parse_decls(
self, decls: cabc.Sequence[str], expose_value: bool
) -> tuple[str | None, list[str], list[str]]:
) -> tuple[str, list[str], list[str]]:
opts = []
secondary_opts = []
name = None
@ -2899,7 +3022,7 @@ class Option(Parameter):
for decl in decls:
if decl.isidentifier():
if name is not None:
raise TypeError(f"Name '{name}' defined twice")
raise TypeError(_("Name '{name}' defined twice").format(name=name))
name = decl
else:
split_char = ";" if decl[:1] == "/" else "/"
@ -2914,8 +3037,10 @@ class Option(Parameter):
secondary_opts.append(second.lstrip())
if first == second:
raise ValueError(
f"Boolean option {decl!r} cannot use the"
" same flag for true/false."
_(
"Boolean option {decl!r} cannot use the"
" same flag for true/false."
).format(decl=decl)
)
else:
possible_names.append(_split_opt(decl))
@ -2929,16 +3054,20 @@ class Option(Parameter):
if name is None:
if not expose_value:
return None, opts, secondary_opts
return "", opts, secondary_opts
raise TypeError(
f"Could not determine name for option with declarations {decls!r}"
_(
"Could not determine name for option with declarations {decls!r}"
).format(decls=decls)
)
if not opts and not secondary_opts:
raise TypeError(
f"No options defined but a name was passed ({name})."
" Did you mean to declare an argument instead? Did"
f" you mean to pass '--{name}'?"
_(
"No options defined but a name was passed ({name})."
" Did you mean to declare an argument instead? Did"
" you mean to pass '--{name}'?"
).format(name=name)
)
return name, opts, secondary_opts
@ -3037,7 +3166,7 @@ class Option(Parameter):
if (
self.allow_from_autoenv
and ctx.auto_envvar_prefix is not None
and self.name is not None
and self.name
):
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
@ -3088,7 +3217,7 @@ class Option(Parameter):
)[1]
elif self.is_bool_flag and not self.secondary_opts and not default_value:
default_string = ""
elif default_value == "":
elif isinstance(default_value, str) and default_value == "":
default_string = '""'
else:
default_string = str(default_value)
@ -3142,10 +3271,10 @@ class Option(Parameter):
default = bool(default)
return confirm(self.prompt, default)
# If show_default is set to True/False, provide this to `prompt` as well. For
# non-bool values of `show_default`, we use `prompt`'s default behavior
# If show_default is given, provide this to `prompt` as well,
# otherwise we use `prompt`'s default behavior
prompt_kwargs: t.Any = {}
if isinstance(self.show_default, bool):
if self.show_default is not None:
prompt_kwargs["show_default"] = self.show_default
return prompt(
@ -3176,11 +3305,7 @@ class Option(Parameter):
if rv is not None:
return rv
if (
self.allow_from_autoenv
and ctx.auto_envvar_prefix is not None
and self.name is not None
):
if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None and self.name:
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
rv = os.environ.get(envvar)
@ -3263,7 +3388,7 @@ class Option(Parameter):
self.is_flag
and value is True
and not self.is_bool_flag
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
and source < ParameterSource.DEFAULT_MAP
):
value = self.flag_value
@ -3273,7 +3398,8 @@ class Option(Parameter):
elif (
self.multiple
and value is not UNSET
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
and isinstance(value, cabc.Iterable)
and source < ParameterSource.DEFAULT_MAP
and any(v is FLAG_NEEDS_VALUE for v in value)
):
value = [self.flag_value if v is FLAG_NEEDS_VALUE else v for v in value]
@ -3282,10 +3408,7 @@ class Option(Parameter):
# The value wasn't set, or used the param's default, prompt for one to the user
# if prompting is enabled.
elif (
(
value is UNSET
or source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
)
(value is UNSET or source >= ParameterSource.DEFAULT_MAP)
and self.prompt is not None
and (self.required or self.prompt_required)
and not ctx.resilient_parsing
@ -3348,14 +3471,14 @@ class Argument(Parameter):
def human_readable_name(self) -> str:
if self.metavar is not None:
return self.metavar
return self.name.upper() # type: ignore
return self.name.upper()
def make_metavar(self, ctx: Context) -> str:
if self.metavar is not None:
return self.metavar
var = self.type.get_metavar(param=self, ctx=ctx)
if not var:
var = self.name.upper() # type: ignore
var = self.name.upper()
if self.deprecated:
var += "!"
if not self.required:
@ -3366,26 +3489,30 @@ class Argument(Parameter):
def _parse_decls(
self, decls: cabc.Sequence[str], expose_value: bool
) -> tuple[str | None, list[str], list[str]]:
) -> tuple[str, list[str], list[str]]:
if not decls:
if not expose_value:
return None, [], []
return "", [], []
raise TypeError("Argument is marked as exposed, but does not have a name.")
if len(decls) == 1:
name = arg = decls[0]
name = name.replace("-", "_").lower()
else:
raise TypeError(
"Arguments take exactly one parameter declaration, got"
f" {len(decls)}: {decls}."
_(
"Arguments take exactly one parameter declaration, got"
" {length}: {decls}."
).format(length=len(decls), decls=decls)
)
return name, [arg], []
def get_usage_pieces(self, ctx: Context) -> list[str]:
return [self.make_metavar(ctx)]
def get_error_hint(self, ctx: Context) -> str:
return f"'{self.make_metavar(ctx)}'"
def get_error_hint(self, ctx: Context | None) -> str:
if ctx is not None:
return f"'{self.make_metavar(ctx)}'"
return f"'{self.human_readable_name}'"
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
parser.add_argument(dest=self.name, nargs=self.nargs, obj=self)