Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/commands-and-groups.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,33 @@ enum.
println()
invoke(cli, prog_name='cli', args=[])
println()


Parameter Source Priority
~~~~~~~~~~~~~~~~~~~~~~~~~

When a parameter's value is resolved, Click checks several sources
in the following order of precedence:

1. **Prompt** (``PROMPT``) — interactively provided by the user at a prompt.
2. **Command line** (``COMMANDLINE``) — provided as a CLI argument or option.
3. **Environment variable** (``ENVIRONMENT``) — read from an environment variable.
4. **Default map** (``DEFAULT_MAP``) — looked up from :attr:`Context.default_map`.
5. **Default** (``DEFAULT``) — the default value defined on the parameter.

:class:`~click.core.ParameterSource` members are ordered from most
explicit to least explicit. Because it is an :class:`~enum.IntEnum`,
you can use comparison operators to check how explicitly a value was
provided:

.. code-block:: python

source = ctx.get_parameter_source("port")

# True if the value was explicitly set (command line, prompt, or env var).
if source < click.ParameterSource.DEFAULT_MAP:
...

# True if the value came from any kind of default.
if source >= click.ParameterSource.DEFAULT_MAP:
...
1 change: 1 addition & 0 deletions src/click/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .core import Group as Group
from .core import Option as Option
from .core import Parameter as Parameter
from .core import ParameterSource as ParameterSource
from .decorators import argument as argument
from .decorators import command as command
from .decorators import confirmation_option as confirmation_option
Expand Down
36 changes: 23 additions & 13 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,30 +140,43 @@ def sort_key(item: Parameter) -> tuple[bool, float]:
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.2
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.

.. versionchanged:: 8.0
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:
Expand Down Expand Up @@ -2558,7 +2571,7 @@ def handle_parse_result(
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 ""
Expand Down Expand Up @@ -3266,7 +3279,7 @@ def consume_value(
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

Expand All @@ -3276,7 +3289,7 @@ def consume_value(
elif (
self.multiple
and value is not UNSET
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
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]
Expand All @@ -3285,10 +3298,7 @@ def consume_value(
# 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
Expand Down
Loading