From 59c2a2c8a0689da06d3b57ec055d5edb25f812bd Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Mon, 10 Aug 2026 21:16:52 -0400 Subject: [PATCH 1/5] update typeddict kwarg forwarding test --- .../results/mypy/callables_kwargs.toml | 51 +++++++++------ .../results/pycroscope/callables_kwargs.toml | 39 ++++++----- .../results/pyrefly/callables_kwargs.toml | 46 +++++++------ .../results/pyright/callables_kwargs.toml | 40 +++++++----- conformance/results/results.html | 65 ++++++++++++++----- conformance/results/ty/callables_kwargs.toml | 39 ++++++----- .../results/zuban/callables_kwargs.toml | 50 ++++++++------ conformance/tests/callables_kwargs.py | 55 +++++++++++++++- docs/spec/callables.rst | 6 +- 9 files changed, 265 insertions(+), 126 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 0586ed15b..4d8f7d5b0 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -1,29 +1,40 @@ conformant = "Partial" notes = """ Allows callable without kwargs to be assigned to callable with unpacked kwargs. +Does not support the `closed` and `extra_items` TypedDict class arguments. +Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. +Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. """ output = """ -callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:46: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:51: error: Unexpected keyword argument "v4" for "func1" [call-arg] -callables_kwargs.py:52: error: Too many positional arguments for "func1" [misc] -callables_kwargs.py:58: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] -callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "int" [arg-type] -callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "str" [arg-type] -callables_kwargs.py:63: error: "func1" gets multiple values for keyword argument "v1" [misc] -callables_kwargs.py:64: error: "func2" gets multiple values for keyword argument "v3" [misc] -callables_kwargs.py:64: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] -callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v1" [misc] -callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment] -callables_kwargs.py:101: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None" -callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment] -callables_kwargs.py:102: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None" -callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment] -callables_kwargs.py:103: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None" -callables_kwargs.py:111: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] -callables_kwargs.py:122: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:47: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:52: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:53: error: Too many positional arguments for "func1" [misc] +callables_kwargs.py:59: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "int" [arg-type] +callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "str" [arg-type] +callables_kwargs.py:64: error: "func1" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v3" [misc] +callables_kwargs.py:65: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:66: error: "func2" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:102: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None" +callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:103: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None" +callables_kwargs.py:104: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:104: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None" +callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] """ conformance_automated = "Fail" errors_diff = """ -Line 134: Expected 1 errors +Line 135: Expected 1 errors +Line 167: Expected 1 errors +Line 185: Expected 1 errors +Line 186: Expected 1 errors +Line 145: Unexpected errors ['callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] """ diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml index 492f041f5..a9d595e10 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -1,19 +1,28 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. +Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors +Line 185: Expected 1 errors +Line 186: Expected 1 errors """ output = """ -./callables_kwargs.py:46:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] -./callables_kwargs.py:51:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] -./callables_kwargs.py:52:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] -./callables_kwargs.py:58:4: Incompatible argument type for v1: expected int but got str [incompatible_argument] -./callables_kwargs.py:61:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] -./callables_kwargs.py:63:4: Multiple values provided for argument 'v1' [incompatible_call] -./callables_kwargs.py:64:4: In call to callables_kwargs.func2: Parameter 'v3' provided as both a positional and a keyword argument [incompatible_call] -./callables_kwargs.py:65:4: Multiple values provided for argument 'v1' [incompatible_call] -./callables_kwargs.py:101:0: Incompatible assignment: expected callables_kwargs.TDProtocol3, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:102:0: Incompatible assignment: expected callables_kwargs.TDProtocol4, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:103:0: Incompatible assignment: expected callables_kwargs.TDProtocol5, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:111:21: Parameter v1 overlaps with TypedDict key in **kwargs [invalid_annotation] -./callables_kwargs.py:122:12: Expected TypedDict type inside Unpack[] for **kwargs [invalid_annotation] -./callables_kwargs.py:134:0: Incompatible assignment: expected callables_kwargs.TDProtocol6, got function 'callables_kwargs.func7' [incompatible_assignment] +./callables_kwargs.py:47:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:52:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:53:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:59:4: Incompatible argument type for v1: expected int but got str [incompatible_argument] +./callables_kwargs.py:62:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:64:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:65:4: In call to callables_kwargs.func2: Parameter 'v3' provided as both a positional and a keyword argument [incompatible_call] +./callables_kwargs.py:66:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:102:0: Incompatible assignment: expected callables_kwargs.TDProtocol3, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:103:0: Incompatible assignment: expected callables_kwargs.TDProtocol4, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:104:0: Incompatible assignment: expected callables_kwargs.TDProtocol5, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:112:21: Parameter v1 overlaps with TypedDict key in **kwargs [invalid_annotation] +./callables_kwargs.py:123:12: Expected TypedDict type inside Unpack[] for **kwargs [invalid_annotation] +./callables_kwargs.py:135:0: Incompatible assignment: expected callables_kwargs.TDProtocol6, got function 'callables_kwargs.func7' [incompatible_assignment] """ diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml index d37554ce3..c268ef9ca 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -1,23 +1,31 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. +Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors +Line 185: Expected 1 errors +Line 186: Expected 1 errors """ output = """ -ERROR callables_kwargs.py:46:10-12: Missing argument `v1` in function `func1` [missing-argument] -ERROR callables_kwargs.py:46:10-12: Missing argument `v3` in function `func1` [missing-argument] -ERROR callables_kwargs.py:51:32-34: Unexpected keyword argument `v4` in function `func1` [unexpected-keyword] -ERROR callables_kwargs.py:52:11-12: Expected argument `v1` to be passed by name in function `func1` [unexpected-positional-argument] -ERROR callables_kwargs.py:52:11-12: Expected 0 positional arguments, got 3 in function `func1` [bad-argument-count] -ERROR callables_kwargs.py:52:14-16: Expected argument `v3` to be passed by name in function `func1` [unexpected-positional-argument] -ERROR callables_kwargs.py:58:11-20: Unpacked keyword argument `str` is not assignable to parameter `v1` with type `int` in function `func1` [bad-argument-type] -ERROR callables_kwargs.py:63:17-22: Multiple values for argument `v1` in function `func1` [bad-keyword-argument] -ERROR callables_kwargs.py:64:11-12: Argument `Literal[1]` is not assignable to parameter `v3` with type `str` in function `func2` [bad-argument-type] -ERROR callables_kwargs.py:64:14-19: Multiple values for argument `v3` in function `func2` [bad-keyword-argument] -ERROR callables_kwargs.py:65:17-22: Multiple values for argument `v1` in function `func2` [bad-keyword-argument] -ERROR callables_kwargs.py:101:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol3` [bad-assignment] -ERROR callables_kwargs.py:102:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol4` [bad-assignment] -ERROR callables_kwargs.py:103:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol5` [bad-assignment] -ERROR callables_kwargs.py:111:20-41: TypedDict key 'v1' in **kwargs overlaps with parameter 'v1' [bad-function-definition] -ERROR callables_kwargs.py:122:21-30: `Unpack` in **kwargs annotation must be used only with a `TypedDict` [invalid-annotation] -ERROR callables_kwargs.py:134:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol6` [bad-assignment] +ERROR callables_kwargs.py:47:10-12: Missing argument `v1` in function `func1` [missing-argument] +ERROR callables_kwargs.py:47:10-12: Missing argument `v3` in function `func1` [missing-argument] +ERROR callables_kwargs.py:52:32-34: Unexpected keyword argument `v4` in function `func1` [unexpected-keyword] +ERROR callables_kwargs.py:53:11-12: Expected argument `v1` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:53:11-12: Expected 0 positional arguments, got 3 in function `func1` [bad-argument-count] +ERROR callables_kwargs.py:53:14-16: Expected argument `v3` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:59:11-20: Unpacked keyword argument `str` is not assignable to parameter `v1` with type `int` in function `func1` [bad-argument-type] +ERROR callables_kwargs.py:64:17-22: Multiple values for argument `v1` in function `func1` [bad-keyword-argument] +ERROR callables_kwargs.py:65:11-12: Argument `Literal[1]` is not assignable to parameter `v3` with type `str` in function `func2` [bad-argument-type] +ERROR callables_kwargs.py:65:14-19: Multiple values for argument `v3` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:66:17-22: Multiple values for argument `v1` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:102:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol3` [bad-assignment] +ERROR callables_kwargs.py:103:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol4` [bad-assignment] +ERROR callables_kwargs.py:104:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol5` [bad-assignment] +ERROR callables_kwargs.py:112:20-41: TypedDict key 'v1' in **kwargs overlaps with parameter 'v1' [bad-function-definition] +ERROR callables_kwargs.py:123:21-30: `Unpack` in **kwargs annotation must be used only with a `TypedDict` [invalid-annotation] +ERROR callables_kwargs.py:135:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol6` [bad-assignment] """ diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 191d97a58..8cc86c821 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,36 +1,44 @@ -conformant = "Pass" +conformant = "Partial" +notes = """ +Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. +Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +""" output = """ -callables_kwargs.py:28:5 - error: Could not access item in TypedDict +callables_kwargs.py:29:5 - error: Could not access item in TypedDict   "v2" is not a required key in "TD2", so access may result in runtime exception (reportTypedDictNotRequiredAccess) -callables_kwargs.py:46:5 - error: Arguments missing for parameters "v1", "v3" (reportCallIssue) -callables_kwargs.py:51:32 - error: No parameter named "v4" (reportCallIssue) -callables_kwargs.py:52:11 - error: Expected 0 positional arguments (reportCallIssue) -callables_kwargs.py:58:13 - error: Argument of type "str" cannot be assigned to parameter "v1" of type "int" in function "func1" +callables_kwargs.py:47:5 - error: Arguments missing for parameters "v1", "v3" (reportCallIssue) +callables_kwargs.py:52:32 - error: No parameter named "v4" (reportCallIssue) +callables_kwargs.py:53:11 - error: Expected 0 positional arguments (reportCallIssue) +callables_kwargs.py:59:13 - error: Argument of type "str" cannot be assigned to parameter "v1" of type "int" in function "func1"   "str" is not assignable to "int" (reportArgumentType) -callables_kwargs.py:63:19 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:64:19 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v1" is already assigned (reportCallIssue) -callables_kwargs.py:64:16 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:65:16 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v3" is already assigned (reportCallIssue) -callables_kwargs.py:65:19 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:66:19 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v1" is already assigned (reportCallIssue) -callables_kwargs.py:101:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol3" +callables_kwargs.py:102:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol3"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int, v2: int, v3: str) -> None"     Keyword parameter "v2" of type "int" is incompatible with type "str"       "int" is not assignable to "str" (reportAssignmentType) -callables_kwargs.py:102:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol4" +callables_kwargs.py:103:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol4"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int) -> None"     Extra parameter "v3" (reportAssignmentType) -callables_kwargs.py:103:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol5" +callables_kwargs.py:104:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol5"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(v1: int, v3: str) -> None"     Function accepts too many positional parameters; expected 0 but received 2       Extra parameter "v1"       Extra parameter "v3" (reportAssignmentType) -callables_kwargs.py:111:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) -callables_kwargs.py:122:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) -callables_kwargs.py:134:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6" +callables_kwargs.py:112:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) +callables_kwargs.py:123:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) +callables_kwargs.py:135:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6"   Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to type "(**kwargs: **TD2) -> None"     Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +callables_kwargs.py:186:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "int" is not assignable to "str" (reportArgumentType) """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors +Line 185: Expected 1 errors """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 4de25dec5..9b7b10813 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1509,13 +1509,48 @@

Python Type System Conformance Test Results

Partial + + + Partial + + + + Partial + + + + Partial + + + + Partial + + + + Partial + - Pass - Pass - Pass - Pass - Pass callables_protocol @@ -1538,11 +1573,11 @@

Python Type System Conformance Test Results

3 / 4 • 75.0% - 4 / 4 • 100.0% - 4 / 4 • 100.0% - 4 / 4 • 100.0% - 4 / 4 • 100.0% - 4 / 4 • 100.0% + 3.5 / 4 • 87.5% + 3.5 / 4 • 87.5% + 3.5 / 4 • 87.5% + 3.5 / 4 • 87.5% + 3.5 / 4 • 87.5% @@ -2652,11 +2687,11 @@

Python Type System Conformance Test Results

108 / 142 • 76.1% - 130 / 142 • 91.5% - 141 / 142 • 99.3% - 136 / 142 • 95.8% - 125.5 / 142 • 88.4% - 140 / 142 • 98.6% + 129.5 / 142 • 91.2% + 140.5 / 142 • 98.9% + 135.5 / 142 • 95.4% + 125 / 142 • 88.0% + 139.5 / 142 • 98.2% diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml index 3318d6bb6..6f9c4395a 100644 --- a/conformance/results/ty/callables_kwargs.toml +++ b/conformance/results/ty/callables_kwargs.toml @@ -1,20 +1,27 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not reject passing unpacked kwargs typed with an open TypedDict to a callable that has no `**kwargs`. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors """ output = """ -callables_kwargs.py:46:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` -callables_kwargs.py:52:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` -callables_kwargs.py:52:11: error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3 -callables_kwargs.py:58:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `str` -callables_kwargs.py:61:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `int | str` -callables_kwargs.py:63:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1` -callables_kwargs.py:64:11: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected `str`, found `Literal[1]` -callables_kwargs.py:64:14: error[parameter-already-assigned] Multiple values provided for parameter `v3` of function `func2` -callables_kwargs.py:65:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2` -callables_kwargs.py:101:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol3` -callables_kwargs.py:102:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol4` -callables_kwargs.py:103:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol5` -callables_kwargs.py:111:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation -callables_kwargs.py:122:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` -callables_kwargs.py:134:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` +callables_kwargs.py:47:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:53:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:53:11: error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3 +callables_kwargs.py:59:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `str` +callables_kwargs.py:62:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `int | str` +callables_kwargs.py:64:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1` +callables_kwargs.py:65:11: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected `str`, found `Literal[1]` +callables_kwargs.py:65:14: error[parameter-already-assigned] Multiple values provided for parameter `v3` of function `func2` +callables_kwargs.py:66:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2` +callables_kwargs.py:102:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol3` +callables_kwargs.py:103:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol4` +callables_kwargs.py:104:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol5` +callables_kwargs.py:112:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation +callables_kwargs.py:123:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` +callables_kwargs.py:135:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` +callables_kwargs.py:185:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` +callables_kwargs.py:186:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` """ diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml index 678f5d318..8d57e8090 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -1,25 +1,33 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. +Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +""" +conformance_automated = "Fail" errors_diff = """ +Line 167: Expected 1 errors +Line 185: Expected 1 errors """ output = """ -callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:46: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:51: error: Unexpected keyword argument "v4" for "func1" [call-arg] -callables_kwargs.py:52: error: Too many positional arguments for "func1" [call-arg] -callables_kwargs.py:52: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:52: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:58: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] -callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "int" [arg-type] -callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] -callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] -callables_kwargs.py:63: error: "func1" gets multiple values for keyword argument "v1" [call-arg] -callables_kwargs.py:64: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] -callables_kwargs.py:64: error: "func2" gets multiple values for keyword argument "v3" [call-arg] -callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v1" [call-arg] -callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment] -callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment] -callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment] -callables_kwargs.py:111: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] -callables_kwargs.py:122: error: Unpack item in ** parameter must be a TypedDict [misc] -callables_kwargs.py:134: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] +callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:47: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:52: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:53: error: Too many positional arguments for "func1" [call-arg] +callables_kwargs.py:53: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:53: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:59: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "int" [arg-type] +callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:64: error: "func1" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:65: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v3" [call-arg] +callables_kwargs.py:66: error: "func2" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:104: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:135: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] +callables_kwargs.py:186: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD5"; expected "str" [arg-type] """ diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index a4c431dbd..58d165c84 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -7,7 +7,8 @@ # This sample tests the handling of Unpack[TypedDict] when used with # a **kwargs parameter in a function signature. -from typing import Protocol, TypeVar, TypedDict, NotRequired, Required, Unpack, assert_type +from typing import Protocol, TypeVar, NotRequired, Required, Unpack, assert_type +from typing_extensions import TypedDict # for the `closed` and `extra_items` arguments class TD1(TypedDict): @@ -132,3 +133,55 @@ def func7(*, v1: int, v3: str, v2: str = "") -> None: v7: TDProtocol6 = func7 # E: source does not have kwargs + + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#passing-kwargs-inside-a-function-to-another-function + + +class TD3(TypedDict): + name: str + + +class TD4(TypedDict, closed=True): + name: str + + +class TD5(TypedDict, extra_items=int): + name: str + + +def takes_name(name: str) -> None: + ... + + +def takes_name_kwargs(name: str, **kwargs) -> None: + ... + + +# > ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` can only +# > be passed to another function if the function to which unpacked kwargs are being +# > passed to has ``**kwargs`` in its signature as well, because then additional +# > keywords would not cause errors at runtime during function invocation. + +def func8(**kwargs: Unpack[TD3]) -> None: + takes_name(**kwargs) # E: a subtype may contain unknown keys + takes_name_kwargs(**kwargs) + + +def func9(**kwargs: Unpack[TD4]) -> None: + takes_name(**kwargs) + takes_name_kwargs(**kwargs) + + +def takes_name_str_kwargs(name: str, **kwargs: str) -> None: + ... + + +def takes_name_int_kwargs(name: str, **kwargs: int) -> None: + ... + + +def func10(**kwargs: Unpack[TD5]) -> None: + takes_name(**kwargs) # E: extra items may be present + takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible + takes_name_int_kwargs(**kwargs) diff --git a/docs/spec/callables.rst b/docs/spec/callables.rst index f23220400..785b74af0 100644 --- a/docs/spec/callables.rst +++ b/docs/spec/callables.rst @@ -413,15 +413,15 @@ it needs completely ignoring any additional values. The calls to ``bar`` and ``spam`` will fail because an unexpected keyword argument will be passed to the ``takes_name`` function. -Therefore, ``kwargs`` hinted with an unpacked ``TypedDict`` can only be passed +Therefore, ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` can only be passed to another function if the function to which unpacked kwargs are being passed to has ``**kwargs`` in its signature as well, because then additional keywords would not cause errors at runtime during function invocation. Otherwise, the type checker should generate an error. In cases similar to the ``bar`` function above the problem could be worked -around by explicitly dereferencing desired fields and using them as arguments -to perform the function call:: +around by marking ``Animal`` with ``closed=True``, or by explicitly dereferencing desired +fields and using them as arguments to perform the function call:: def bar(**kwargs: Unpack[Animal]): name = kwargs["name"] From 8368356636802ffd91f1e0f0f6dd5c2c3b974f48 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Tue, 11 Aug 2026 23:27:49 -0400 Subject: [PATCH 2/5] update spec wording and results to make erroring on implicitly open TypedDicts optional --- .../results/mypy/callables_kwargs.toml | 14 +++++++---- .../results/pycroscope/callables_kwargs.toml | 6 ++--- .../results/pyrefly/callables_kwargs.toml | 6 ++--- .../results/pyright/callables_kwargs.toml | 8 +++--- conformance/results/ty/callables_kwargs.toml | 6 ++--- .../results/zuban/callables_kwargs.toml | 8 +++--- conformance/tests/callables_kwargs.py | 25 +++++++++++++------ docs/spec/callables.rst | 9 +++---- 8 files changed, 49 insertions(+), 33 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 4d8f7d5b0..030d23629 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -27,14 +27,18 @@ callables_kwargs.py:104: note: "TDProtocol5.__call__" has type "def __call__(sel callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] -callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:149: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:153: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef] """ conformance_automated = "Fail" errors_diff = """ Line 135: Expected 1 errors -Line 167: Expected 1 errors -Line 185: Expected 1 errors -Line 186: Expected 1 errors +Line 176: Expected 1 errors +Line 194: Expected 1 errors +Line 195: Expected 1 errors Line 145: Unexpected errors ['callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] -Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 153: Unexpected errors ['callables_kwargs.py:153: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 193: Unexpected errors ['callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef]'] """ diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml index a9d595e10..107baaaff 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -6,9 +6,9 @@ Does not check the `extra_items` type of unpacked kwargs against the target call """ conformance_automated = "Fail" errors_diff = """ -Line 167: Expected 1 errors -Line 185: Expected 1 errors -Line 186: Expected 1 errors +Line 176: Expected 1 errors +Line 194: Expected 1 errors +Line 195: Expected 1 errors """ output = """ ./callables_kwargs.py:47:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml index c268ef9ca..bb38eee0b 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -6,9 +6,9 @@ Does not check the `extra_items` type of unpacked kwargs against the target call """ conformance_automated = "Fail" errors_diff = """ -Line 167: Expected 1 errors -Line 185: Expected 1 errors -Line 186: Expected 1 errors +Line 176: Expected 1 errors +Line 194: Expected 1 errors +Line 195: Expected 1 errors """ output = """ ERROR callables_kwargs.py:47:10-12: Missing argument `v1` in function `func1` [missing-argument] diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 8cc86c821..2f5dcf49c 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -34,11 +34,13 @@ callables_kwargs.py:123:21 - error: Expected TypedDict type argument for Unpack callables_kwargs.py:135:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6"   Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to type "(**kwargs: **TD2) -> None"     Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) -callables_kwargs.py:186:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +callables_kwargs.py:180:5 - error: Function declaration "func10" is obscured by a declaration of the same name (reportRedeclaration) +callables_kwargs.py:195:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs"   "int" is not assignable to "str" (reportArgumentType) """ conformance_automated = "Fail" errors_diff = """ -Line 167: Expected 1 errors -Line 185: Expected 1 errors +Line 176: Expected 1 errors +Line 194: Expected 1 errors +Line 180: Unexpected errors ['callables_kwargs.py:180:5 - error: Function declaration "func10" is obscured by a declaration of the same name (reportRedeclaration)'] """ diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml index 6f9c4395a..8a5170e4e 100644 --- a/conformance/results/ty/callables_kwargs.toml +++ b/conformance/results/ty/callables_kwargs.toml @@ -4,7 +4,7 @@ Does not reject passing unpacked kwargs typed with an open TypedDict to a callab """ conformance_automated = "Fail" errors_diff = """ -Line 167: Expected 1 errors +Line 176: Expected 1 errors """ output = """ callables_kwargs.py:47:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` @@ -22,6 +22,6 @@ callables_kwargs.py:104:19: error[invalid-assignment] Object of type `def func1( callables_kwargs.py:112:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation callables_kwargs.py:123:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` callables_kwargs.py:135:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` -callables_kwargs.py:185:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` -callables_kwargs.py:186:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` +callables_kwargs.py:194:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` +callables_kwargs.py:195:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` """ diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml index 8d57e8090..500efa063 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -5,8 +5,9 @@ Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_i """ conformance_automated = "Fail" errors_diff = """ -Line 167: Expected 1 errors -Line 185: Expected 1 errors +Line 176: Expected 1 errors +Line 194: Expected 1 errors +Line 193: Unexpected errors ['callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef]'] """ output = """ callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] @@ -29,5 +30,6 @@ callables_kwargs.py:104: error: Incompatible types in assignment (expression has callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] callables_kwargs.py:135: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] -callables_kwargs.py:186: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD5"; expected "str" [arg-type] +callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef] +callables_kwargs.py:195: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD6"; expected "str" [arg-type] """ diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index 58d165c84..22091a618 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -142,11 +142,15 @@ class TD3(TypedDict): name: str -class TD4(TypedDict, closed=True): +class TD4(TypedDict, closed=False): name: str -class TD5(TypedDict, extra_items=int): +class TD5(TypedDict, closed=True): + name: str + + +class TD6(TypedDict, extra_items=int): name: str @@ -158,17 +162,22 @@ def takes_name_kwargs(name: str, **kwargs) -> None: ... -# > ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` can only -# > be passed to another function if the function to which unpacked kwargs are being -# > passed to has ``**kwargs`` in its signature as well, because then additional -# > keywords would not cause errors at runtime during function invocation. +# > It is only safe to pass ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` +# > to another function if that function has ``**kwargs`` in its signature as well. In this situation, +# > type checkers should error if the ``TypedDict`` is explicitly open (``closed=False``) or has ``extra_items``. +# > Additionally, type checkers may error if the ``TypedDict`` is implicitly open. def func8(**kwargs: Unpack[TD3]) -> None: - takes_name(**kwargs) # E: a subtype may contain unknown keys + takes_name(**kwargs) # E?: a subtype may contain unknown keys takes_name_kwargs(**kwargs) def func9(**kwargs: Unpack[TD4]) -> None: + takes_name(**kwargs) # E: a subtype may contain unknown keys + takes_name_kwargs(**kwargs) + + +def func10(**kwargs: Unpack[TD5]) -> None: takes_name(**kwargs) takes_name_kwargs(**kwargs) @@ -181,7 +190,7 @@ def takes_name_int_kwargs(name: str, **kwargs: int) -> None: ... -def func10(**kwargs: Unpack[TD5]) -> None: +def func10(**kwargs: Unpack[TD6]) -> None: takes_name(**kwargs) # E: extra items may be present takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible takes_name_int_kwargs(**kwargs) diff --git a/docs/spec/callables.rst b/docs/spec/callables.rst index 785b74af0..bf4dc383e 100644 --- a/docs/spec/callables.rst +++ b/docs/spec/callables.rst @@ -413,11 +413,10 @@ it needs completely ignoring any additional values. The calls to ``bar`` and ``spam`` will fail because an unexpected keyword argument will be passed to the ``takes_name`` function. -Therefore, ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` can only be passed -to another function if the function to which unpacked kwargs are being passed -to has ``**kwargs`` in its signature as well, because then additional keywords -would not cause errors at runtime during function invocation. Otherwise, the -type checker should generate an error. +Therefore, it is only safe to pass ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` +to another function if that function has ``**kwargs`` in its signature as well. In this situation, +type checkers should error if the ``TypedDict`` is explicitly open (``closed=False``) or has ``extra_items``. +Additionally, type checkers may error if the ``TypedDict`` is implicitly open. In cases similar to the ``bar`` function above the problem could be worked around by marking ``Animal`` with ``closed=True``, or by explicitly dereferencing desired From 2678723908e0cad8edca867bf32880a71ac8ef07 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 13 Aug 2026 08:19:39 -0400 Subject: [PATCH 3/5] link to terms & handle implicitly open typeddicts the same as explicitly open ones --- .../results/mypy/callables_kwargs.toml | 13 ++++-------- .../results/pycroscope/callables_kwargs.toml | 5 ++--- .../results/pyrefly/callables_kwargs.toml | 5 ++--- .../results/pyright/callables_kwargs.toml | 7 ++----- conformance/results/results.html | 11 +++------- conformance/results/ty/callables_kwargs.toml | 12 ++++------- .../results/zuban/callables_kwargs.toml | 7 ++----- conformance/tests/callables_kwargs.py | 20 +++++-------------- docs/spec/callables.rst | 7 +++---- 9 files changed, 27 insertions(+), 60 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 030d23629..eec7a9a13 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -27,18 +27,13 @@ callables_kwargs.py:104: note: "TDProtocol5.__call__" has type "def __call__(sel callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] -callables_kwargs.py:149: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] -callables_kwargs.py:153: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] -callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef] +callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] """ conformance_automated = "Fail" errors_diff = """ Line 135: Expected 1 errors -Line 176: Expected 1 errors -Line 194: Expected 1 errors -Line 195: Expected 1 errors +Line 184: Expected 1 errors +Line 185: Expected 1 errors Line 145: Unexpected errors ['callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] -Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] -Line 153: Unexpected errors ['callables_kwargs.py:153: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] -Line 193: Unexpected errors ['callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef]'] +Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] """ diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml index 107baaaff..710649de2 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -6,9 +6,8 @@ Does not check the `extra_items` type of unpacked kwargs against the target call """ conformance_automated = "Fail" errors_diff = """ -Line 176: Expected 1 errors -Line 194: Expected 1 errors -Line 195: Expected 1 errors +Line 184: Expected 1 errors +Line 185: Expected 1 errors """ output = """ ./callables_kwargs.py:47:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml index bb38eee0b..666a28973 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -6,9 +6,8 @@ Does not check the `extra_items` type of unpacked kwargs against the target call """ conformance_automated = "Fail" errors_diff = """ -Line 176: Expected 1 errors -Line 194: Expected 1 errors -Line 195: Expected 1 errors +Line 184: Expected 1 errors +Line 185: Expected 1 errors """ output = """ ERROR callables_kwargs.py:47:10-12: Missing argument `v1` in function `func1` [missing-argument] diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 2f5dcf49c..8173c49e4 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -34,13 +34,10 @@ callables_kwargs.py:123:21 - error: Expected TypedDict type argument for Unpack callables_kwargs.py:135:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6"   Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to type "(**kwargs: **TD2) -> None"     Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) -callables_kwargs.py:180:5 - error: Function declaration "func10" is obscured by a declaration of the same name (reportRedeclaration) -callables_kwargs.py:195:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +callables_kwargs.py:185:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs"   "int" is not assignable to "str" (reportArgumentType) """ conformance_automated = "Fail" errors_diff = """ -Line 176: Expected 1 errors -Line 194: Expected 1 errors -Line 180: Unexpected errors ['callables_kwargs.py:180:5 - error: Function declaration "func10" is obscured by a declaration of the same name (reportRedeclaration)'] +Line 184: Expected 1 errors """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 9b7b10813..be5d0b8fa 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1538,12 +1538,7 @@

