diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml index 0586ed15..ea50dfab 100644 --- a/conformance/results/mypy/callables_kwargs.toml +++ b/conformance/results/mypy/callables_kwargs.toml @@ -1,29 +1,52 @@ 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 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: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: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 134: Expected 1 errors +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 492f041f..2f40f5ee 100644 --- a/conformance/results/pycroscope/callables_kwargs.toml +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -1,19 +1,37 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +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 = """ +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: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: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 d37554ce..d90812c4 100644 --- a/conformance/results/pyrefly/callables_kwargs.toml +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -1,23 +1,38 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +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 = """ +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: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: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 191d97a5..418dcd9f 100644 --- a/conformance/results/pyright/callables_kwargs.toml +++ b/conformance/results/pyright/callables_kwargs.toml @@ -1,36 +1,53 @@ -conformant = "Pass" +conformant = "Partial" +notes = """ +Does not reject unpacking a TypedDict that sets `extra_items` in a call to a callable that has no `**kwargs`. +""" output = """ -callables_kwargs.py:28: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: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: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:63: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:64: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:65: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:101: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:102: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:103: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: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: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: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 = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 231: Expected 1 errors +Line 234: Expected 1 errors """ diff --git a/conformance/results/results.html b/conformance/results/results.html index 7c89516d..bd77af97 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -1580,13 +1580,42 @@

Python Type System Conformance Test Results

Partial + + + Partial + + + + Partial + + + + Partial + Pass - Pass - Pass - Pass - Pass + + Partial + + callables_protocol @@ -1619,11 +1648,11 @@

Python Type System Conformance Test Results

3 / 4 • 75.0% - 2.5 / 4 • 62.5% - 4 / 4 • 100.0% - 4 / 4 • 100.0% - 4 / 4 • 100.0% + 2 / 4 • 50.0% + 3.5 / 4 • 87.5% + 3.5 / 4 • 87.5% 4 / 4 • 100.0% + 3.5 / 4 • 87.5% @@ -2733,11 +2762,11 @@

Python Type System Conformance Test Results

108.5 / 145 • 74.8% - 127.5 / 145 • 87.9% - 140.5 / 145 • 96.9% - 135.5 / 145 • 93.4% - 128 / 145 • 88.3% + 127 / 145 • 87.6% 140 / 145 • 96.6% + 135 / 145 • 93.1% + 128 / 145 • 88.3% + 139.5 / 145 • 96.2% diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml index 3318d6bb..1b4e29f0 100644 --- a/conformance/results/ty/callables_kwargs.toml +++ b/conformance/results/ty/callables_kwargs.toml @@ -1,20 +1,29 @@ +conformant = "Pass" conformance_automated = "Pass" errors_diff = """ """ 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: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 678f5d31..7a6e4c8c 100644 --- a/conformance/results/zuban/callables_kwargs.toml +++ b/conformance/results/zuban/callables_kwargs.toml @@ -1,25 +1,43 @@ -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +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 = """ +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: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: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 a4c431db..0d1367e0 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, Never, NotRequired, Required, Unpack, assert_type +from typing_extensions import TypedDict # for the `closed` and `extra_items` arguments class TD1(TypedDict): @@ -19,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) @@ -94,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 @@ -123,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 @@ -132,3 +152,94 @@ 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 TDOpenImplicit(TypedDict): + name: str + + +class TDOpenExplicit(TypedDict, closed=False): + name: str + + +class TDClosed(TypedDict, closed=True): + name: str + + +class TDExtraItems(TypedDict, extra_items=int): + name: str + + +class TDExtraItemsNever(TypedDict, extra_items=Never): + name: str + + +def takes_name(name: str) -> None: + ... + + +def takes_name_kwargs(name: str, **kwargs) -> None: + ... + + +def takes_name_str_kwargs(name: str, **kwargs: str) -> None: + ... + + +def takes_name_int_kwargs(name: str, **kwargs: int) -> None: + ... + + +# > 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 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 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 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 f2322040..edb39a86 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,15 +413,14 @@ 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 -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 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 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"]