Skip to content

Commit 26bf6d1

Browse files
committed
Implement closed support in expandtype
Close the kwargs TypedDict returned by ParamSpec where possible. Use an empty, closed TypedDict instead of a `dict[str, Never]` for functions that do not accept kwargs.
1 parent e79c8d9 commit 26bf6d1

2 files changed

Lines changed: 13 additions & 13 deletions

File tree

mypy/expandtype.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def _possible_callable_kwargs(cls, repl: Parameters, dict_type: Instance) -> Pro
344344
return dict_type
345345
kwargs = {}
346346
required_names = set()
347-
extra_items: Type = UninhabitedType()
347+
extra_items: Type | None = None
348348
for kind, name, type in zip(repl.arg_kinds, repl.arg_names, repl.arg_types):
349349
if kind == ArgKind.ARG_NAMED and name is not None:
350350
kwargs[name] = type
@@ -354,11 +354,11 @@ def _possible_callable_kwargs(cls, repl: Parameters, dict_type: Instance) -> Pro
354354
extra_items = type
355355
elif not kind.is_star() and name is not None:
356356
kwargs[name] = type
357-
if not kwargs:
357+
if not kwargs and extra_items is not None:
358358
return Instance(dict_type.type, [dict_type.args[0], extra_items])
359-
# TODO: when PEP 728 is implemented, pass extra_items below.
360-
# TODO: implement closure analysis
361-
return TypedDictType(kwargs, required_names, set(), False, fallback=dict_type)
359+
# TODO: when PEP 728 `extra_items` is implemented, pass extra_items below.
360+
is_closed = extra_items is None
361+
return TypedDictType(kwargs, required_names, set(), is_closed, fallback=dict_type)
362362

363363
def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type:
364364
# Sometimes solver may need to expand a type variable with (a copy of) itself

test-data/unit/check-parameter-specification.test

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,16 +2742,16 @@ def f8(x: int, /, **kwargs: str) -> int:
27422742
def f9(x: int, **kwargs: Unpack[Opt]) -> int:
27432743
return 0
27442744

2745-
reveal_type(Sneaky(f1).kwargs) # N: Revealed type is "builtins.dict[builtins.str, Never]"
2746-
reveal_type(Sneaky(f2, 1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int})"
2747-
reveal_type(Sneaky(f3, 1).kwargs) # N: Revealed type is "builtins.dict[builtins.str, Never]"
2748-
reveal_type(Sneaky(f4, x=1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x': builtins.int})"
2749-
reveal_type(Sneaky(f5, 1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y'?: builtins.int})"
2750-
reveal_type(Sneaky(f5, 1, 2).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y'?: builtins.int})"
2745+
reveal_type(Sneaky(f1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {}, closed=True)"
2746+
reveal_type(Sneaky(f2, 1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int}, closed=True)"
2747+
reveal_type(Sneaky(f3, 1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {}, closed=True)"
2748+
reveal_type(Sneaky(f4, x=1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x': builtins.int}, closed=True)"
2749+
reveal_type(Sneaky(f5, 1).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y'?: builtins.int}, closed=True)"
2750+
reveal_type(Sneaky(f5, 1, 2).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y'?: builtins.int}, closed=True)"
27512751
reveal_type(Sneaky(f6, x=1).kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
27522752
reveal_type(Sneaky(f6, x=1, y=2).kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
27532753
reveal_type(Sneaky(f7, 1, y='').kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int})"
27542754
reveal_type(Sneaky(f8, 1, y='').kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
2755-
reveal_type(Sneaky(f9, 1, y=0).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})"
2756-
reveal_type(Sneaky(f9, 1, y=0, z='').kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})"
2755+
reveal_type(Sneaky(f9, 1, y=0).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str}, closed=True)"
2756+
reveal_type(Sneaky(f9, 1, y=0, z='').kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str}, closed=True)"
27572757
[builtins fixtures/paramspec.pyi]

0 commit comments

Comments
 (0)