Python Type System Conformance Test Results

  • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
  • - - Partial -
      -
    • Does not reject passing unpacked kwargs typed with an open TypedDict to a callable that has no **kwargs.
    • -
    - + Pass Partial
      @@ -1576,7 +1571,7 @@

      Python Type System Conformance Test Results

      3.5 / 4 • 87.5% 3.5 / 4 • 87.5% 3.5 / 4 • 87.5% - 3.5 / 4 • 87.5% + 4 / 4 • 100.0% 3.5 / 4 • 87.5% @@ -2690,7 +2685,7 @@

      Python Type System Conformance Test Results

      129.5 / 142 • 91.2% 140.5 / 142 • 98.9% 135.5 / 142 • 95.4% - 125 / 142 • 88.0% + 125.5 / 142 • 88.4% 139.5 / 142 • 98.2% diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml index 8a5170e4e..44aa885c3 100644 --- a/conformance/results/ty/callables_kwargs.toml +++ b/conformance/results/ty/callables_kwargs.toml @@ -1,10 +1,6 @@ -conformant = "Partial" -notes = """ -Does not reject passing unpacked kwargs typed with an open TypedDict to a callable that has no `**kwargs`. -""" -conformance_automated = "Fail" +conformant = "Pass" +conformance_automated = "Pass" errors_diff = """ -Line 176: Expected 1 errors """ output = """ callables_kwargs.py:47:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` @@ -22,6 +18,6 @@ callables_kwargs.py:104:19: error[invalid-assignment] Object of type `def func1( callables_kwargs.py:112:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation callables_kwargs.py:123:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` callables_kwargs.py:135:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` -callables_kwargs.py:194:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` -callables_kwargs.py:195:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` +callables_kwargs.py:184:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` +callables_kwargs.py:185:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` """ diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml index 500efa063..92f8aec34 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -5,9 +5,7 @@ Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_i """ conformance_automated = "Fail" errors_diff = """ -Line 176: Expected 1 errors -Line 194: Expected 1 errors -Line 193: Unexpected errors ['callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef]'] +Line 184: Expected 1 errors """ output = """ callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] @@ -30,6 +28,5 @@ callables_kwargs.py:104: error: Incompatible types in assignment (expression has callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] callables_kwargs.py:135: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] -callables_kwargs.py:193: error: Name "func10" already defined on line 180 [no-redef] -callables_kwargs.py:195: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD6"; expected "str" [arg-type] +callables_kwargs.py:185: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD5"; expected "str" [arg-type] """ diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index 22091a618..cc8b9df93 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -142,15 +142,11 @@ class TD3(TypedDict): name: str -class TD4(TypedDict, closed=False): +class TD4(TypedDict, closed=True): name: str -class TD5(TypedDict, closed=True): - name: str - - -class TD6(TypedDict, extra_items=int): +class TD5(TypedDict, extra_items=int): name: str @@ -163,9 +159,8 @@ def takes_name_kwargs(name: str, **kwargs) -> None: # > It is only safe to pass ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` -# > to another function if that function has ``**kwargs`` in its signature as well. In this situation, -# > type checkers should error if the ``TypedDict`` is explicitly open (``closed=False``) or has ``extra_items``. -# > Additionally, type checkers may error if the ``TypedDict`` is implicitly open. +# > to another function if that function has ``**kwargs`` in its signature as well. Type checkers +# > should error if the ``TypedDict`` has ``extra_items``, and may error if the ``TypedDict`` is open. def func8(**kwargs: Unpack[TD3]) -> None: takes_name(**kwargs) # E?: a subtype may contain unknown keys @@ -173,11 +168,6 @@ def func8(**kwargs: Unpack[TD3]) -> None: def func9(**kwargs: Unpack[TD4]) -> None: - takes_name(**kwargs) # E: a subtype may contain unknown keys - takes_name_kwargs(**kwargs) - - -def func10(**kwargs: Unpack[TD5]) -> None: takes_name(**kwargs) takes_name_kwargs(**kwargs) @@ -190,7 +180,7 @@ def takes_name_int_kwargs(name: str, **kwargs: int) -> None: ... -def func10(**kwargs: Unpack[TD6]) -> None: +def func10(**kwargs: Unpack[TD5]) -> None: takes_name(**kwargs) # E: extra items may be present takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible takes_name_int_kwargs(**kwargs) diff --git a/docs/spec/callables.rst b/docs/spec/callables.rst index bf4dc383e..b29e70292 100644 --- a/docs/spec/callables.rst +++ b/docs/spec/callables.rst @@ -413,10 +413,9 @@ it needs completely ignoring any additional values. The calls to ``bar`` and ``spam`` will fail because an unexpected keyword argument will be passed to the ``takes_name`` function. -Therefore, it is only safe to pass ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` -to another function if that function has ``**kwargs`` in its signature as well. In this situation, -type checkers should error if the ``TypedDict`` is explicitly open (``closed=False``) or has ``extra_items``. -Additionally, type checkers may error if the ``TypedDict`` is implicitly open. +Therefore, it is only safe to pass ``kwargs`` hinted with an unpacked, non-:term:`closed` ``TypedDict`` +to another function if that function has ``**kwargs`` in its signature as well. Type checkers should +error if the ``TypedDict`` has :term:`extra items`, and may error if the ``TypedDict`` is :term:`open`. In cases similar to the ``bar`` function above the problem could be worked around by marking ``Animal`` with ``closed=True``, or by explicitly dereferencing desired From 846feb74a6e134c2fd758071229292477f8ade93 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 13 Aug 2026 15:39:28 -0400 Subject: [PATCH 4/5] address reviewer comments, revise spec and add a bunch of test cases --- .../results/mypy/callables_kwargs.toml | 67 +++++++------ .../results/pycroscope/callables_kwargs.toml | 43 +++++---- .../results/pyrefly/callables_kwargs.toml | 47 +++++---- .../results/pyright/callables_kwargs.toml | 44 +++++---- conformance/results/results.html | 5 - conformance/results/ty/callables_kwargs.toml | 40 ++++---- .../results/zuban/callables_kwargs.toml | 55 ++++++----- conformance/tests/callables_kwargs.py | 95 +++++++++++++++---- docs/spec/callables.rst | 17 ++-- 9 files changed, 261 insertions(+), 152 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index eec7a9a13..764525eb9 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -2,38 +2,51 @@ conformant = "Partial" notes = """ Allows callable without kwargs to be assigned to callable with unpacked kwargs. Does not support the `closed` and `extra_items` TypedDict class arguments. -Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. """ output = """ -callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:47: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:52: error: Unexpected keyword argument "v4" for "func1" [call-arg] -callables_kwargs.py:53: error: Too many positional arguments for "func1" [misc] -callables_kwargs.py:59: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] -callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "int" [arg-type] -callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "str" [arg-type] -callables_kwargs.py:64: error: "func1" gets multiple values for keyword argument "v1" [misc] -callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v3" [misc] -callables_kwargs.py:65: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] -callables_kwargs.py:66: error: "func2" gets multiple values for keyword argument "v1" [misc] -callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment] -callables_kwargs.py:102: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None" -callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment] -callables_kwargs.py:103: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None" -callables_kwargs.py:104: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment] -callables_kwargs.py:104: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None" -callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] -callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] -callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] -callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:23: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:27: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:55: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:55: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:60: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:61: error: Too many positional arguments for "func1" [misc] +callables_kwargs.py:67: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:70: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "int" [arg-type] +callables_kwargs.py:70: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "str" [arg-type] +callables_kwargs.py:72: error: "func1" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:73: error: "func2" gets multiple values for keyword argument "v3" [misc] +callables_kwargs.py:73: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:74: error: "func2" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:121: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:121: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None" +callables_kwargs.py:122: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:122: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None" +callables_kwargs.py:123: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:123: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None" +callables_kwargs.py:131: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:142: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:166: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:170: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:174: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +callables_kwargs.py:178: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] """ conformance_automated = "Fail" errors_diff = """ -Line 135: Expected 1 errors -Line 184: Expected 1 errors -Line 185: Expected 1 errors -Line 145: Unexpected errors ['callables_kwargs.py:145: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] -Line 149: Unexpected errors ['callables_kwargs.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 154: Expected 1 errors +Line 206: Expected 1 errors +Line 209: Expected 1 errors +Line 215: Expected 1 errors +Line 218: Expected 1 errors +Line 231: Expected 1 errors +Line 232: Expected 1 errors +Line 234: Expected 1 errors +Line 235: Expected 1 errors +Line 23: Unexpected errors ['callables_kwargs.py:23: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 27: Unexpected errors ['callables_kwargs.py:27: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 166: Unexpected errors ['callables_kwargs.py:166: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 170: Unexpected errors ['callables_kwargs.py:170: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 174: Unexpected errors ['callables_kwargs.py:174: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 178: Unexpected errors ['callables_kwargs.py:178: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] """ diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml index 710649de2..9e69aac7b 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -1,27 +1,36 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. """ conformance_automated = "Fail" errors_diff = """ -Line 184: Expected 1 errors -Line 185: Expected 1 errors +Line 206: Expected 1 errors +Line 209: Expected 1 errors +Line 215: Expected 1 errors +Line 218: Expected 1 errors +Line 231: Expected 1 errors +Line 232: Expected 1 errors +Line 234: Expected 1 errors +Line 235: Expected 1 errors +Line 155: Unexpected errors ["./callables_kwargs.py:155:0: Incompatible assignment: expected callables_kwargs.TDProtocol7, got function 'callables_kwargs.func7' [incompatible_assignment]"] +Line 156: Unexpected errors ["./callables_kwargs.py:156:0: Incompatible assignment: expected callables_kwargs.TDProtocol8, got function 'callables_kwargs.func7' [incompatible_assignment]"] """ output = """ -./callables_kwargs.py:47:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] -./callables_kwargs.py:52:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] -./callables_kwargs.py:53:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] -./callables_kwargs.py:59:4: Incompatible argument type for v1: expected int but got str [incompatible_argument] -./callables_kwargs.py:62:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] -./callables_kwargs.py:64:4: Multiple values provided for argument 'v1' [incompatible_call] -./callables_kwargs.py:65:4: In call to callables_kwargs.func2: Parameter 'v3' provided as both a positional and a keyword argument [incompatible_call] -./callables_kwargs.py:66:4: Multiple values provided for argument 'v1' [incompatible_call] -./callables_kwargs.py:102:0: Incompatible assignment: expected callables_kwargs.TDProtocol3, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:103:0: Incompatible assignment: expected callables_kwargs.TDProtocol4, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:104:0: Incompatible assignment: expected callables_kwargs.TDProtocol5, got function 'callables_kwargs.func1' [incompatible_assignment] -./callables_kwargs.py:112:21: Parameter v1 overlaps with TypedDict key in **kwargs [invalid_annotation] -./callables_kwargs.py:123:12: Expected TypedDict type inside Unpack[] for **kwargs [invalid_annotation] -./callables_kwargs.py:135:0: Incompatible assignment: expected callables_kwargs.TDProtocol6, got function 'callables_kwargs.func7' [incompatible_assignment] +./callables_kwargs.py:55:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:60:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:61:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:67:4: Incompatible argument type for v1: expected int but got str [incompatible_argument] +./callables_kwargs.py:70:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:72:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:73:4: In call to callables_kwargs.func2: Parameter 'v3' provided as both a positional and a keyword argument [incompatible_call] +./callables_kwargs.py:74:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:121:0: Incompatible assignment: expected callables_kwargs.TDProtocol3, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:122:0: Incompatible assignment: expected callables_kwargs.TDProtocol4, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:123:0: Incompatible assignment: expected callables_kwargs.TDProtocol5, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:131:21: Parameter v1 overlaps with TypedDict key in **kwargs [invalid_annotation] +./callables_kwargs.py:142:12: Expected TypedDict type inside Unpack[] for **kwargs [invalid_annotation] +./callables_kwargs.py:154:0: Incompatible assignment: expected callables_kwargs.TDProtocol6, got function 'callables_kwargs.func7' [incompatible_assignment] +./callables_kwargs.py:155:0: Incompatible assignment: expected callables_kwargs.TDProtocol7, got function 'callables_kwargs.func7' [incompatible_assignment] +./callables_kwargs.py:156:0: Incompatible assignment: expected callables_kwargs.TDProtocol8, got function 'callables_kwargs.func7' [incompatible_assignment] """ diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml index 666a28973..b15c28537 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -1,30 +1,37 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. """ conformance_automated = "Fail" errors_diff = """ -Line 184: Expected 1 errors -Line 185: Expected 1 errors +Line 206: Expected 1 errors +Line 209: Expected 1 errors +Line 215: Expected 1 errors +Line 218: Expected 1 errors +Line 231: Expected 1 errors +Line 232: Expected 1 errors +Line 234: Expected 1 errors +Line 235: Expected 1 errors +Line 156: Unexpected errors ["`(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol8` [bad-assignment]"] """ output = """ -ERROR callables_kwargs.py:47:10-12: Missing argument `v1` in function `func1` [missing-argument] -ERROR callables_kwargs.py:47:10-12: Missing argument `v3` in function `func1` [missing-argument] -ERROR callables_kwargs.py:52:32-34: Unexpected keyword argument `v4` in function `func1` [unexpected-keyword] -ERROR callables_kwargs.py:53:11-12: Expected argument `v1` to be passed by name in function `func1` [unexpected-positional-argument] -ERROR callables_kwargs.py:53:11-12: Expected 0 positional arguments, got 3 in function `func1` [bad-argument-count] -ERROR callables_kwargs.py:53:14-16: Expected argument `v3` to be passed by name in function `func1` [unexpected-positional-argument] -ERROR callables_kwargs.py:59:11-20: Unpacked keyword argument `str` is not assignable to parameter `v1` with type `int` in function `func1` [bad-argument-type] -ERROR callables_kwargs.py:64:17-22: Multiple values for argument `v1` in function `func1` [bad-keyword-argument] -ERROR callables_kwargs.py:65:11-12: Argument `Literal[1]` is not assignable to parameter `v3` with type `str` in function `func2` [bad-argument-type] -ERROR callables_kwargs.py:65:14-19: Multiple values for argument `v3` in function `func2` [bad-keyword-argument] -ERROR callables_kwargs.py:66:17-22: Multiple values for argument `v1` in function `func2` [bad-keyword-argument] -ERROR callables_kwargs.py:102:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol3` [bad-assignment] -ERROR callables_kwargs.py:103:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol4` [bad-assignment] -ERROR callables_kwargs.py:104:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol5` [bad-assignment] -ERROR callables_kwargs.py:112:20-41: TypedDict key 'v1' in **kwargs overlaps with parameter 'v1' [bad-function-definition] -ERROR callables_kwargs.py:123:21-30: `Unpack` in **kwargs annotation must be used only with a `TypedDict` [invalid-annotation] -ERROR callables_kwargs.py:135:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol6` [bad-assignment] +ERROR callables_kwargs.py:55:10-12: Missing argument `v1` in function `func1` [missing-argument] +ERROR callables_kwargs.py:55:10-12: Missing argument `v3` in function `func1` [missing-argument] +ERROR callables_kwargs.py:60:32-34: Unexpected keyword argument `v4` in function `func1` [unexpected-keyword] +ERROR callables_kwargs.py:61:11-12: Expected argument `v1` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:61:11-12: Expected 0 positional arguments, got 3 in function `func1` [bad-argument-count] +ERROR callables_kwargs.py:61:14-16: Expected argument `v3` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:67:11-20: Unpacked keyword argument `str` is not assignable to parameter `v1` with type `int` in function `func1` [bad-argument-type] +ERROR callables_kwargs.py:72:17-22: Multiple values for argument `v1` in function `func1` [bad-keyword-argument] +ERROR callables_kwargs.py:73:11-12: Argument `Literal[1]` is not assignable to parameter `v3` with type `str` in function `func2` [bad-argument-type] +ERROR callables_kwargs.py:73:14-19: Multiple values for argument `v3` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:74:17-22: Multiple values for argument `v1` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:121:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol3` [bad-assignment] +ERROR callables_kwargs.py:122:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol4` [bad-assignment] +ERROR callables_kwargs.py:123:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol5` [bad-assignment] +ERROR callables_kwargs.py:131:20-41: TypedDict key 'v1' in **kwargs overlaps with parameter 'v1' [bad-function-definition] +ERROR callables_kwargs.py:142:21-30: `Unpack` in **kwargs annotation must be used only with a `TypedDict` [invalid-annotation] +ERROR callables_kwargs.py:154:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol6` [bad-assignment] +ERROR callables_kwargs.py:156:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol8` [bad-assignment] """ diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index 8173c49e4..c70be3c93 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,43 +1,53 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. """ output = """ -callables_kwargs.py:29:5 - error: Could not access item in TypedDict +callables_kwargs.py:37:5 - error: Could not access item in TypedDict   "v2" is not a required key in "TD2", so access may result in runtime exception (reportTypedDictNotRequiredAccess) -callables_kwargs.py:47:5 - error: Arguments missing for parameters "v1", "v3" (reportCallIssue) -callables_kwargs.py:52:32 - error: No parameter named "v4" (reportCallIssue) -callables_kwargs.py:53:11 - error: Expected 0 positional arguments (reportCallIssue) -callables_kwargs.py:59:13 - error: Argument of type "str" cannot be assigned to parameter "v1" of type "int" in function "func1" +callables_kwargs.py:55:5 - error: Arguments missing for parameters "v1", "v3" (reportCallIssue) +callables_kwargs.py:60:32 - error: No parameter named "v4" (reportCallIssue) +callables_kwargs.py:61:11 - error: Expected 0 positional arguments (reportCallIssue) +callables_kwargs.py:67:13 - error: Argument of type "str" cannot be assigned to parameter "v1" of type "int" in function "func1"   "str" is not assignable to "int" (reportArgumentType) -callables_kwargs.py:64:19 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:72:19 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v1" is already assigned (reportCallIssue) -callables_kwargs.py:65:16 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:73:16 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v3" is already assigned (reportCallIssue) -callables_kwargs.py:66:19 - error: Unable to match unpacked TypedDict argument to parameters +callables_kwargs.py:74:19 - error: Unable to match unpacked TypedDict argument to parameters   Parameter "v1" is already assigned (reportCallIssue) -callables_kwargs.py:102:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol3" +callables_kwargs.py:121:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol3"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int, v2: int, v3: str) -> None"     Keyword parameter "v2" of type "int" is incompatible with type "str"       "int" is not assignable to "str" (reportAssignmentType) -callables_kwargs.py:103:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol4" +callables_kwargs.py:122:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol4"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int) -> None"     Extra parameter "v3" (reportAssignmentType) -callables_kwargs.py:104:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol5" +callables_kwargs.py:123:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol5"   Type "(**kwargs: **TD2) -> None" is not assignable to type "(v1: int, v3: str) -> None"     Function accepts too many positional parameters; expected 0 but received 2       Extra parameter "v1"       Extra parameter "v3" (reportAssignmentType) -callables_kwargs.py:112:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) -callables_kwargs.py:123:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) -callables_kwargs.py:135:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6" +callables_kwargs.py:131:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) +callables_kwargs.py:142:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) +callables_kwargs.py:154:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6"   Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to type "(**kwargs: **TD2) -> None"     Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) -callables_kwargs.py:185:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +callables_kwargs.py:206:29 - error: Argument of type "object" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "object" is not assignable to "str" (reportArgumentType) +callables_kwargs.py:209:29 - error: Argument of type "object" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "object" is not assignable to "str" (reportArgumentType) +callables_kwargs.py:215:29 - error: Argument of type "object" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "object" is not assignable to "str" (reportArgumentType) +callables_kwargs.py:218:29 - error: Argument of type "object" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "object" is not assignable to "str" (reportArgumentType) +callables_kwargs.py:232:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs" +  "int" is not assignable to "str" (reportArgumentType) +callables_kwargs.py:235:29 - error: Argument of type "int" cannot be assigned to parameter "kwargs" of type "str" in function "takes_name_str_kwargs"   "int" is not assignable to "str" (reportArgumentType) """ conformance_automated = "Fail" errors_diff = """ -Line 184: Expected 1 errors +Line 231: Expected 1 errors +Line 234: Expected 1 errors """ diff --git a/conformance/results/results.html b/conformance/results/results.html index be5d0b8fa..d2a93f89c 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1510,7 +1510,6 @@

      Python Type System Conformance Test Results

      • Allows callable without kwargs to be assigned to callable with unpacked kwargs.
      • Does not support the closed and extra_items TypedDict class arguments.
      • -
      • Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no **kwargs.
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      @@ -1518,7 +1517,6 @@

      Python Type System Conformance Test Results

      Partial
        -
      • Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no **kwargs.
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      @@ -1526,7 +1524,6 @@

      Python Type System Conformance Test Results

      Partial
        -
      • Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no **kwargs.
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      @@ -1534,7 +1531,6 @@

      Python Type System Conformance Test Results

      Partial
        -
      • Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no **kwargs.
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      @@ -1542,7 +1538,6 @@

      Python Type System Conformance Test Results

      Partial
        -
      • Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no **kwargs.
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml index 44aa885c3..1b4e29f0c 100644 --- a/conformance/results/ty/callables_kwargs.toml +++ b/conformance/results/ty/callables_kwargs.toml @@ -3,21 +3,27 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ -callables_kwargs.py:47:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` -callables_kwargs.py:53:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` -callables_kwargs.py:53:11: error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3 -callables_kwargs.py:59:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `str` -callables_kwargs.py:62:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `int | str` -callables_kwargs.py:64:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1` -callables_kwargs.py:65:11: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected `str`, found `Literal[1]` -callables_kwargs.py:65:14: error[parameter-already-assigned] Multiple values provided for parameter `v3` of function `func2` -callables_kwargs.py:66:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2` -callables_kwargs.py:102:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol3` -callables_kwargs.py:103:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol4` -callables_kwargs.py:104:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol5` -callables_kwargs.py:112:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation -callables_kwargs.py:123:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` -callables_kwargs.py:135:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` -callables_kwargs.py:184:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` -callables_kwargs.py:185:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` +callables_kwargs.py:55:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:61:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:61:11: error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3 +callables_kwargs.py:67:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `str` +callables_kwargs.py:70:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `int | str` +callables_kwargs.py:72:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1` +callables_kwargs.py:73:11: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected `str`, found `Literal[1]` +callables_kwargs.py:73:14: error[parameter-already-assigned] Multiple values provided for parameter `v3` of function `func2` +callables_kwargs.py:74:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2` +callables_kwargs.py:121:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol3` +callables_kwargs.py:122:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol4` +callables_kwargs.py:123:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol5` +callables_kwargs.py:131:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation +callables_kwargs.py:142:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` +callables_kwargs.py:154:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` +callables_kwargs.py:206:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `str` +callables_kwargs.py:209:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `str` +callables_kwargs.py:215:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `str` +callables_kwargs.py:218:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `str` +callables_kwargs.py:231:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` +callables_kwargs.py:232:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` +callables_kwargs.py:234:16: error[unknown-argument] Unpacked argument may contain keyword arguments that do not match any known parameter of function `takes_name` +callables_kwargs.py:235:27: error[invalid-argument-type] Argument to function `takes_name_str_kwargs` is incorrect: Expected `str`, found `int` """ diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml index 92f8aec34..fa6c2fa25 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -1,32 +1,41 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a non-closed TypedDict to a callable that has no `**kwargs`. Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. """ conformance_automated = "Fail" errors_diff = """ -Line 184: Expected 1 errors +Line 206: Expected 1 errors +Line 209: Expected 1 errors +Line 215: Expected 1 errors +Line 218: Expected 1 errors +Line 231: Expected 1 errors +Line 234: Expected 1 errors +Line 155: Unexpected errors ['callables_kwargs.py:155: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol7") [assignment]'] +Line 156: Unexpected errors ['callables_kwargs.py:156: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol8") [assignment]'] """ output = """ -callables_kwargs.py:47: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:47: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:52: error: Unexpected keyword argument "v4" for "func1" [call-arg] -callables_kwargs.py:53: error: Too many positional arguments for "func1" [call-arg] -callables_kwargs.py:53: error: Missing named argument "v1" for "func1" [call-arg] -callables_kwargs.py:53: error: Missing named argument "v3" for "func1" [call-arg] -callables_kwargs.py:59: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] -callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "int" [arg-type] -callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] -callables_kwargs.py:62: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] -callables_kwargs.py:64: error: "func1" gets multiple values for keyword argument "v1" [call-arg] -callables_kwargs.py:65: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] -callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v3" [call-arg] -callables_kwargs.py:66: error: "func2" gets multiple values for keyword argument "v1" [call-arg] -callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment] -callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment] -callables_kwargs.py:104: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment] -callables_kwargs.py:112: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] -callables_kwargs.py:123: error: Unpack item in ** parameter must be a TypedDict [misc] -callables_kwargs.py:135: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] -callables_kwargs.py:185: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TD5"; expected "str" [arg-type] +callables_kwargs.py:55: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:55: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:60: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:61: error: Too many positional arguments for "func1" [call-arg] +callables_kwargs.py:61: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:61: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:67: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:70: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "int" [arg-type] +callables_kwargs.py:70: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:70: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:72: error: "func1" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:73: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:73: error: "func2" gets multiple values for keyword argument "v3" [call-arg] +callables_kwargs.py:74: error: "func2" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:121: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:122: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:123: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:131: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:142: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:154: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] +callables_kwargs.py:155: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol7") [assignment] +callables_kwargs.py:156: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol8") [assignment] +callables_kwargs.py:232: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TDExtraItems"; expected "str" [arg-type] +callables_kwargs.py:235: error: Argument 1 to "takes_name_str_kwargs" has incompatible type "**TDExtraItems"; expected "str" [arg-type] """ diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py index cc8b9df93..0d1367e0f 100644 --- a/conformance/tests/callables_kwargs.py +++ b/conformance/tests/callables_kwargs.py @@ -7,7 +7,7 @@ # This sample tests the handling of Unpack[TypedDict] when used with # a **kwargs parameter in a function signature. -from typing import Protocol, TypeVar, NotRequired, Required, Unpack, assert_type +from typing import Protocol, TypeVar, Never, NotRequired, Required, Unpack, assert_type from typing_extensions import TypedDict # for the `closed` and `extra_items` arguments @@ -20,6 +20,14 @@ class TD2(TD1): v3: Required[str] +class TD3(TD1, closed=True): + v3: Required[str] + + +class TD4(TD1, extra_items=Never): + v3: Required[str] + + def func1(**kwargs: Unpack[TD2]) -> None: v1 = kwargs["v1"] assert_type(v1, int) @@ -95,6 +103,17 @@ class TDProtocol6(Protocol): def __call__(self, **kwargs: Unpack[TD2]) -> None: ... + +class TDProtocol7(Protocol): + def __call__(self, **kwargs: Unpack[TD3]) -> None: + ... + + +class TDProtocol8(Protocol): + def __call__(self, **kwargs: Unpack[TD4]) -> None: + ... + + # Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#assignment v1: TDProtocol1 = func1 # OK @@ -124,7 +143,7 @@ def func6(**kwargs: Unpack[T]) -> None: # E: unpacked value must be a TypedDict ... # > The situation where the destination callable contains **kwargs: Unpack[TypedDict] and -# > the source callable doesn’t contain **kwargs should be disallowed. This is because, +# > the source callable doesn’t contain **kwargs should be disallowed, if the TypedDict is :term:`closed`. This is because # > we cannot be sure that additional keyword arguments are not being passed in when an instance of a subclass # > had been assigned to a variable with a base class type and then unpacked in the destination callable invocation @@ -133,20 +152,30 @@ def func7(*, v1: int, v3: str, v2: str = "") -> None: v7: TDProtocol6 = func7 # E: source does not have kwargs +v8: TDProtocol7 = func7 # OK, unpacked TypedDict has closed=True +v9: TDProtocol8 = func7 # OK, unpacked TypedDict has extra_items=Never # Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#passing-kwargs-inside-a-function-to-another-function -class TD3(TypedDict): +class TDOpenImplicit(TypedDict): + name: str + + +class TDOpenExplicit(TypedDict, closed=False): name: str -class TD4(TypedDict, closed=True): +class TDClosed(TypedDict, closed=True): name: str -class TD5(TypedDict, extra_items=int): +class TDExtraItems(TypedDict, extra_items=int): + name: str + + +class TDExtraItemsNever(TypedDict, extra_items=Never): name: str @@ -158,29 +187,59 @@ def takes_name_kwargs(name: str, **kwargs) -> None: ... -# > It is only safe to pass ``kwargs`` hinted with an unpacked, non-closed ``TypedDict`` -# > to another function if that function has ``**kwargs`` in its signature as well. Type checkers -# > should error if the ``TypedDict`` has ``extra_items``, and may error if the ``TypedDict`` is open. +def takes_name_str_kwargs(name: str, **kwargs: str) -> None: + ... -def func8(**kwargs: Unpack[TD3]) -> None: - takes_name(**kwargs) # E?: a subtype may contain unknown keys - takes_name_kwargs(**kwargs) +def takes_name_int_kwargs(name: str, **kwargs: int) -> None: + ... -def func9(**kwargs: Unpack[TD4]) -> None: - takes_name(**kwargs) + +# > Therefore, it is only safe to unpack a non-:term:`closed` TypedDict in a function call +# > if that function has ``**kwargs`` in its signature, and any :term:`extra items` are assignable to the type of ``**kwargs``. +# > If the function being called has ``**kwargs``, checkers should error if the TypedDict's extra items are not assignable to the type of ``**kwargs``. +# > If the function being called does not have ``**kwargs``, checkers may error if the TypedDict is :term:`open`. + +def open_implicit(value: TDOpenImplicit, **kwargs: Unpack[TDOpenImplicit]) -> None: + takes_name(**value) # E?: a subtype may contain unknown keys + takes_name_kwargs(**value) + takes_name_str_kwargs(**value) # E: extra items type is not compatible + takes_name(**kwargs) # E?: a subtype may contain unknown keys takes_name_kwargs(**kwargs) + takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible -def takes_name_str_kwargs(name: str, **kwargs: str) -> None: - ... +def open_explicit(value:TDOpenExplicit, **kwargs: Unpack[TDOpenExplicit]) -> None: + takes_name(**value) # E?: a subtype may contain unknown keys + takes_name_kwargs(**value) + takes_name_str_kwargs(**value) # E: extra items type is not compatible + takes_name(**kwargs) # E?: a subtype may contain unknown keys + takes_name_kwargs(**kwargs) + takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible -def takes_name_int_kwargs(name: str, **kwargs: int) -> None: - ... +def kwargs_closed(value: TDClosed, **kwargs: Unpack[TDClosed]) -> None: + takes_name(**value) + takes_name_kwargs(**value) + takes_name_str_kwargs(**value) + takes_name(**kwargs) + takes_name_kwargs(**kwargs) + takes_name_str_kwargs(**kwargs) -def func10(**kwargs: Unpack[TD5]) -> None: +def kwargs_extra_items(value: TDExtraItems, **kwargs: Unpack[TDExtraItems]) -> None: + takes_name(**value) # E: extra items may be present + takes_name_str_kwargs(**value) # E: extra items type is not compatible + takes_name_int_kwargs(**value) takes_name(**kwargs) # E: extra items may be present takes_name_str_kwargs(**kwargs) # E: extra items type is not compatible takes_name_int_kwargs(**kwargs) + + +def kwargs_extra_items_never(value: TDExtraItemsNever, **kwargs: Unpack[TDExtraItemsNever]) -> None: + takes_name(**value) + takes_name_kwargs(**value) + takes_name_str_kwargs(**value) + takes_name(**kwargs) + takes_name_kwargs(**kwargs) + takes_name_str_kwargs(**kwargs) diff --git a/docs/spec/callables.rst b/docs/spec/callables.rst index b29e70292..edb39a861 100644 --- a/docs/spec/callables.rst +++ b/docs/spec/callables.rst @@ -304,9 +304,9 @@ only:: The reverse situation where the destination callable contains ``**kwargs: Unpack[TypedDict]`` and the source callable doesn't contain -``**kwargs`` should be disallowed. This is because, we cannot be sure that -additional keyword arguments are not being passed in when an instance of a -subclass had been assigned to a variable with a base class type and then +``**kwargs`` should be disallowed, if the TypedDict is :term:`closed`. This is because +we cannot be sure that additional keyword arguments are not being passed in when an +instance of a subclass had been assigned to a variable with a base class type and then unpacked in the destination callable invocation:: def dest(**kwargs: Unpack[Animal]): ... @@ -366,8 +366,8 @@ traditionally typed ``**kwargs`` aren't checked for keyword names. To summarize, function parameters should behave contravariantly and function return types should behave covariantly. -Passing kwargs inside a function to another function -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Unpacking a TypedDict as keyword arguments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :ref:`A previous point ` mentions the problem of possibly passing additional keyword arguments by @@ -413,9 +413,10 @@ it needs completely ignoring any additional values. The calls to ``bar`` and ``spam`` will fail because an unexpected keyword argument will be passed to the ``takes_name`` function. -Therefore, it is only safe to pass ``kwargs`` hinted with an unpacked, non-:term:`closed` ``TypedDict`` -to another function if that function has ``**kwargs`` in its signature as well. Type checkers should -error if the ``TypedDict`` has :term:`extra items`, and may error if the ``TypedDict`` is :term:`open`. +Therefore, it is only safe to unpack a non-:term:`closed` TypedDict in a function call +if that function has ``**kwargs`` in its signature, and any :term:`extra items` are assignable to the type of ``**kwargs``. +If the function being called has ``**kwargs``, checkers should error if the TypedDict's extra items are not assignable to the type of ``**kwargs``. +If the function being called does not have ``**kwargs``, checkers may error if the TypedDict is :term:`open`. In cases similar to the ``bar`` function above the problem could be worked around by marking ``Animal`` with ``closed=True``, or by explicitly dereferencing desired From b14001866d1e21c441c0e9dad5e7a5e8bd203648 Mon Sep 17 00:00:00 2001 From: yangdanny97 Date: Thu, 13 Aug 2026 15:43:19 -0400 Subject: [PATCH 5/5] modify notes --- .../results/mypy/callables_kwargs.toml | 4 ++-- .../results/pycroscope/callables_kwargs.toml | 5 +++-- .../results/pyrefly/callables_kwargs.toml | 5 +++-- .../results/pyright/callables_kwargs.toml | 2 +- conformance/results/results.html | 20 +++++++++++-------- .../results/zuban/callables_kwargs.toml | 4 +++- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 764525eb9..ea50dfab9 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -2,8 +2,8 @@ conformant = "Partial" notes = """ Allows callable without kwargs to be assigned to callable with unpacked kwargs. Does not support the `closed` and `extra_items` TypedDict class arguments. -Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. -Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. +Does not check the type of an unpacked TypedDict's extra items against the target callable's `**kwargs` annotation. +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. """ output = """ callables_kwargs.py:23: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml index 9e69aac7b..2f40f5ee5 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -1,7 +1,8 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. -Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. +Does not check the type of an unpacked TypedDict's extra items against the target callable's `**kwargs` annotation. +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. +Incorrectly rejects assigning a callable without `**kwargs` to a callable whose `**kwargs` is typed with a closed TypedDict or one that sets `extra_items=Never`. """ conformance_automated = "Fail" errors_diff = """ diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml index b15c28537..d90812c47 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -1,7 +1,8 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. -Does not check the `extra_items` type of unpacked kwargs against the target callable's `**kwargs` annotation. +Does not check the type of an unpacked TypedDict's extra items against the target callable's `**kwargs` annotation. +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. +Incorrectly rejects assigning a callable without `**kwargs` to a callable whose `**kwargs` is typed with a TypedDict that sets `extra_items=Never`. """ conformance_automated = "Fail" errors_diff = """ diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml index c70be3c93..418dcd9f7 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,6 +1,6 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. """ output = """ callables_kwargs.py:37:5 - error: Could not access item in TypedDict diff --git a/conformance/results/results.html b/conformance/results/results.html index d2a93f89c..5919a6f5b 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1510,35 +1510,39 @@

      Python Type System Conformance Test Results

      • Allows callable without kwargs to be assigned to callable with unpacked kwargs.
      • Does not support the closed and extra_items TypedDict class arguments.
      • -
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • -
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      • +
      • Does not check the type of an unpacked TypedDict's extra items against the target callable's **kwargs annotation.
      • +
      • Does not reject unpacking a TypedDict that sets extra_items in a call to a callable that has no **kwargs.
      Partial
        -
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • -
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      • +
      • Does not check the type of an unpacked TypedDict's extra items against the target callable's **kwargs annotation.
      • +
      • Does not reject unpacking a TypedDict that sets extra_items in a call to a callable that has no **kwargs.
      • +
      • Incorrectly rejects assigning a callable without **kwargs to a callable whose **kwargs is typed with a closed TypedDict or one that sets extra_items=Never.
      Partial
        -
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • -
      • Does not check the extra_items type of unpacked kwargs against the target callable's **kwargs annotation.
      • +
      • Does not check the type of an unpacked TypedDict's extra items against the target callable's **kwargs annotation.
      • +
      • Does not reject unpacking a TypedDict that sets extra_items in a call to a callable that has no **kwargs.
      • +
      • Incorrectly rejects assigning a callable without **kwargs to a callable whose **kwargs is typed with a TypedDict that sets extra_items=Never.
      Partial
        -
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • +
      • Does not reject unpacking a TypedDict that sets extra_items in a call to a callable that has no **kwargs.
      Pass Partial
        -
      • Does not reject passing unpacked kwargs typed with a TypedDict that has extra_items to a callable that has no **kwargs.
      • +
      • Does not check the type of an open TypedDict's extra items against the target callable's **kwargs annotation.
      • +
      • Does not reject unpacking a TypedDict that sets extra_items in a call to a callable that has no **kwargs.
      • +
      • Incorrectly rejects assigning a callable without **kwargs to a callable whose **kwargs is typed with a closed TypedDict or one that sets extra_items=Never.
      diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml index fa6c2fa25..7a6e4c8c4 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -1,6 +1,8 @@ conformant = "Partial" notes = """ -Does not reject passing unpacked kwargs typed with a TypedDict that has `extra_items` to a callable that has no `**kwargs`. +Does not check the type of an open TypedDict's extra items against the target callable's `**kwargs` annotation. +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. +Incorrectly rejects assigning a callable without `**kwargs` to a callable whose `**kwargs` is typed with a closed TypedDict or one that sets `extra_items=Never`. """ conformance_automated = "Fail" errors_diff = """