Skip to content

Commit c7e1ba8

Browse files
authored
Reorder ParameterSource (#3248)
2 parents 76552ff + ac5cec5 commit c7e1ba8

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
@@ -34,6 +34,9 @@ Unreleased
3434
via ``fileno()``, so that ``faulthandler``, ``subprocess``, and other
3535
C-level consumers no longer crash with ``io.UnsupportedOperation``.
3636
:issue:`2865`
37+
- Change :class:`ParameterSource` to an :class:`~enum.IntEnum` and reorder
38+
its members from most to least explicit, so values can be compared to
39+
check whether a parameter was explicitly provided. :issue:`2879` :pr:`3248`
3740

3841
Version 8.3.2
3942
-------------

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 ""
@@ -3304,7 +3317,7 @@ def consume_value(
33043317
self.is_flag
33053318
and value is True
33063319
and not self.is_bool_flag
3307-
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3320+
and source < ParameterSource.DEFAULT_MAP
33083321
):
33093322
value = self.flag_value
33103323

@@ -3314,7 +3327,7 @@ def consume_value(
33143327
elif (
33153328
self.multiple
33163329
and value is not UNSET
3317-
and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3330+
and source < ParameterSource.DEFAULT_MAP
33183331
and any(v is FLAG_NEEDS_VALUE for v in value)
33193332
):
33203333
value = [self.flag_value if v is FLAG_NEEDS_VALUE else v for v in value]
@@ -3323,10 +3336,7 @@ def consume_value(
33233336
# The value wasn't set, or used the param's default, prompt for one to the user
33243337
# if prompting is enabled.
33253338
elif (
3326-
(
3327-
value is UNSET
3328-
or source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP)
3329-
)
3339+
(value is UNSET or source >= ParameterSource.DEFAULT_MAP)
33303340
and self.prompt is not None
33313341
and (self.required or self.prompt_required)
33323342
and not ctx.resilient_parsing

0 commit comments

Comments
 (0)