Skip to content

Commit ac5cec5

Browse files
committed
Reorder ParameterSource from most to least explicit
Closes #2879
1 parent 60cad4a commit ac5cec5

4 files changed

Lines changed: 56 additions & 13 deletions

File tree

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Unreleased
2626
via ``fileno()``, so that ``faulthandler``, ``subprocess``, and other
2727
C-level consumers no longer crash with ``io.UnsupportedOperation``.
2828
:issue:`2865`
29+
- Change :class:`ParameterSource` to an :class:`~enum.IntEnum` and reorder
30+
its members from most to least explicit, so values can be compared to
31+
check whether a parameter was explicitly provided. :issue:`2879` :pr:`3248`
2932

3033
Version 8.3.2
3134
-------------

docs/commands-and-groups.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,35 @@ allowed to read from the context, but not to perform any modifications on
342342
it.
343343

344344

345+
### Parameter Source Priority
346+
347+
When a parameter's value is resolved, Click checks several sources
348+
in the following order of precedence:
349+
350+
1. **Prompt** (`PROMPT`): interactively provided by the user at a prompt.
351+
2. **Command line** (`COMMANDLINE`): provided as a CLI argument or option.
352+
3. **Environment variable** (`ENVIRONMENT`): read from an environment variable.
353+
4. **Default map** (`DEFAULT_MAP`): looked up from {attr}`Context.default_map`.
354+
5. **Default** (`DEFAULT`): the default value defined on the parameter.
355+
356+
{class}`~click.core.ParameterSource` members are ordered from most
357+
explicit to least explicit. Because it is an {class}`~enum.IntEnum`,
358+
you can use comparison operators to check how explicitly a value was
359+
provided:
360+
361+
```python
362+
source = ctx.get_parameter_source("port")
363+
364+
# True if the value was explicitly set (command line, prompt, or env var).
365+
if source < click.ParameterSource.DEFAULT_MAP:
366+
...
367+
368+
# True if the value came from any kind of default.
369+
if source >= click.ParameterSource.DEFAULT_MAP:
370+
...
371+
```
372+
373+
345374
### Detecting the Source of a Parameter
346375

347376
In some situations it's helpful to understand whether or not an option

src/click/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from .core import Group as Group
1515
from .core import Option as Option
1616
from .core import Parameter as Parameter
17+
from .core import ParameterSource as ParameterSource
1718
from .decorators import argument as argument
1819
from .decorators import command as command
1920
from .decorators import confirmation_option as confirmation_option

src/click/core.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,30 +140,43 @@ def sort_key(item: Parameter) -> tuple[bool, float]:
140140
return sorted(declaration_order, key=sort_key)
141141

142142

143-
class ParameterSource(enum.Enum):
144-
"""This is an :class:`~enum.Enum` that indicates the source of a
143+
class ParameterSource(enum.IntEnum):
144+
"""This is an :class:`~enum.IntEnum` that indicates the source of a
145145
parameter's value.
146146
147147
Use :meth:`click.Context.get_parameter_source` to get the
148148
source for a parameter by name.
149149
150+
Members are ordered from most explicit to least explicit source.
151+
This allows comparison to check if a value was explicitly provided:
152+
153+
.. code-block:: python
154+
155+
source = ctx.get_parameter_source("port")
156+
if source < click.ParameterSource.DEFAULT_MAP:
157+
... # value was explicitly set
158+
159+
.. versionchanged:: 8.3.3
160+
Use :class:`~enum.IntEnum` and reorder members from most to
161+
least explicit. Supports comparison operators.
162+
150163
.. versionchanged:: 8.0
151164
Use :class:`~enum.Enum` and drop the ``validate`` method.
152165
153166
.. versionchanged:: 8.0
154167
Added the ``PROMPT`` value.
155168
"""
156169

170+
PROMPT = enum.auto()
171+
"""Used a prompt to confirm a default or provide a value."""
157172
COMMANDLINE = enum.auto()
158173
"""The value was provided by the command line args."""
159174
ENVIRONMENT = enum.auto()
160175
"""The value was provided with an environment variable."""
161-
DEFAULT = enum.auto()
162-
"""Used the default specified by the parameter."""
163176
DEFAULT_MAP = enum.auto()
164177
"""Used a default provided by :attr:`Context.default_map`."""
165-
PROMPT = enum.auto()
166-
"""Used a prompt to confirm a default or provide a value."""
178+
DEFAULT = enum.auto()
179+
"""Used the default specified by the parameter."""
167180

168181

169182
class Context:
@@ -2563,7 +2576,7 @@ def handle_parse_result(
25632576
if (
25642577
self.deprecated
25652578
and value is not UNSET
2566-
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
2579+
and source < ParameterSource.DEFAULT_MAP
25672580
):
25682581
extra_message = (
25692582
f" {self.deprecated}" if isinstance(self.deprecated, str) else ""
@@ -3285,7 +3298,7 @@ def consume_value(
32853298
self.is_flag
32863299
and value is True
32873300
and not self.is_bool_flag
3288-
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3301+
and source < ParameterSource.DEFAULT_MAP
32893302
):
32903303
value = self.flag_value
32913304

@@ -3295,7 +3308,7 @@ def consume_value(
32953308
elif (
32963309
self.multiple
32973310
and value is not UNSET
3298-
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3311+
and source < ParameterSource.DEFAULT_MAP
32993312
and any(v is FLAG_NEEDS_VALUE for v in value)
33003313
):
33013314
value = [self.flag_value if v is FLAG_NEEDS_VALUE else v for v in value]
@@ -3304,10 +3317,7 @@ def consume_value(
33043317
# The value wasn't set, or used the param's default, prompt for one to the user
33053318
# if prompting is enabled.
33063319
elif (
3307-
(
3308-
value is UNSET
3309-
or source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3310-
)
3320+
(value is UNSET or source >= ParameterSource.DEFAULT_MAP)
33113321
and self.prompt is not None
33123322
and (self.required or self.prompt_required)
33133323
and not ctx.resilient_parsing

0 commit comments

Comments
 (0)