Skip to content

Commit 5f97c00

Browse files
erictrautpre-commit-ci[bot]EneggcarljmJelleZijlstra
authored
Update to the Overloads chapter (#1839)
* First draft of an update to the Overloads chapter. * Attempts to clearly define the algorithm for overload matching. * Describes checks for overload consistency, overlapping overloads, and implementation consistency. * [pre-commit.ci] auto fixes from pre-commit.com hooks * Updated draft based on initial round of feedback. * [pre-commit.ci] auto fixes from pre-commit.com hooks * Fixed reference. * Fixed reference. * Another reference fix. * Incorporated PR feedback. * Made changes to proposed overload chapter based on reviewer feedback. * Incorporated additional feedback from reviewers. * Incorporated more feedback. * Fixed typo in code sample. * Update docs/spec/overload.rst Co-authored-by: Eneg <[email protected]> * (very) initial steps on conformance tests * fix abstractmethod without implementation check * split overloads_invalid.py from overloads_basic.py * add test for final with overload * add tests for correct usage of override with an overload * add test for wrong use of override with overload * rename overloads_invalid to overloads_definitions * add support for stub test files, add overloads_definitions_stub.pyi * add initial overloads_consistency tests * add tests for mixed async-def * add tests for signature-transforming decorators * add test for partially overlapping overloads * add test for fully overlapping overloads * add tests for step-1 of overload evaluation * add tests for steps 2 and 3 of overload evaluation * add tests for bool expansion (no checker does this) * add tests for enum and tuple expansion * add test for type[A | B] expansion * add test for step 4 in overload matching * add test for steps 5/6 in overload matching * no expectation of return type if there are call errors * improve variadic test to not use overlapping overloads * Apply suggestions from code review Co-authored-by: Alex Waygood <[email protected]> * Update conformance/tests/overloads_consistency.py * Removed section on overlapping overloads. We're struggling to get agreement on the proper behavior, and this section is lower priority. We can revisit in the future if there's a desire to do so. * Updated Step 5 of overload algorithm based on PR feedback. * Fixed bug in conformance test. Updated conformance results for latest versions of type checkers. Removed outdated references to "overlapping overloads" in spec. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Eneg <[email protected]> Co-authored-by: Carl Meyer <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]> Co-authored-by: Alex Waygood <[email protected]>
1 parent f558518 commit 5f97c00

33 files changed

+1994
-80
lines changed

