Skip to content
Closed
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: 21 additions & 9 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,21 @@ def ensure_object(self, object_type: type[V]) -> V:
self.obj = rv = object_type()
return rv

def _lookup_default(self, name: str, call: bool = True) -> t.Any:
"""Internal version of :meth:`lookup_default` that returns
:data:`UNSET` when no value is found, for use by internal
methods that need to distinguish missing from ``None``.
"""
if self.default_map is not None:
value = self.default_map.get(name, UNSET)

if call and callable(value):
return value()

return value

return UNSET

@t.overload
def lookup_default(
self, name: str, call: t.Literal[True] = True
Expand All @@ -705,15 +720,12 @@ def lookup_default(self, name: str, call: bool = True) -> t.Any | None:
.. versionchanged:: 8.0
Added the ``call`` parameter.
"""
if self.default_map is not None:
value = self.default_map.get(name, UNSET)

if call and callable(value):
return value()
value = self._lookup_default(name, call=call)

return value
if value is UNSET:
return None

return UNSET
return value

def fail(self, message: str) -> t.NoReturn:
"""Aborts the execution of the program with a specific error
Expand Down Expand Up @@ -2278,7 +2290,7 @@ def get_default(
.. 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:
value = self.default
Expand Down Expand Up @@ -2321,7 +2333,7 @@ def consume_value(
source = ParameterSource.ENVIRONMENT

if value is UNSET:
default_map_value = ctx.lookup_default(self.name) # type: ignore
default_map_value = ctx._lookup_default(self.name)
if default_map_value is not UNSET:
value = default_map_value
source = ParameterSource.DEFAULT_MAP
Expand Down
17 changes: 17 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,3 +780,20 @@ def test_propagate_opt_prefixes():
ctx = click.Context(click.Command("test2"), parent=parent)

assert ctx._opt_prefixes == {"-", "--", "!"}


def test_lookup_default_returns_none_when_missing():
"""lookup_default should return None, not an internal sentinel,
when the key is not in default_map or default_map is None."""
cmd = click.Command("test")
ctx = click.Context(cmd)

# No default_map at all
assert ctx.lookup_default("missing") is None

# default_map exists but key is missing
ctx.default_map = {"other": "value"}
assert ctx.lookup_default("missing") is None

# default_map has the key
assert ctx.lookup_default("other") == "value"
Loading