conformance/results/mypy/overloads_basic.toml

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
conformant = "Pass"
22
output = """
3-
overloads_basic.py:37: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload]
4-
overloads_basic.py:37: note: Possible overload variants:
5-
overloads_basic.py:37: note: def __getitem__(self, int, /) -> int
6-
overloads_basic.py:37: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes
7-
overloads_basic.py:62: error: Single overload definition, multiple required [misc]
8-
overloads_basic.py:74: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]
3+
overloads_basic.py:39: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload]
4+
overloads_basic.py:39: note: Possible overload variants:
5+
overloads_basic.py:39: note: def __getitem__(self, int, /) -> int
6+
overloads_basic.py:39: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes
97
"""
108
conformance_automated = "Pass"
119
errors_diff = """
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
conformant = "Pass"
2+
conformance_automated = "Pass"
3+
errors_diff = """
4+
"""
5+
output = """
6+
overloads_consistency.py:28: error: Overloaded function implementation cannot produce return type of signature 2 [misc]
7+
overloads_consistency.py:44: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc]
8+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
conformant = "Partial"
2+
conformance_automated = "Fail"
3+
notes = """
4+
Does not allow an overload with no implementation in an abstract base class.
5+
Allows @override to be on all overloads and implementation, instead of just implementation.
6+
"""
7+
errors_diff = """
8+
Line 245: Expected 1 errors
9+
Line 49: Unexpected errors ['overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]']
10+
"""
11+
output = """
12+
overloads_definitions.py:14: error: Single overload definition, multiple required [misc]
13+
overloads_definitions.py:26: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]
14+
overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]
15+
overloads_definitions.py:63: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]
16+
overloads_definitions.py:78: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc]
17+
overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc]
18+
overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc]
19+
overloads_definitions.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc]
20+
overloads_definitions.py:133: error: @final should be applied only to overload implementation [misc]
21+
overloads_definitions.py:148: error: @final should be applied only to overload implementation [misc]
22+
overloads_definitions.py:196: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc]
23+
overloads_definitions.py:211: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc]
24+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
conformant = "Partial"
2+
notes = """
3+
Allows @override to appear in a stub file not on the first overload.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 168: Expected 1 errors
8+
"""
9+
output = """
10+
overloads_definitions_stub.pyi:13: error: Single overload definition, multiple required [misc]
11+
overloads_definitions_stub.pyi:37: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc]
12+
overloads_definitions_stub.pyi:38: error: Self argument missing for a non-static method (or an invalid type for self) [misc]
13+
overloads_definitions_stub.pyi:46: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc]
14+
overloads_definitions_stub.pyi:85: error: In a stub file @final must be applied only to the first overload [misc]
15+
overloads_definitions_stub.pyi:101: error: In a stub file @final must be applied only to the first overload [misc]
16+
overloads_definitions_stub.pyi:128: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc]
17+
overloads_definitions_stub.pyi:140: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc]
18+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
conformant = "Partial"
2+
notes = """
3+
Does not expand boolean arguments to Literal[True] and Literal[False].
4+
Does not expand enum arguments to literal variants.
5+
Does not expand tuple arguments to possible combinations.
6+
"""
7+
conformance_automated = "Fail"
8+
errors_diff = """
9+
Line 106: Unexpected errors ['overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]']
10+
Line 107: Unexpected errors ['overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]']
11+
Line 129: Unexpected errors ['overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]']
12+
Line 130: Unexpected errors ['overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]']
13+
Line 169: Unexpected errors ['overloads_evaluation.py:169: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]']
14+
Line 170: Unexpected errors ['overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type]']
15+
"""
16+
output = """
17+
overloads_evaluation.py:32: error: All overload variants of "example1" require at least one argument [call-overload]
18+
overloads_evaluation.py:32: note: Possible overload variants:
19+
overloads_evaluation.py:32: note: def example1(x: int, y: str) -> int
20+
overloads_evaluation.py:32: note: def example1(x: str) -> str
21+
overloads_evaluation.py:40: error: No overload variant of "example1" matches argument types "int", "int" [call-overload]
22+
overloads_evaluation.py:40: note: Possible overload variants:
23+
overloads_evaluation.py:40: note: def example1(x: int, y: str) -> int
24+
overloads_evaluation.py:40: note: def example1(x: str) -> str
25+
overloads_evaluation.py:45: error: No overload variant of "example1" matches argument type "int" [call-overload]
26+
overloads_evaluation.py:45: note: Possible overload variants:
27+
overloads_evaluation.py:45: note: def example1(x: int, y: str) -> int
28+
overloads_evaluation.py:45: note: def example1(x: str) -> str
29+
overloads_evaluation.py:89: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type]
30+
overloads_evaluation.py:89: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type]
31+
overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]
32+
overloads_evaluation.py:106: note: Possible overload variants:
33+
overloads_evaluation.py:106: note: def expand_bool(x: Literal[False]) -> Literal[0]
34+
overloads_evaluation.py:106: note: def expand_bool(x: Literal[True]) -> Literal[1]
35+
overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]
36+
overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]
37+
overloads_evaluation.py:129: note: Possible overload variants:
38+
overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0]
39+
overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]
40+
overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]
41+
overloads_evaluation.py:169: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]
42+
overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type]
43+
"""

conformance/results/mypy/version.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "mypy 1.15.0"
2-
test_duration = 1.7
2+
test_duration = 1.6

conformance/results/pyre/overloads_basic.toml

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
conformant = "Pass"
2-
notes = """
3-
"""
42
output = """
5-
overloads_basic.py:37:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`.
6-
overloads_basic.py:63:0 Incompatible overload [43]: At least two overload signatures must be present.
7-
overloads_basic.py:75:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation.
3+
overloads_basic.py:39:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`.
84
"""
95
conformance_automated = "Pass"
106
errors_diff = """
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
conformant = "Partial"
2+
notes = """
3+
Does not apply decorator transforms before checking overload consistency.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 108: Unexpected errors ['overloads_consistency.py:108:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`None`) is incompatible with the return type of the implementation (`bytes`).', 'overloads_consistency.py:108:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `108`.']
8+
Line 112: Unexpected errors ['overloads_consistency.py:112:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`str`) is incompatible with the return type of the implementation (`bytes`).', 'overloads_consistency.py:112:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `112`.', 'overloads_consistency.py:112:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
9+
"""
10+
output = """
11+
overloads_consistency.py:25:0 Incompatible overload [43]: The return type of overloaded function `return_type` (`str`) is incompatible with the return type of the implementation (`int`).
12+
overloads_consistency.py:41:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `41`.
13+
overloads_consistency.py:108:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`None`) is incompatible with the return type of the implementation (`bytes`).
14+
overloads_consistency.py:108:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `108`.
15+
overloads_consistency.py:112:0 Incompatible overload [43]: The return type of overloaded function `decorated` (`str`) is incompatible with the return type of the implementation (`bytes`).
16+
overloads_consistency.py:112:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `112`.
17+
overloads_consistency.py:112:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
18+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
conformant = "Partial"
2+
conformance_automated = "Fail"
3+
notes = """
4+
Does not allow an overload with no implementation in a Protocol or an abstract base class.
5+
Expects @final/@override on all overloads and implementation, instead of implementation only.
6+
"""
7+
errors_diff = """
8+
Line 245: Expected 1 errors
9+
Lines 148, 150: Expected error (tag 'invalid_final_2')
10+
Line 40: Unexpected errors ['overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.']
11+
Line 51: Unexpected errors ['overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.']
12+
Line 128: Unexpected errors ['overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
13+
Line 239: Unexpected errors ['overloads_definitions.py:239:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
14+
"""
15+
output = """
16+
overloads_definitions.py:15:0 Incompatible overload [43]: At least two overload signatures must be present.
17+
overloads_definitions.py:27:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation.
18+
overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.
19+
overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.
20+
overloads_definitions.py:64:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation.
21+
overloads_definitions.py:80:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `80`.
22+
overloads_definitions.py:85:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `85`.
23+
overloads_definitions.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
24+
overloads_definitions.py:97:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `97`.
25+
overloads_definitions.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
26+
overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
27+
overloads_definitions.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
28+
overloads_definitions.py:204:4 Invalid override [40]: `overloads_definitions.Child.final_method` cannot override final method defined in `Base`.
29+
overloads_definitions.py:220:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
30+
overloads_definitions.py:220:4 Invalid override [40]: `overloads_definitions.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`.
31+
overloads_definitions.py:239:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
32+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
conformant = "Partial"
2+
notes = """
3+
Expects @final and @override to be present on all overloads, not just first.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 168: Expected 1 errors
8+
Lines 80, 82, 85, 87: Expected error (tag 'invalid_final')
9+
Lines 96, 98, 101: Expected error (tag 'invalid_final_2')
10+
Lines 122, 128, 129, 133: Expected error (tag 'override-final')
11+
Line 75: Unexpected errors ['overloads_definitions_stub.pyi:75:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
12+
Line 91: Unexpected errors ['overloads_definitions_stub.pyi:91:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
13+
Line 146: Unexpected errors ['overloads_definitions_stub.pyi:146:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
14+
Line 162: Unexpected errors ['overloads_definitions_stub.pyi:162:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
15+
Line 174: Unexpected errors ['overloads_definitions_stub.pyi:174:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).']
16+
"""
17+
output = """
18+
overloads_definitions_stub.pyi:14:0 Incompatible overload [43]: At least two overload signatures must be present.
19+
overloads_definitions_stub.pyi:43:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
20+
overloads_definitions_stub.pyi:52:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
21+
overloads_definitions_stub.pyi:75:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
22+
overloads_definitions_stub.pyi:91:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
23+
overloads_definitions_stub.pyi:142:4 Invalid override [40]: `overloads_definitions_stub.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`.
24+
overloads_definitions_stub.pyi:146:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
25+
overloads_definitions_stub.pyi:162:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
26+
overloads_definitions_stub.pyi:174:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).
27+
"""

0 commit comments

Comments
 (0)