From bab532e6862db25e1d568171d350265d0a5efb31 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Tue, 13 Aug 2024 17:06:03 -0600 Subject: [PATCH 01/38] 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. --- docs/spec/overload.rst | 406 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 392 insertions(+), 14 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 3b534046..5e346f2e 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -1,11 +1,21 @@ .. _`overload`: -``@overload`` +``Overloads`` ============= +In Python, it is common for callable objects to be polymorphic, meaning +they accept different types of arguments. It is also common for such +callables to return different types depending on the arguments passed to +them. Overloads provide a way to describe the accepted input signatures +and corresponding return types. + + +Overload definitions +^^^^^^^^^^^^^^^^^^^^ + The ``@overload`` decorator allows describing functions and methods -that support multiple different combinations of argument types. This -pattern is used frequently in builtin modules and types. For example, +that support multiple different combinations of argument types. This +pattern is used frequently in builtin modules and types. For example, the ``__getitem__()`` method of the ``bytes`` type can be described as follows:: @@ -18,9 +28,9 @@ follows:: @overload def __getitem__(self, s: slice) -> bytes: ... -This description is more precise than would be possible using unions -(which cannot express the relationship between the argument and return -types):: +This description is more precise than would be possible using unions, +which cannot express the relationship between the argument and return +types:: class bytes: ... @@ -54,15 +64,15 @@ Note that we could also easily add items to support ``map(None, ...)``:: iter2: Iterable[T2]) -> Iterable[tuple[T1, T2]]: ... Uses of the ``@overload`` decorator as shown above are suitable for -stub files. In regular modules, a series of ``@overload``-decorated +stub files. In regular modules, a series of ``@overload``-decorated definitions must be followed by exactly one non-``@overload``-decorated definition (for the same function/method). The ``@overload``-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-``@overload``-decorated definition, while the latter is used at -runtime but should be ignored by a type checker. At runtime, calling -a ``@overload``-decorated function directly will raise -``NotImplementedError``. Here's an example of a non-stub overload +runtime but should be ignored by a type checker. At runtime, calling +an ``@overload``-decorated function directly will raise +``NotImplementedError``. Here's an example of a non-stub overload that can't easily be expressed using a union or a type variable:: @overload @@ -77,9 +87,9 @@ that can't easily be expressed using a union or a type variable:: def utf8(value): -A constrained ``TypeVar`` type can often be used instead of using the -``@overload`` decorator. For example, the definitions of ``concat1`` -and ``concat2`` in this stub file are equivalent:: +A constrained ``TypeVar`` type can sometimes be used instead of +using the ``@overload`` decorator. For example, the definitions +of ``concat1`` and ``concat2`` in this stub file are equivalent:: from typing import TypeVar @@ -99,8 +109,376 @@ variable is not sufficient. Another important difference between type variables such as ``AnyStr`` and using ``@overload`` is that the prior can also be used to define -constraints for generic class type parameters. For example, the type +constraints for generic class type parameters. For example, the type parameter of the generic class ``typing.IO`` is constrained (only ``IO[str]``, ``IO[bytes]`` and ``IO[Any]`` are valid):: class IO(Generic[AnyStr]): ... + + +Invalid overload definitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type checkers should enforce the following rules for overload definitions. + +At least two ``@overload``-decorated definitions must be present. If only +one is present, an error should be reported. + +An overload implementation should be present for all overload function +definitions. Type checkers should report an error or warning if an +implementation is missing. Overload definitions within stub files, +protocols, and abstract base classes are exempt from this check. + +If an overload is decorated with ``@staticmethod`` or ``@classmethod``, +all overloads must be similarly decorated. The implementation, +if present, must be decorated in the same manner. Similarly, if one overload +is ``async``, all overloads must be ``async`` as well as the implementation. +Type checkers should report an error if these conditions are not met. + +If one or more overloads are decorated with ``@final`` but the +implementation is not, an error should be reported. + + +Implementation consistency +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If an overload implementation is defined, type checkers should validate +that its signature is consistent with all of its associated overload +signatures. The implementation should accept all potential argument lists +that are accepted by the overloads and should produce all potential return +types produced by the overloads. In typing terms, this means the input +signature of the implementation should be assignable to the input signatures +of all overloads, and the return type of all overloads should be assignable to +the return type of the implementation. + +If the implementation is inconsistent with its overloads, a type checker +should report an error:: + + @overload + def func(x: str, /) -> str: ... + @overload + def func(x: int) -> int: ... + + # This implementation is inconsistent with the second overload + # because it does not accept a keyword argument ``x`` and the + # the overload's return type ``int`` is not assignable to the + implementation's return type ``str``. + def func(x: int | str, /) -> str: + return str(x) + +Overlapping overloads +^^^^^^^^^^^^^^^^^^^^^ + +If two overloads can accept the same set of arguments but have +different return types, the overloads are said to "partially overlap". +This condition is indicative of a programming error and should +be reported by type checkers:: + + # These overloads partially overlap because both accept an + # argument of type ``Literal[0]``, but their return types + # differ. + + @overload + def func(x: Literal[0]) -> int: ... + @overload + def func(x: int) -> str: ... + +If all arguments accepted by an overload are also always accepted by +an earlier overload, the two overloads are said to "fully overlap". +In this case, the latter overload will never be used. This condition +is indicative of a programming error and should be reported by type +checkers:: + + # These overloads fully overlap because the second overload + # accepts all arguments accepted by the first overload. + + @overload + def func[T](x: T) -> T: ... + @overload + def func(x: int) -> int: ... + + +Overload call evaluation +^^^^^^^^^^^^^^^^^^^^^^^^ + +When a type checker evaluates the call of an overloaded function, it +attempts to "match" the supplied arguments with one or more overloads. +This section describes the algorithm that type checkers should use +for overload matching. + +Only the overloads (the ``@overload``-decorated signatures) should be +considered for matching purposes. The implementation, if provided, +should be ignored for purposes of overload matching. + + +Step 1: Examine the argument list to determine the number of +positional and keyword arguments. Use this information to eliminate any +overload candidates that are not plausible based on their +input signatures. + +- If no candidate overloads remain, generate an error and stop. +- If only one candidate overload remains, it is the winning match. Evaluate +it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 2. + + +Step 2: Evaluate each remaining overload as a regular (non-overloaded) +call to determine whether it is compatible with the supplied +argument list. Unlike step 1, this step considers the types of the parameters +and arguments. During this step, do not generate any user-visible errors. +Simply record which of the overloads result in evaluation errors. + +- If all overloads result in errors, proceed to step 3. +- If only one overload evaluates without error, it is the winning match. +Evaluate it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 4. + + +Step 3: If step 2 produces errors for all overloads, perform +"argument type expansion". Some types can be decomposed +into two or more subtypes. For example, the type ``int | str`` can be +expanded into ``int`` and ``str``. + +Expansion should be performed one argument at a time from left to +right. Each expansion results in sets of effective argument types. +For example, if there are two arguments whose types evaluate to +``int | str`` and ``int | bytes``, expanding the first argument type +results in two sets of argument types: ``(int, ?)`` and ``(str, ?)``. +Here ``?`` represents an unexpanded argument type. +If expansion of the second argument is required, four sets of +argument types are produced: ``(int, int)``, ``(int, bytes)``, +``(str, int)``, and ``(str, bytes)``. + +After each argument expansion, return to step 2 and evaluate all +expanded argument lists. + +- If all argument lists evaluate successfully, combine their +respective return types by union to determine the final return type +for the call, and stop. +- If argument expansion has been applied to all arguments and one or +more of the expanded argument lists cannot be evaluated successfully, +generate an error and stop. + +For additional details about argument type expansion, see +:ref:`argument-type-expansion` below. + + +Step 4: If the argument list is compatible with two or more overloads, +determine whether one or more of the overloads has a variadic parameter +(either ``*args`` or ``**kwargs``) that maps to a corresponding argument +that supplies an indeterminate number of positional or keyword arguments. +If so, eliminate overloads that do not have a variadic parameter. + +- If this results in only one remaining candidate overload, it is +the winning match. Evaluate it as if it were a non-overloaded function +call and stop. +- If two or more candidate overloads remain, proceed to step 5. + + +Step 5: If the type of one or more arguments evaluates to a +:ref:`gradual type` (e.g. ``list[Any]`` or ``str | Any``), determine +whether some theoretical :ref:`materialization` of these gradual types +could be used to disambiguate between two or more of the remaining +overloads. + +- If none of the arguments evaluate to a gradual type, proceed to step 6. +- If one or more arguments evaluate to a gradual type but no possible +materializations of these types would disambiguate between the remaining +overloads, proceed to step 6. +- If possible materializations of these types would disambiguate between +two or more of the remaining overloads and this subset of overloads have +consistent return types, proceed to step 6. If the return types include +type variables, constraint solving should be applied here before testing +for consistency. +- If none of the above conditions are met, the presence of gradual types +leads to an ambiguous overload selection. Assume a return type of ``Any`` +and stop. This preserves the "gradual guarantee". + +[Eric's note for reviewers: I'm struggling to come up with an +understandable and unambiguous way to describe this step. +Suggestions are welcome.] + +[Eric's note for reviewers: Pyright currently does not use return type +consistency in the above check. Instead, it looks for non-overlapping +return types. If return types are overlapping (that is, one is a consistent +subtype of another), it uses the wider return type. Only if there is no +consistency relationship between return types does it consider it an +"ambiguous" situation and turns it into an Any. This produces better +results for users of language servers, but it doesn't strictly preserve +the gradual guarantee. I'm willing to abandon this in favor of a +strict consistency check.] + + +Step 6: Choose the first remaining candidate overload as the winning +match. Evaluate it as if it were a non-overloaded function call and stop. + + +Example 1:: + + @overload + def example1(x: int, y: str) -> int: ... + @overload + def example1(x: str) -> str: ... + + example1() # Error in step 1: no plausible overloads + example1(1, "") # Step 1 eliminates second overload + example1("") # Step 1 eliminates first overload + + example1("", "") # Error in step 2: no compatible overloads + example1(1) # Error in step 2: no compatible overloads + + +Example 2:: + + @overload + def example2(x: int, y: str, z: int) -> str: ... + @overload + def example2(x: int, y: int, z: int) -> int: ... + + def test(val: str | int): + # In this example, argument type expansion is + # performed on the first two arguments. Expansion + # of the third is unnecessary. + r1 = example2(1, val, 1) + reveal_type(r1) # Should reveal str | int + + # Here, the types of all three arguments are expanded + # without success. + example2(val, val, val) # Error in step 3 + + +Example 3:: + + @overload + def example3(x: int) -> int: ... + @overload + def example3(x: int, y: int) -> tuple[int, int]: ... + @overload + def example3(*args: int) -> tuple[int, ...]: ... + + def test(): + # Step 1 eliminates second overload. Step 4 and + # step 5 do not apply. Step 6 picks the first + # overload. + r1 = example3(1) + reveal_type(r1) # Should reveal int + + # Step 1 eliminates first overload. Step 4 and + # step 5 do not apply. Step 6 picks the second + # overload. + r2 = example3(1, 2) + reveal_type(r2) # Should reveal tuple[int, int] + + # Step 1 doesn't eliminate any overloads. Step 4 + # picks the third overload. + val = [1, 2, 3, 4] + r3 = example3(*val) + reveal_type(r3) # Should reveal tuple[int, ...] + + +Example 4:: + + @overload + def example4(x: list[int], y: int) -> int: ... + @overload + def example4(x: list[str], y: str) -> int: ... + @overload + def example4(x: int, y: int) -> list[int]: ... + + def test(v1: list[Any], v2: Any): + # Step 4 eliminates third overload. Step 5 + # determines that first and second overloads + # both apply and are ambiguous due to Any, but + # return types are consistent. + r1 = example4(v1, 1) + reveal_type(r1) # Should reveal int + + # Step 4 eliminates third overload. Step 5 + # determines that first and second overloads + # both apply and are ambiguous due to Any, but + # return types are consistent. + r2 = example4([1], v2) + reveal_type(r2) # Should reveal int + + # Step 5 determines that first, second, and third + # overloads all apply and are ambiguous due to Any. + # These have inconsistent return types, so evaluated + # type is Any. + r3 = example4(v2, v2) + reveal_type(r3) # Should reveal Any + + + +Argument type expansion +^^^^^^^^^^^^^^^^^^^^^^^ + +When performing argument type expansion, the following types should be +expanded: + +1. Unions: Each subtype of the union should be considered as a separate +argument type. For example, the type ``int | str`` should be expanded +into ``int`` and ``str``. + +2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + +3. ``Enum`` types (other than those that derive from ``enum.Flag``) should +be expanded into their literal members. + +4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + +5. Tuples of known length that contain expandable types should be expanded +into all possible combinations of their subtypes. For example, the type +``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, +``(int, Literal[False])``, ``(str, Literal[True])``, and +``(str, Literal[False])``. + + +[Eric's note for reviewers: I'm not 100% convinced we should +support argument expansion in all of these cases. Tuple expansion, +in particular, can be very costly and can quickly blow up in complexity. +Currently, pyright and mypy support only the case 1 in the list above, +but I have had requests to support 2 and 3.] + +When performing type expansion for an argument, the argument that +is targeted for expansion should be evaluated without the use of +any context. All arguments that are not yet expanded should +continue to be evaluated with the benefit of context supplied by parameter +types within each overload signature. + +Example:: + + class MyDict[T](TypedDict): + x: T + + @overload + def func[T](a: int, b: MyDict[T]) -> T: ... + + @overload + def func(a: str, b: dict[str, int]) -> str: ... + + + def test(val: int | str): + result = func(val, {'x': 1}) + reveal_type(result) # Should reveal "int | str" + +In this case, type expansion is performed on the first argument, +which expands its type from ``int | str`` to ``int`` and ``str``. +The expression for the second argument is evaluated in the context +of both overloads. For the first overload, the second argument evaluates +to ``MyDict[int]``, and for the second overload it evaluates to +``dict[str, int]``. Both overloads are used to evaluate this call, +and the final type of ``result`` is ``int | str``. + +[Eric's note: mypy apparently doesn't do this currently. It evaluates all +arguments without the benefit of context, which produces less-than-ideal +results in some cases.] + + +[Eric's note for reviewers: We may want to provide for argument type expansion +for regular (non-overloaded) calls as well. This came up recently in +[this thread](https://discuss.python.org/t/proposal-relax-un-correlated-constrained-typevars/59658). +I'm a bit hesitant to add this to the spec because it adds significant +complexity to call evaluations and would likely result in a measurable slowdown +in type evaluation, but it's worth considering. We could perhaps mitigate the +slowdown by applying this behavior only when a constrained type variable is +used in the call's signature.] From de81026ce28282961524aaf3df662c4ce1c70495 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 23:07:10 +0000 Subject: [PATCH 02/38] [pre-commit.ci] auto fixes from pre-commit.com hooks --- docs/spec/overload.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5e346f2e..7c298135 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -158,7 +158,7 @@ should report an error:: def func(x: str, /) -> str: ... @overload def func(x: int) -> int: ... - + # This implementation is inconsistent with the second overload # because it does not accept a keyword argument ``x`` and the # the overload's return type ``int`` is not assignable to the @@ -169,7 +169,7 @@ should report an error:: Overlapping overloads ^^^^^^^^^^^^^^^^^^^^^ -If two overloads can accept the same set of arguments but have +If two overloads can accept the same set of arguments but have different return types, the overloads are said to "partially overlap". This condition is indicative of a programming error and should be reported by type checkers:: @@ -184,7 +184,7 @@ be reported by type checkers:: def func(x: int) -> str: ... If all arguments accepted by an overload are also always accepted by -an earlier overload, the two overloads are said to "fully overlap". +an earlier overload, the two overloads are said to "fully overlap". In this case, the latter overload will never be used. This condition is indicative of a programming error and should be reported by type checkers:: @@ -355,7 +355,7 @@ Example 3:: def example3(x: int, y: int) -> tuple[int, int]: ... @overload def example3(*args: int) -> tuple[int, ...]: ... - + def test(): # Step 1 eliminates second overload. Step 4 and # step 5 do not apply. Step 6 picks the first From 5ca254e5937265f101870f5319d55e0181219666 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 16 Aug 2024 11:55:46 -0700 Subject: [PATCH 03/38] Updated draft based on initial round of feedback. --- docs/spec/overload.rst | 126 ++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5e346f2e..dace0f38 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -124,27 +124,32 @@ Type checkers should enforce the following rules for overload definitions. At least two ``@overload``-decorated definitions must be present. If only one is present, an error should be reported. -An overload implementation should be present for all overload function -definitions. Type checkers should report an error or warning if an -implementation is missing. Overload definitions within stub files, -protocols, and abstract base classes are exempt from this check. +The ``@overload``-decorated definitions must be followed by an overload +implementation, which does not include an ``@overload`` decorator. Type +checkers should report an error or warning if an implementation is missing. +Overload definitions within stub files, protocols, and abstract base classes +are exempt from this check. If an overload is decorated with ``@staticmethod`` or ``@classmethod``, all overloads must be similarly decorated. The implementation, -if present, must be decorated in the same manner. Similarly, if one overload -is ``async``, all overloads must be ``async`` as well as the implementation. -Type checkers should report an error if these conditions are not met. +if present, must be decorated in the same manner. Type checkers should report +an error if these conditions are not met. -If one or more overloads are decorated with ``@final`` but the +If one or more overloads are decorated with ``@final`` or ``@override`` but the implementation is not, an error should be reported. +Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +within the same overload definition. Type checkers should desugar all +``async def`` statements before testing for implementation consistency +and overlapping overloads (described below). + Implementation consistency ^^^^^^^^^^^^^^^^^^^^^^^^^^ If an overload implementation is defined, type checkers should validate -that its signature is consistent with all of its associated overload -signatures. The implementation should accept all potential argument lists +that it is consistent with all of its associated overload signatures. +The implementation should accept all potential sets of arguments that are accepted by the overloads and should produce all potential return types produced by the overloads. In typing terms, this means the input signature of the implementation should be assignable to the input signatures @@ -166,22 +171,36 @@ should report an error:: def func(x: int | str, /) -> str: return str(x) +When a type checker checks the implementation for consistency with overloads, +it should first apply any transforms that change the effective type of the +implementation including the presence of a ``yield`` statement in the +implementation body, the use of ``async def``, and the presence of additional +decorators. + + Overlapping overloads ^^^^^^^^^^^^^^^^^^^^^ -If two overloads can accept the same set of arguments but have -different return types, the overloads are said to "partially overlap". -This condition is indicative of a programming error and should -be reported by type checkers:: +If two overloads can accept the same set of arguments, they are said +to "partially overlap". If two overloads partially overlap, the return type +of the latter overload should be assignable to the return type of the +former overload. If this condition doesn't hold, it is indicative of a +programming error and should be reported by type checkers:: # These overloads partially overlap because both accept an # argument of type ``Literal[0]``, but their return types # differ. @overload - def func(x: Literal[0]) -> int: ... + def func1(x: Literal[0]) -> int: ... @overload - def func(x: int) -> str: ... + def func1(x: int) -> str: ... + +[Eric's note for reviewers: Mypy exempts `__get__` from the above check. +Refer to https://github.com/python/typing/issues/253#issuecomment-389262904 +for Ivan's explanation. I'm not convinced this exemption is necessary. +Currently pyright copies the exemption. Do we want to codify this or leave it +out?] If all arguments accepted by an overload are also always accepted by an earlier overload, the two overloads are said to "fully overlap". @@ -218,7 +237,7 @@ input signatures. - If no candidate overloads remain, generate an error and stop. - If only one candidate overload remains, it is the winning match. Evaluate -it as if it were a non-overloaded function call and stop. + it as if it were a non-overloaded function call and stop. - If two or more candidate overloads remain, proceed to step 2. @@ -230,7 +249,7 @@ Simply record which of the overloads result in evaluation errors. - If all overloads result in errors, proceed to step 3. - If only one overload evaluates without error, it is the winning match. -Evaluate it as if it were a non-overloaded function call and stop. + Evaluate it as if it were a non-overloaded function call and stop. - If two or more candidate overloads remain, proceed to step 4. @@ -253,11 +272,12 @@ After each argument expansion, return to step 2 and evaluate all expanded argument lists. - If all argument lists evaluate successfully, combine their -respective return types by union to determine the final return type -for the call, and stop. + respective return types by union to determine the final return type + for the call, and stop. - If argument expansion has been applied to all arguments and one or -more of the expanded argument lists cannot be evaluated successfully, -generate an error and stop. + more of the expanded argument lists cannot be evaluated successfully, + generate an error and stop. + For additional details about argument type expansion, see :ref:`argument-type-expansion` below. @@ -270,29 +290,30 @@ that supplies an indeterminate number of positional or keyword arguments. If so, eliminate overloads that do not have a variadic parameter. - If this results in only one remaining candidate overload, it is -the winning match. Evaluate it as if it were a non-overloaded function -call and stop. + the winning match. Evaluate it as if it were a non-overloaded function + call and stop. - If two or more candidate overloads remain, proceed to step 5. Step 5: If the type of one or more arguments evaluates to a -:ref:`gradual type` (e.g. ``list[Any]`` or ``str | Any``), determine -whether some theoretical :ref:`materialization` of these gradual types -could be used to disambiguate between two or more of the remaining -overloads. +type that includes a :term:`gradual form` (e.g. ``list[Any]`` or +``str | Any``), determine whether some theoretical +:ref:`materialization` of these gradual types could be used to disambiguate +between two or more of the remaining overloads. - If none of the arguments evaluate to a gradual type, proceed to step 6. - If one or more arguments evaluate to a gradual type but no possible -materializations of these types would disambiguate between the remaining -overloads, proceed to step 6. + materializations of these types would disambiguate between the remaining + overloads, proceed to step 6. - If possible materializations of these types would disambiguate between -two or more of the remaining overloads and this subset of overloads have -consistent return types, proceed to step 6. If the return types include -type variables, constraint solving should be applied here before testing -for consistency. + two or more of the remaining overloads and this subset of overloads have + consistent return types, proceed to step 6. If the return types include + type variables, constraint solving should be applied here before testing + for consistency. - If none of the above conditions are met, the presence of gradual types -leads to an ambiguous overload selection. Assume a return type of ``Any`` -and stop. This preserves the "gradual guarantee". + leads to an ambiguous overload selection. Assume a return type of ``Any`` + and stop. This preserves the "gradual guarantee". + [Eric's note for reviewers: I'm struggling to come up with an understandable and unambiguous way to describe this step. @@ -386,27 +407,19 @@ Example 4:: def example4(x: int, y: int) -> list[int]: ... def test(v1: list[Any], v2: Any): - # Step 4 eliminates third overload. Step 5 - # determines that first and second overloads - # both apply and are ambiguous due to Any, but - # return types are consistent. - r1 = example4(v1, 1) - reveal_type(r1) # Should reveal int - - # Step 4 eliminates third overload. Step 5 + # Step 2 eliminates the third overload. Step 5 # determines that first and second overloads # both apply and are ambiguous due to Any, but # return types are consistent. - r2 = example4([1], v2) - reveal_type(r2) # Should reveal int - - # Step 5 determines that first, second, and third - # overloads all apply and are ambiguous due to Any. - # These have inconsistent return types, so evaluated - # type is Any. - r3 = example4(v2, v2) - reveal_type(r3) # Should reveal Any + r1 = example4(v1, v2) + reveal_type(r1) # Reveals int + # Step 2 eliminates the second overload. Step 5 + # determines that first and third overloads + # both apply and are ambiguous due to Any, and + # the return types are inconsistent. + r2 = example4(v2, 1) + reveal_type(r2) # Should reveal Any Argument type expansion @@ -482,3 +495,10 @@ complexity to call evaluations and would likely result in a measurable slowdown in type evaluation, but it's worth considering. We could perhaps mitigate the slowdown by applying this behavior only when a constrained type variable is used in the call's signature.] + +[Eric's note for reviewers: What about expansion based on multiple inheritance? +For example, if class C inherits from A and B, should we expand C into A and B +for purposes of overload matching? This could get very expensive and difficult +to spec, and it feels like a significant edge case, so I'm inclined to leave it +out. No one has asked for this, to my knowledge.] + From f993b28eaa194c7611ec380ff8a324596ff3279b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:57:17 +0000 Subject: [PATCH 04/38] [pre-commit.ci] auto fixes from pre-commit.com hooks --- docs/spec/overload.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5d2fca77..278e9576 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -196,7 +196,7 @@ programming error and should be reported by type checkers:: @overload def func1(x: int) -> str: ... -[Eric's note for reviewers: Mypy exempts `__get__` from the above check. +[Eric's note for reviewers: Mypy exempts `__get__` from the above check. Refer to https://github.com/python/typing/issues/253#issuecomment-389262904 for Ivan's explanation. I'm not convinced this exemption is necessary. Currently pyright copies the exemption. Do we want to codify this or leave it @@ -501,4 +501,3 @@ For example, if class C inherits from A and B, should we expand C into A and B for purposes of overload matching? This could get very expensive and difficult to spec, and it feels like a significant edge case, so I'm inclined to leave it out. No one has asked for this, to my knowledge.] - From 660295c78acb5624715531ddcf0553e048d80e94 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 16 Aug 2024 12:08:20 -0700 Subject: [PATCH 05/38] Fixed reference. --- docs/spec/overload.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5d2fca77..b6a13735 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -280,7 +280,7 @@ expanded argument lists. For additional details about argument type expansion, see -:ref:`argument-type-expansion` below. +`argument-type-expansion`_ below. Step 4: If the argument list is compatible with two or more overloads, @@ -422,6 +422,8 @@ Example 4:: reveal_type(r2) # Should reveal Any +.. _argument-type-expansion: + Argument type expansion ^^^^^^^^^^^^^^^^^^^^^^^ From 831945b80004920dbd7ec64f0ba6f846df70ac50 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 16 Aug 2024 12:12:44 -0700 Subject: [PATCH 06/38] Fixed reference. --- docs/spec/overload.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index af81c02e..83bfb580 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -298,7 +298,7 @@ If so, eliminate overloads that do not have a variadic parameter. Step 5: If the type of one or more arguments evaluates to a type that includes a :term:`gradual form` (e.g. ``list[Any]`` or ``str | Any``), determine whether some theoretical -:ref:`materialization` of these gradual types could be used to disambiguate +:term:`materialization` of these gradual types could be used to disambiguate between two or more of the remaining overloads. - If none of the arguments evaluate to a gradual type, proceed to step 6. From 3906a1263ef5bd0cc6a8b5855ca8c5ca7d842444 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 16 Aug 2024 12:16:17 -0700 Subject: [PATCH 07/38] Another reference fix. --- docs/spec/overload.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 83bfb580..2eebcdde 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -298,8 +298,8 @@ If so, eliminate overloads that do not have a variadic parameter. Step 5: If the type of one or more arguments evaluates to a type that includes a :term:`gradual form` (e.g. ``list[Any]`` or ``str | Any``), determine whether some theoretical -:term:`materialization` of these gradual types could be used to disambiguate -between two or more of the remaining overloads. +:term:`materialization ` of these gradual types could be used +to disambiguate between two or more of the remaining overloads. - If none of the arguments evaluate to a gradual type, proceed to step 6. - If one or more arguments evaluate to a gradual type but no possible From bb8fe09e033b1e7931ad5b392291984279c9847e Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Thu, 22 Aug 2024 18:24:22 -0700 Subject: [PATCH 08/38] Incorporated PR feedback. --- docs/spec/overload.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 2eebcdde..aecf8470 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -377,7 +377,7 @@ Example 3:: @overload def example3(*args: int) -> tuple[int, ...]: ... - def test(): + def test(val: list[int]): # Step 1 eliminates second overload. Step 4 and # step 5 do not apply. Step 6 picks the first # overload. @@ -392,7 +392,6 @@ Example 3:: # Step 1 doesn't eliminate any overloads. Step 4 # picks the third overload. - val = [1, 2, 3, 4] r3 = example3(*val) reveal_type(r3) # Should reveal tuple[int, ...] From cce3879e735f401fdd4fe0654913d0d23c351076 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Tue, 27 Aug 2024 17:06:38 -0700 Subject: [PATCH 09/38] Made changes to proposed overload chapter based on reviewer feedback. --- docs/spec/overload.rst | 194 ++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 127 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index aecf8470..09d91fef 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -139,8 +139,9 @@ If one or more overloads are decorated with ``@final`` or ``@override`` but the implementation is not, an error should be reported. Overloads are allowed to use a mixture of ``async def`` and ``def`` statements -within the same overload definition. Type checkers should desugar all -``async def`` statements before testing for implementation consistency +within the same overload definition. Type checkers should convert +``async def`` statements to a non-async signature (wrapping the return +type in a ``Coroutine``) before testing for implementation consistency and overlapping overloads (described below). @@ -152,9 +153,9 @@ that it is consistent with all of its associated overload signatures. The implementation should accept all potential sets of arguments that are accepted by the overloads and should produce all potential return types produced by the overloads. In typing terms, this means the input -signature of the implementation should be assignable to the input signatures -of all overloads, and the return type of all overloads should be assignable to -the return type of the implementation. +signature of the implementation should be :term: to the input +signatures of all overloads, and the return type of all overloads should be +assignable to the return type of the implementation. If the implementation is inconsistent with its overloads, a type checker should report an error:: @@ -183,33 +184,32 @@ Overlapping overloads If two overloads can accept the same set of arguments, they are said to "partially overlap". If two overloads partially overlap, the return type -of the latter overload should be assignable to the return type of the -former overload. If this condition doesn't hold, it is indicative of a +of the former overload should be assignable to the return type of the +latter overload. If this condition doesn't hold, it is indicative of a programming error and should be reported by type checkers:: # These overloads partially overlap because both accept an - # argument of type ``Literal[0]``, but their return types - # differ. + # argument of type Literal[0], but the return type int is + # not assignable to str. @overload def func1(x: Literal[0]) -> int: ... @overload def func1(x: int) -> str: ... -[Eric's note for reviewers: Mypy exempts `__get__` from the above check. -Refer to https://github.com/python/typing/issues/253#issuecomment-389262904 -for Ivan's explanation. I'm not convinced this exemption is necessary. -Currently pyright copies the exemption. Do we want to codify this or leave it -out?] +Type checkers may exempt certain magic methods from the above check +for conditions that are mandated by their usage in the runtime. For example, +the ``__get__`` method of a descriptor is often defined using overloads +that would partially overlap if the above rule is enforced. -If all arguments accepted by an overload are also always accepted by -an earlier overload, the two overloads are said to "fully overlap". +If all possible sets of arguments accepted by an overload are also always +accepted by an earlier overload, the two overloads are said to "fully overlap". In this case, the latter overload will never be used. This condition is indicative of a programming error and should be reported by type checkers:: - # These overloads fully overlap because the second overload - # accepts all arguments accepted by the first overload. + # These overloads fully overlap because the first overload + # accepts all arguments accepted by the second overload. @overload def func[T](x: T) -> T: ... @@ -217,6 +217,14 @@ checkers:: def func(x: int) -> int: ... +[Eric's note for reviewers: We've identified a number of subtle issues and +cases where current type checkers do not honor the above rules, especially +for partially-overlapping overloads. At this point, I'm tempted to delete +this section entirely. We could always add it back to the spec later +if and when we find that there's a need for it and we achieve consensus on +the details.] + + Overload call evaluation ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -254,21 +262,20 @@ Simply record which of the overloads result in evaluation errors. Step 3: If step 2 produces errors for all overloads, perform -"argument type expansion". Some types can be decomposed -into two or more subtypes. For example, the type ``int | str`` can be -expanded into ``int`` and ``str``. +"argument type expansion". Union types can be expanded +into their constituent subtypes. For example, the type ``int | str`` can +be expanded into ``int`` and ``str``. -Expansion should be performed one argument at a time from left to +Type expansion should be performed one argument at a time from left to right. Each expansion results in sets of effective argument types. For example, if there are two arguments whose types evaluate to ``int | str`` and ``int | bytes``, expanding the first argument type -results in two sets of argument types: ``(int, ?)`` and ``(str, ?)``. -Here ``?`` represents an unexpanded argument type. -If expansion of the second argument is required, four sets of -argument types are produced: ``(int, int)``, ``(int, bytes)``, +results in two sets of argument types: ``(int, int | bytes)`` and +``(str, int | bytes)``. If type expansion for the second argument is required, +four sets of argument types are produced: ``(int, int)``, ``(int, bytes)``, ``(str, int)``, and ``(str, bytes)``. -After each argument expansion, return to step 2 and evaluate all +After each argument's expansion, return to step 2 and evaluate all expanded argument lists. - If all argument lists evaluate successfully, combine their @@ -295,45 +302,31 @@ If so, eliminate overloads that do not have a variadic parameter. - If two or more candidate overloads remain, proceed to step 5. -Step 5: If the type of one or more arguments evaluates to a -type that includes a :term:`gradual form` (e.g. ``list[Any]`` or -``str | Any``), determine whether some theoretical -:term:`materialization ` of these gradual types could be used -to disambiguate between two or more of the remaining overloads. - -- If none of the arguments evaluate to a gradual type, proceed to step 6. -- If one or more arguments evaluate to a gradual type but no possible - materializations of these types would disambiguate between the remaining - overloads, proceed to step 6. -- If possible materializations of these types would disambiguate between - two or more of the remaining overloads and this subset of overloads have - consistent return types, proceed to step 6. If the return types include - type variables, constraint solving should be applied here before testing - for consistency. -- If none of the above conditions are met, the presence of gradual types - leads to an ambiguous overload selection. Assume a return type of ``Any`` - and stop. This preserves the "gradual guarantee". - - -[Eric's note for reviewers: I'm struggling to come up with an -understandable and unambiguous way to describe this step. -Suggestions are welcome.] - -[Eric's note for reviewers: Pyright currently does not use return type -consistency in the above check. Instead, it looks for non-overlapping -return types. If return types are overlapping (that is, one is a consistent -subtype of another), it uses the wider return type. Only if there is no -consistency relationship between return types does it consider it an -"ambiguous" situation and turns it into an Any. This produces better -results for users of language servers, but it doesn't strictly preserve -the gradual guarantee. I'm willing to abandon this in favor of a -strict consistency check.] +Step 5: For each argument, determine whether all possible +:term:`materializations ` of the argument's type are assignable to +the corresponding parameter type for each of the remaining overloads. If so, +eliminate all of the subsequent remaining overloads. + +For example, if the argument type is ``list[Any]`` and there are three remaining +overloads with corresponding parameter types of ``list[int]``, ``list[Any]`` +and ``Any``. We can eliminate the third of the remaining overloads because +all manifestations of ``list[Any]`` are assignable to ``list[Any]``, the parameter +in the second overload. We cannot eliminate the second overload because there +are possible manifestations of ``list[Any]`` (for example, ``list[str]``) that +are not assignable to ``list[int]``. + +Once this filtering process is applied for all arguments, examine the return +types of the remaining overloads. If these return types include type variables, +they should be replaced with their solved types. If the resulting return types +for all remaining overloads are :term:, proceed to step 6. + +If the return types are not equivalent, overload matching is ambiguous. In +this case, assume a return type of ``Any`` and stop. Step 6: Choose the first remaining candidate overload as the winning match. Evaluate it as if it were a non-overloaded function call and stop. - Example 1:: @overload @@ -356,24 +349,24 @@ Example 2:: @overload def example2(x: int, y: int, z: int) -> int: ... - def test(val: str | int): + def test(values: list[str | int]): # In this example, argument type expansion is # performed on the first two arguments. Expansion # of the third is unnecessary. - r1 = example2(1, val, 1) + r1 = example2(1, values[0], 1) reveal_type(r1) # Should reveal str | int # Here, the types of all three arguments are expanded # without success. - example2(val, val, val) # Error in step 3 + example2(values[0], values[1], values[2]) # Error in step 3 Example 3:: @overload - def example3(x: int) -> int: ... + def example3(x: int, /) -> tuple[int]: ... @overload - def example3(x: int, y: int) -> tuple[int, int]: ... + def example3(x: int, y: int, /) -> tuple[int, int]: ... @overload def example3(*args: int) -> tuple[int, ...]: ... @@ -426,11 +419,12 @@ Example 4:: Argument type expansion ^^^^^^^^^^^^^^^^^^^^^^^ -When performing argument type expansion, the following types should be -expanded: +When performing argument type expansion, a type that is equivalent to +a union of a finite set of subtypes should be expanded into its constituent +subtypes. This includes the following cases. -1. Unions: Each subtype of the union should be considered as a separate -argument type. For example, the type ``int | str`` should be expanded +1. Explicit unions: Each subtype of the union should be considered as a +separate argument type. For example, the type ``int | str`` should be expanded into ``int`` and ``str``. 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. @@ -441,64 +435,10 @@ be expanded into their literal members. 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. 5. Tuples of known length that contain expandable types should be expanded -into all possible combinations of their subtypes. For example, the type +into all possible combinations of their element types. For example, the type ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, ``(int, Literal[False])``, ``(str, Literal[True])``, and ``(str, Literal[False])``. - -[Eric's note for reviewers: I'm not 100% convinced we should -support argument expansion in all of these cases. Tuple expansion, -in particular, can be very costly and can quickly blow up in complexity. -Currently, pyright and mypy support only the case 1 in the list above, -but I have had requests to support 2 and 3.] - -When performing type expansion for an argument, the argument that -is targeted for expansion should be evaluated without the use of -any context. All arguments that are not yet expanded should -continue to be evaluated with the benefit of context supplied by parameter -types within each overload signature. - -Example:: - - class MyDict[T](TypedDict): - x: T - - @overload - def func[T](a: int, b: MyDict[T]) -> T: ... - - @overload - def func(a: str, b: dict[str, int]) -> str: ... - - - def test(val: int | str): - result = func(val, {'x': 1}) - reveal_type(result) # Should reveal "int | str" - -In this case, type expansion is performed on the first argument, -which expands its type from ``int | str`` to ``int`` and ``str``. -The expression for the second argument is evaluated in the context -of both overloads. For the first overload, the second argument evaluates -to ``MyDict[int]``, and for the second overload it evaluates to -``dict[str, int]``. Both overloads are used to evaluate this call, -and the final type of ``result`` is ``int | str``. - -[Eric's note: mypy apparently doesn't do this currently. It evaluates all -arguments without the benefit of context, which produces less-than-ideal -results in some cases.] - - -[Eric's note for reviewers: We may want to provide for argument type expansion -for regular (non-overloaded) calls as well. This came up recently in -[this thread](https://discuss.python.org/t/proposal-relax-un-correlated-constrained-typevars/59658). -I'm a bit hesitant to add this to the spec because it adds significant -complexity to call evaluations and would likely result in a measurable slowdown -in type evaluation, but it's worth considering. We could perhaps mitigate the -slowdown by applying this behavior only when a constrained type variable is -used in the call's signature.] - -[Eric's note for reviewers: What about expansion based on multiple inheritance? -For example, if class C inherits from A and B, should we expand C into A and B -for purposes of overload matching? This could get very expensive and difficult -to spec, and it feels like a significant edge case, so I'm inclined to leave it -out. No one has asked for this, to my knowledge.] +The above list may not be exhaustive, and additional cases may be added in +the future as the type system evolves. From 7591a4dda7d1f3668b4e8233efffe5575cc91aef Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 28 Aug 2024 10:03:26 -0700 Subject: [PATCH 10/38] Incorporated additional feedback from reviewers. --- docs/spec/overload.rst | 54 ++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 09d91fef..5a70d298 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -130,13 +130,19 @@ checkers should report an error or warning if an implementation is missing. Overload definitions within stub files, protocols, and abstract base classes are exempt from this check. -If an overload is decorated with ``@staticmethod`` or ``@classmethod``, -all overloads must be similarly decorated. The implementation, -if present, must be decorated in the same manner. Type checkers should report -an error if these conditions are not met. - -If one or more overloads are decorated with ``@final`` or ``@override`` but the -implementation is not, an error should be reported. +If one overload signature is decorated with ``@staticmethod`` or +``@classmethod``, all overload signatures must be similarly decorated. The +implementation, if present, must also have a consistent decorator. Type +checkers should report an error if these conditions are not met. + +If a ``@final`` or ``@override`` decorator is supplied for a function with +overloads, the decorator should be applied only to the overload implementation +if it is present. If an overload implementation isn't present (for example, in +a stub file), the ``@final`` or ``@override`` decorator should be applied only +to the first overload. Type checkers should enforce these rules and generate +an error when they are violated. If a ``@final`` or ``@override`` decorator +follows these rules, a type checker should treat the decorator as if it is +present on all overloads. Overloads are allowed to use a mixture of ``async def`` and ``def`` statements within the same overload definition. Type checkers should convert @@ -202,6 +208,21 @@ for conditions that are mandated by their usage in the runtime. For example, the ``__get__`` method of a descriptor is often defined using overloads that would partially overlap if the above rule is enforced. +Type checkers may ignore the possibility of multiple inheritance or +intersections involving structural types for purposes of computing overlap. +In the following example, classes ``A`` and ``B`` could theoretically overlap +because there could be a common type ``C`` that derives from both ``A`` and +``B``, but type checkers may choose not to flag this as an overlapping +overload:: + + class A: ... + class B: ... + + @overload + def func(x: A) -> int: ... + @overload + def func(x: B) -> str: ... + If all possible sets of arguments accepted by an overload are also always accepted by an earlier overload, the two overloads are said to "fully overlap". In this case, the latter overload will never be used. This condition @@ -217,21 +238,14 @@ checkers:: def func(x: int) -> int: ... -[Eric's note for reviewers: We've identified a number of subtle issues and -cases where current type checkers do not honor the above rules, especially -for partially-overlapping overloads. At this point, I'm tempted to delete -this section entirely. We could always add it back to the spec later -if and when we find that there's a need for it and we achieve consensus on -the details.] - - Overload call evaluation ^^^^^^^^^^^^^^^^^^^^^^^^ When a type checker evaluates the call of an overloaded function, it attempts to "match" the supplied arguments with one or more overloads. This section describes the algorithm that type checkers should use -for overload matching. +for overload matching. This algorithm should be applied even in the +presence of :ref:. Only the overloads (the ``@overload``-decorated signatures) should be considered for matching purposes. The implementation, if provided, @@ -310,10 +324,10 @@ eliminate all of the subsequent remaining overloads. For example, if the argument type is ``list[Any]`` and there are three remaining overloads with corresponding parameter types of ``list[int]``, ``list[Any]`` and ``Any``. We can eliminate the third of the remaining overloads because -all manifestations of ``list[Any]`` are assignable to ``list[Any]``, the parameter -in the second overload. We cannot eliminate the second overload because there -are possible manifestations of ``list[Any]`` (for example, ``list[str]``) that -are not assignable to ``list[int]``. +all materializations of ``list[Any]`` are assignable to ``list[Any]``, the +parameter in the second overload. We cannot eliminate the second overload +because there are possible materializations of ``list[Any]`` (for example, +``list[str]``) that are not assignable to ``list[int]``. Once this filtering process is applied for all arguments, examine the return types of the remaining overloads. If these return types include type variables, From 91d4adce96eee9bb4b9266a2ca78bcb9a3ee5080 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 28 Aug 2024 19:42:53 -0700 Subject: [PATCH 11/38] Incorporated more feedback. --- docs/spec/overload.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5a70d298..6e574dca 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -192,16 +192,15 @@ If two overloads can accept the same set of arguments, they are said to "partially overlap". If two overloads partially overlap, the return type of the former overload should be assignable to the return type of the latter overload. If this condition doesn't hold, it is indicative of a -programming error and should be reported by type checkers:: - - # These overloads partially overlap because both accept an - # argument of type Literal[0], but the return type int is - # not assignable to str. +programming error and should be reported by type checkers. The purpose of +this check is to prevent unsoundness of this form:: @overload - def func1(x: Literal[0]) -> int: ... + def is_one(x: Literal[0]) -> Literal[True]: ... @overload - def func1(x: int) -> str: ... + def is_one(x: int) -> Literal[False]: ... + + reveal_type(is_one(int(1))) # Reveals Literal[False], but True at runtime Type checkers may exempt certain magic methods from the above check for conditions that are mandated by their usage in the runtime. For example, From 69d6d4a453f6f72f10342211edce586bc2ae5cad Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 13 Dec 2024 13:18:09 -0800 Subject: [PATCH 12/38] Fixed typo in code sample. --- docs/spec/overload.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 6e574dca..074d540a 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -196,7 +196,7 @@ programming error and should be reported by type checkers. The purpose of this check is to prevent unsoundness of this form:: @overload - def is_one(x: Literal[0]) -> Literal[True]: ... + def is_one(x: Literal[1]) -> Literal[True]: ... @overload def is_one(x: int) -> Literal[False]: ... From e13dbbeb4b9436fae86b33ca292475cb02191c39 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Mon, 16 Dec 2024 12:25:36 -0800 Subject: [PATCH 13/38] Update docs/spec/overload.rst Co-authored-by: Eneg <42005170+Enegg@users.noreply.github.com> --- docs/spec/overload.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 074d540a..78c4e501 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -388,7 +388,7 @@ Example 3:: # step 5 do not apply. Step 6 picks the first # overload. r1 = example3(1) - reveal_type(r1) # Should reveal int + reveal_type(r1) # Should reveal tuple[int] # Step 1 eliminates first overload. Step 4 and # step 5 do not apply. Step 6 picks the second From 57495db72d7ba81ae952b3b601ca3a52a5f26935 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Wed, 8 Jan 2025 17:48:27 -0800 Subject: [PATCH 14/38] (very) initial steps on conformance tests --- conformance/results/mypy/overloads_basic.toml | 25 ++++-- conformance/results/mypy/version.toml | 4 +- conformance/results/pyre/overloads_basic.toml | 21 +++-- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_basic.toml | 32 +++++-- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_basic.toml | 43 ++++++++-- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 18 ++-- conformance/tests/overloads_basic.py | 83 ++++++++++++++++--- 10 files changed, 180 insertions(+), 52 deletions(-) diff --git a/conformance/results/mypy/overloads_basic.toml b/conformance/results/mypy/overloads_basic.toml index c42562c9..81cda133 100644 --- a/conformance/results/mypy/overloads_basic.toml +++ b/conformance/results/mypy/overloads_basic.toml @@ -1,12 +1,21 @@ -conformant = "Pass" +conformant = "Partial" +notes = """ +Does not allow an overload with no implementation in an abstract base class. +""" output = """ -overloads_basic.py:37: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] -overloads_basic.py:37: note: Possible overload variants: -overloads_basic.py:37: note: def __getitem__(self, int, /) -> int -overloads_basic.py:37: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes -overloads_basic.py:62: error: Single overload definition, multiple required [misc] -overloads_basic.py:74: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_basic.py:41: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] +overloads_basic.py:41: note: Possible overload variants: +overloads_basic.py:41: note: def __getitem__(self, int, /) -> int +overloads_basic.py:41: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes +overloads_basic.py:66: error: Single overload definition, multiple required [misc] +overloads_basic.py:78: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_basic.py:101: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_basic.py:116: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_basic.py:126: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_basic.py:126: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_basic.py:129: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 101: Unexpected errors ['overloads_basic.py:101: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 3d498066..1012f096 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ -version = "mypy 1.14.0" -test_duration = 1.6 +version = "mypy 1.14.1" +test_duration = 1.7 diff --git a/conformance/results/pyre/overloads_basic.toml b/conformance/results/pyre/overloads_basic.toml index 7a5d5b54..bd7aa8fd 100644 --- a/conformance/results/pyre/overloads_basic.toml +++ b/conformance/results/pyre/overloads_basic.toml @@ -1,11 +1,22 @@ -conformant = "Pass" +conformant = "Partial" notes = """ +Does not allow an overload with no implementation in a Protocol or an abstract base class. """ output = """ -overloads_basic.py:37:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. -overloads_basic.py:63:0 Incompatible overload [43]: At least two overload signatures must be present. -overloads_basic.py:75:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_basic.py:41:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. +overloads_basic.py:67:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_basic.py:79:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. +overloads_basic.py:102:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_basic.py:118:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `118`. +overloads_basic.py:123:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `123`. +overloads_basic.py:126:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_basic.py:131:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `131`. +overloads_basic.py:136:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `136`. +overloads_basic.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 92: Unexpected errors ['overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] +Line 102: Unexpected errors ['overloads_basic.py:102:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 94de3055..e22b34fd 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 7.3 +test_duration = 5.9 diff --git a/conformance/results/pyright/overloads_basic.toml b/conformance/results/pyright/overloads_basic.toml index 5171f52e..b425209b 100644 --- a/conformance/results/pyright/overloads_basic.toml +++ b/conformance/results/pyright/overloads_basic.toml @@ -1,11 +1,31 @@ -conformant = "Pass" +conformant = "Partial" +notes = """ +Does not allow an overload with no implementation in an abstract base class. +""" output = """ -overloads_basic.py:37:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) -overloads_basic.py:37:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__" +overloads_basic.py:41:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) +overloads_basic.py:41:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__"   "Literal['']" is not assignable to "slice[Any, Any, Any]" (reportArgumentType) -overloads_basic.py:63:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) -overloads_basic.py:75:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_basic.py:67:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_basic.py:79:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_basic.py:102:9 - error: "func4" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_basic.py:118:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_basic.py:126:9 - error: Overloaded implementation is not consistent with signature of overload 1 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "int" is incompatible with type "Self@C" +      Type "int" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_basic.py:126:9 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "str" is incompatible with type "Self@C" +      Type "str" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_basic.py:131:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_basic.py:139:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 102: Unexpected errors ['overloads_basic.py:102:9 - error: "func4" is marked as overload, but no implementation is provided (reportNoOverloadImplementation)'] """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index e8e963b7..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.2 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_basic.toml b/conformance/results/pytype/overloads_basic.toml index 06e69b8c..4bcdb001 100644 --- a/conformance/results/pytype/overloads_basic.toml +++ b/conformance/results/pytype/overloads_basic.toml @@ -2,28 +2,57 @@ conformant = "Partial" notes = """ Does not reject a function with a single @overload signature. Does not reject a function with @overload signature but no implementation. +Does not allow an overload with no implementation in a Protocol or an abstract base class. +Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`. +Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. """ output = """ -overloads_basic.py:31:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] +overloads_basic.py:35:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] return b"" \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:37:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] +overloads_basic.py:41:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] b[""] # E: no matching overload \u001b[1m\u001b[31m~~~~~\u001b[39m\u001b[0m -overloads_basic.py:58:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] +overloads_basic.py:62:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] pass \u001b[1m\u001b[31m~~~~\u001b[39m\u001b[0m +overloads_basic.py:98:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_basic.py:108:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_basic.py:119:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_basic.py:124:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + """ conformance_automated = "Fail" errors_diff = """ -Lines 62, 63: Expected error (tag 'func1') -Lines 74, 75: Expected error (tag 'func2') -Line 31: Unexpected errors ['overloads_basic.py:31:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] -Line 58: Unexpected errors ['overloads_basic.py:58:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] +Lines 66, 67: Expected error (tag 'func1') +Lines 78, 79: Expected error (tag 'func2') +Lines 116, 118, 123, 126: Expected error (tag 'func5') +Lines 129, 131, 136, 139: Expected error (tag 'func6') +Line 35: Unexpected errors ['overloads_basic.py:35:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] +Line 62: Unexpected errors ['overloads_basic.py:62:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] +Line 98: Unexpected errors ["overloads_basic.py:98:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] +Line 108: Unexpected errors ["overloads_basic.py:108:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 119: Unexpected errors ['overloads_basic.py:119:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 124: Unexpected errors ['overloads_basic.py:124:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 94cd5eee..c33e3893 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 37.2 +test_duration = 30.3 diff --git a/conformance/results/results.html b/conformance/results/results.html index e388ee38..de3b3ff0 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -158,17 +158,17 @@

Python Type System Conformance Test Results

- - - - - + + + + - + + + + + + + - + - + - - - + + + - + - + - + - + diff --git a/conformance/src/test_groups.py b/conformance/src/test_groups.py index f0145ff1..b974d401 100644 --- a/conformance/src/test_groups.py +++ b/conformance/src/test_groups.py @@ -4,6 +4,7 @@ """ from dataclasses import dataclass +from itertools import chain from pathlib import Path from typing import Mapping, Sequence @@ -39,7 +40,7 @@ def get_test_cases( # files that support one or more tests. test_cases = [ p - for p in Path(tests_dir).glob("*.py") + for p in chain(tests_dir.glob("*.py"), tests_dir.glob("*.pyi")) if p.name.split("_")[0] in test_group_names ] diff --git a/conformance/tests/overloads_invalid.py b/conformance/tests/overloads_definitions.py similarity index 99% rename from conformance/tests/overloads_invalid.py rename to conformance/tests/overloads_definitions.py index 83ce3873..eb85f4bd 100644 --- a/conformance/tests/overloads_invalid.py +++ b/conformance/tests/overloads_definitions.py @@ -1,5 +1,5 @@ """ -Tests errors on invalid `@typing.overload` usage. +Tests valid/invalid definition of overloaded functions. """ from abc import ABC, abstractmethod From ac3b70ec36edd8fe7b3a944405813bcb1309892f Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 15:09:06 -0800 Subject: [PATCH 21/38] add support for stub test files, add overloads_definitions_stub.pyi --- .../results/mypy/overloads_definitions.toml | 31 +--- .../mypy/overloads_definitions_stub.toml | 18 ++ .../results/pyre/overloads_definitions.toml | 12 +- .../pyre/overloads_definitions_stub.toml | 27 +++ conformance/results/pyre/version.toml | 2 +- .../pyright/overloads_definitions.toml | 10 +- .../pyright/overloads_definitions_stub.toml | 20 ++ .../results/pytype/overloads_definitions.toml | 73 ++++++-- .../pytype/overloads_definitions_stub.toml | 17 ++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 +- conformance/src/type_checker.py | 4 +- conformance/tests/overloads_definitions.py | 32 +++- .../tests/overloads_definitions_stub.pyi | 175 ++++++++++++++++++ 14 files changed, 366 insertions(+), 69 deletions(-) create mode 100644 conformance/results/mypy/overloads_definitions_stub.toml create mode 100644 conformance/results/pyre/overloads_definitions_stub.toml create mode 100644 conformance/results/pyright/overloads_definitions_stub.toml create mode 100644 conformance/results/pytype/overloads_definitions_stub.toml create mode 100644 conformance/tests/overloads_definitions_stub.pyi diff --git a/conformance/results/mypy/overloads_definitions.toml b/conformance/results/mypy/overloads_definitions.toml index 6a76b7f3..d38c913f 100644 --- a/conformance/results/mypy/overloads_definitions.toml +++ b/conformance/results/mypy/overloads_definitions.toml @@ -2,10 +2,11 @@ conformant = "Partial" conformance_automated = "Fail" notes = """ Does not allow an overload with no implementation in an abstract base class. +Allows @override to be on all overloads and implementation, instead of just implementation. """ errors_diff = """ +Line 245: Expected 1 errors Line 49: Unexpected errors ['overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] -Line 202: Unexpected errors ['overloads_definitions.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override]'] """ output = """ overloads_definitions.py:14: error: Single overload definition, multiple required [misc] @@ -18,30 +19,6 @@ overloads_definitions.py:88: error: Overloaded function implementation does not overloads_definitions.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] overloads_definitions.py:133: error: @final should be applied only to overload implementation [misc] overloads_definitions.py:148: error: @final should be applied only to overload implementation [misc] -overloads_definitions.py:176: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] -overloads_definitions.py:176: error: Signature of "final_method" incompatible with supertype "Base" [override] -overloads_definitions.py:176: note: Superclass: -overloads_definitions.py:176: note: @overload -overloads_definitions.py:176: note: def final_method(self, x: int) -> int -overloads_definitions.py:176: note: @overload -overloads_definitions.py:176: note: def final_method(self, x: str) -> str -overloads_definitions.py:176: note: Subclass: -overloads_definitions.py:176: note: def final_method(self, x: int | str) -> int | str -overloads_definitions.py:183: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] -overloads_definitions.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override] -overloads_definitions.py:202: note: Superclass: -overloads_definitions.py:202: note: def good_override(self, x: int | str) -> int | str -overloads_definitions.py:202: note: Subclass: -overloads_definitions.py:202: note: @overload -overloads_definitions.py:202: note: def good_override(self, x: int) -> int -overloads_definitions.py:202: note: @overload -overloads_definitions.py:202: note: def good_override(self, x: str) -> str -overloads_definitions.py:217: error: Signature of "to_override" incompatible with supertype "Base" [override] -overloads_definitions.py:217: note: Superclass: -overloads_definitions.py:217: note: def to_override(self, x: int | str) -> int | str -overloads_definitions.py:217: note: Subclass: -overloads_definitions.py:217: note: @overload -overloads_definitions.py:217: note: def to_override(self, x: int) -> int -overloads_definitions.py:217: note: @overload -overloads_definitions.py:217: note: def to_override(self, x: str) -> str +overloads_definitions.py:196: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions.py:211: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] """ diff --git a/conformance/results/mypy/overloads_definitions_stub.toml b/conformance/results/mypy/overloads_definitions_stub.toml new file mode 100644 index 00000000..d1623d20 --- /dev/null +++ b/conformance/results/mypy/overloads_definitions_stub.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Allows @override to appear in a stub file not on the first overload. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +""" +output = """ +overloads_definitions_stub.pyi:13: error: Single overload definition, multiple required [misc] +overloads_definitions_stub.pyi:37: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:38: error: Self argument missing for a non-static method (or an invalid type for self) [misc] +overloads_definitions_stub.pyi:46: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:85: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:101: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:128: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions_stub.pyi:140: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/pyre/overloads_definitions.toml b/conformance/results/pyre/overloads_definitions.toml index b9aaf9e4..fc29f762 100644 --- a/conformance/results/pyre/overloads_definitions.toml +++ b/conformance/results/pyre/overloads_definitions.toml @@ -5,12 +5,12 @@ Does not allow an overload with no implementation in a Protocol or an abstract b Expects @final/@override on all overloads and implementation, instead of implementation only. """ errors_diff = """ -Line 217: Expected 1 errors +Line 245: Expected 1 errors Lines 148, 150: Expected error (tag 'invalid_final_2') Line 40: Unexpected errors ['overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] Line 51: Unexpected errors ['overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] 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).'] -Line 211: Unexpected errors ['overloads_definitions.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +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).'] """ output = """ overloads_definitions.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. @@ -25,8 +25,8 @@ overloads_definitions.py:97:4 Incompatible overload [43]: The implementation of overloads_definitions.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). overloads_definitions.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_definitions.py:176:4 Invalid override [40]: `overloads_definitions.Child.final_method` cannot override final method defined in `Base`. -overloads_definitions.py:192:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_definitions.py:192: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`. -overloads_definitions.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:204:4 Invalid override [40]: `overloads_definitions.Child.final_method` cannot override final method defined in `Base`. +overloads_definitions.py:220:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +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`. +overloads_definitions.py:239:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ diff --git a/conformance/results/pyre/overloads_definitions_stub.toml b/conformance/results/pyre/overloads_definitions_stub.toml new file mode 100644 index 00000000..93772e38 --- /dev/null +++ b/conformance/results/pyre/overloads_definitions_stub.toml @@ -0,0 +1,27 @@ +conformant = "Partial" +notes = """ +Expects @final and @override to be present on all overloads, not just first. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 122, 128, 129, 133: Expected error (tag 'override-final') +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).'] +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).'] +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).'] +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).'] +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).'] +""" +output = """ +overloads_definitions_stub.pyi:14:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_definitions_stub.pyi:43:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:52:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:75:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:91:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +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`. +overloads_definitions_stub.pyi:146:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:162:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions_stub.pyi:174:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index e22b34fd..cd4e9e46 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 5.9 +test_duration = 6.7 diff --git a/conformance/results/pyright/overloads_definitions.toml b/conformance/results/pyright/overloads_definitions.toml index 252e6fb0..6a28a589 100644 --- a/conformance/results/pyright/overloads_definitions.toml +++ b/conformance/results/pyright/overloads_definitions.toml @@ -4,7 +4,7 @@ Allows @final/@override on all overloads and implementation; should be implement """ conformance_automated = "Fail" errors_diff = """ -Line 217: Expected 1 errors +Line 245: Expected 1 errors Lines 148, 150: Expected error (tag 'invalid_final_2') """ output = """ @@ -27,10 +27,6 @@ overloads_definitions.py:88:9 - error: Overloaded implementation is not consiste overloads_definitions.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) overloads_definitions.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) overloads_definitions.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) -overloads_definitions.py:176:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) -overloads_definitions.py:176:9 - error: Method "final_method" overrides class "Base" in an incompatible manner -  Return type mismatch: base method returns type "int", override returns type "int | str" -    Type "int | str" is not assignable to type "int" -      "str" is not assignable to "int" (reportIncompatibleMethodOverride) -overloads_definitions.py:192:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +overloads_definitions.py:204:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_definitions.py:220:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) """ diff --git a/conformance/results/pyright/overloads_definitions_stub.toml b/conformance/results/pyright/overloads_definitions_stub.toml new file mode 100644 index 00000000..6725a7a3 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions_stub.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Does not enforce that @final/@override in a stub should be only on first overload. +Does not enforce @override when correctly used with an overloaded method in a stub file. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 140, 142: Expected error (tag 'bad_override') +""" +output = """ +overloads_definitions_stub.pyi:14:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions_stub.pyi:38:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:38:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions_stub.pyi:48:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:52:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions_stub.pyi:133:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +""" diff --git a/conformance/results/pytype/overloads_definitions.toml b/conformance/results/pytype/overloads_definitions.toml index 33ab8bd7..ccd16630 100644 --- a/conformance/results/pytype/overloads_definitions.toml +++ b/conformance/results/pytype/overloads_definitions.toml @@ -8,7 +8,7 @@ Does not enforce any rules on location of @final or @override decorators. """ conformance_automated = "Fail" errors_diff = """ -Line 217: Expected 1 errors +Line 245: Expected 1 errors Lines 14, 15: Expected error (tag 'func1') Lines 26, 27: Expected error (tag 'func2') Lines 63, 64: Expected error (tag 'not_abstract') @@ -25,12 +25,12 @@ Line 86: Unexpected errors ['overloads_definitions.py:86:9: \\x1b[1m\\x1b[31merr Line 129: Unexpected errors ['overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] Line 143: Unexpected errors ['overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] Line 160: Unexpected errors ['overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] -Line 165: Unexpected errors ['overloads_definitions.py:165:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] -Line 168: Unexpected errors ['overloads_definitions.py:168:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] -Line 177: Unexpected errors ['overloads_definitions.py:177:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] -Line 193: Unexpected errors ['overloads_definitions.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_definitions.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] -Line 212: Unexpected errors ['overloads_definitions.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] -Line 229: Unexpected errors ['overloads_definitions.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 176: Unexpected errors ['overloads_definitions.py:176:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:176:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 187: Unexpected errors ['overloads_definitions.py:187:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:187:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 205: Unexpected errors ['overloads_definitions.py:205:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_definitions.py:205:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 221: Unexpected errors ['overloads_definitions.py:221:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_definitions.py:221:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] +Line 240: Unexpected errors ['overloads_definitions.py:240:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:240:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 257: Unexpected errors ['overloads_definitions.py:257:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:257:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] """ output = """ overloads_definitions.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] @@ -103,17 +103,27 @@ overloads_definitions.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:165:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:176:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:168:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:176:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:171:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] +overloads_definitions.py:187:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:187:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:190:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] class Child(Base): # E[override-final] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m @@ -121,8 +131,26 @@ class Child(Base): # E[override-final] \u001b[1m\u001b[31m\u001b[39m\u001b[0m # The correctly-decorated @final method `Base.final_method` should cause an \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m - # error if overridden in a child class: -\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # error if overridden in a child class (we use an overload here to avoid +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # questions of override LSP compatibility and focus only on the override): +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: int) -> int: # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: str) -> str: # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m \u001b[1m\u001b[31m\u001b[39m\u001b[0m def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method @@ -234,44 +262,49 @@ class Child(Base): # E[override-final] ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_definitions.py:177:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_definitions.py:205:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_definitions.py:205:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:184:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] +overloads_definitions.py:212:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] def bad_override(self, x: int) -> int: # E[bad_override] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_definitions.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_definitions.py:221:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_definitions.py:221:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:240:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:240:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:257:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_definitions.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:257:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/overloads_definitions_stub.toml b/conformance/results/pytype/overloads_definitions_stub.toml new file mode 100644 index 00000000..de2b6eda --- /dev/null +++ b/conformance/results/pytype/overloads_definitions_stub.toml @@ -0,0 +1,17 @@ +conformant = "Fail" +notes = """ +Does not enforce any of the specified rules regarding overload definitions. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 168: Expected 1 errors +Lines 13, 14: Expected error (tag 'func1') +Lines 37, 38, 43: Expected error (tag 'func5') +Lines 46, 48, 52: Expected error (tag 'func6') +Lines 80, 82, 85, 87: Expected error (tag 'invalid_final') +Lines 96, 98, 101: Expected error (tag 'invalid_final_2') +Lines 122, 128, 129, 133: Expected error (tag 'override-final') +Lines 140, 142: Expected error (tag 'bad_override') +""" +output = """ +""" diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 7dff3b18..5e9befee 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.1 +test_duration = 30.0 diff --git a/conformance/results/results.html b/conformance/results/results.html index be3f3a9b..8abded90 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
- + + + + + + + diff --git a/conformance/src/type_checker.py b/conformance/src/type_checker.py index b6b60fe7..21bec0d8 100644 --- a/conformance/src/type_checker.py +++ b/conformance/src/type_checker.py @@ -269,7 +269,7 @@ def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: line_to_errors: dict[int, list[str]] = {} for line in output: # Ignore multi-line errors - if ".py:" not in line: + if ".py:" not in line and ".pyi:" not in line: continue # Ignore reveal_type errors if "Revealed type [-1]" in line: @@ -378,7 +378,7 @@ def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: # annotations_forward_refs.py:103:1: unexpected indent [python-compiler-error] line_to_errors: dict[int, list[str]] = {} for line in output: - match = re.search(r"^[a-zA-Z0-9_]+.py:(\d+):(\d+): ", line) + match = re.search(r"^[a-zA-Z0-9_]+.pyi?:(\d+):(\d+): ", line) if match is not None: lineno = int(match.group(1)) line_to_errors.setdefault(int(lineno), []).append(line) diff --git a/conformance/tests/overloads_definitions.py b/conformance/tests/overloads_definitions.py index eb85f4bd..a2323d7f 100644 --- a/conformance/tests/overloads_definitions.py +++ b/conformance/tests/overloads_definitions.py @@ -159,11 +159,30 @@ def invalid_final_2(self, x: str) -> str: def invalid_final_2(self, x: int | str) -> int | str: ... - # These methods are just here for the @override test below: + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... def good_override(self, x: int | str) -> int | str: ... + @overload + def to_override(self, x: int) -> int: + ... + + @overload + def to_override(self, x: str) -> str: + ... + def to_override(self, x: int | str) -> int | str: ... @@ -171,7 +190,16 @@ def to_override(self, x: int | str) -> int | str: class Child(Base): # E[override-final] # The correctly-decorated @final method `Base.final_method` should cause an - # error if overridden in a child class: + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method ... diff --git a/conformance/tests/overloads_definitions_stub.pyi b/conformance/tests/overloads_definitions_stub.pyi new file mode 100644 index 00000000..f86f2980 --- /dev/null +++ b/conformance/tests/overloads_definitions_stub.pyi @@ -0,0 +1,175 @@ +""" +Tests valid/invalid definition of overloaded functions in stub files, where the +rules differ from non-stubs, since an implementation is not required. +""" + +from typing import ( + final, + overload, + override, +) + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +@overload +def func2(x: int) -> int: + ... + + +@overload +def func2(x: str) -> str: + ... + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + def func5(x: int) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str) -> str: # E[func5] + ... + + @overload # E[func6] + @classmethod + def func6(cls, x: int) -> int: # E[func6] + ... + + @overload + def func6(cls, x: str) -> str: # E[func6] + ... + + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + + # This is a good definition of an overloaded final method in a stub (@final + # decorator on first overload only): + + @overload + @final + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... + + # The @final decorator should not be on multiple overloads: + + @overload # E[invalid_final] @final should be on first overload + @final + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + + @overload # E[invalid_final] + @final + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + + @overload + def invalid_final(self, x: bytes) -> bytes: + ... + + # The @final decorator should not be on all overloads: + + @overload # E[invalid_final_2] @final should be on first overload + @final + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] + ... + + @overload # E[invalid_final_2] + @final + def invalid_final_2(self, x: str) -> str: + ... + + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: ... + @overload + def good_override(self, x: str) -> str: ... + + @overload + def to_override(self, x: int) -> int: ... + @overload + def to_override(self, x: str) -> str: ... + + +class Child(Base): # E[override-final] + + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: # E[override-final] + ... + + @overload + def final_method(self, x: str) -> str: # E[override-final] can't override final method + ... + + # This is the right way to mark an overload as @override (decorate first + # overload only), so the use of @override should cause an error (because + # there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + @override + def bad_override(self, x: int) -> int: # E[bad_override] + ... + + @overload + def bad_override(self, x: str) -> str: + ... + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + @override + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E: @override should appear only on first overload + def to_override(self, x: int) -> int: + ... + + @overload + @override + def to_override(self, x: str) -> str: + ... From 87377edba7c2813c084293ee01c872ff4aa71ca1 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 15:35:53 -0800 Subject: [PATCH 22/38] add initial overloads_consistency tests --- .../results/mypy/overloads_consistency.toml | 8 ++++ .../results/pyre/overloads_consistency.toml | 8 ++++ conformance/results/pyre/version.toml | 2 +- .../pyright/overloads_consistency.toml | 13 ++++++ .../results/pytype/overloads_consistency.toml | 23 ++++++++++ .../results/pytype/overloads_definitions.toml | 8 ++-- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 10 ++++- conformance/tests/overloads_consistency.py | 43 +++++++++++++++++++ 9 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 conformance/results/mypy/overloads_consistency.toml create mode 100644 conformance/results/pyre/overloads_consistency.toml create mode 100644 conformance/results/pyright/overloads_consistency.toml create mode 100644 conformance/results/pytype/overloads_consistency.toml create mode 100644 conformance/tests/overloads_consistency.py diff --git a/conformance/results/mypy/overloads_consistency.toml b/conformance/results/mypy/overloads_consistency.toml new file mode 100644 index 00000000..ae047845 --- /dev/null +++ b/conformance/results/mypy/overloads_consistency.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:27: error: Overloaded function implementation cannot produce return type of signature 2 [misc] +overloads_consistency.py:42: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +""" diff --git a/conformance/results/pyre/overloads_consistency.toml b/conformance/results/pyre/overloads_consistency.toml new file mode 100644 index 00000000..5dc6d632 --- /dev/null +++ b/conformance/results/pyre/overloads_consistency.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:24:0 Incompatible overload [43]: The return type of overloaded function `return_type` (`str`) is incompatible with the return type of the implementation (`int`). +overloads_consistency.py:39:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `39`. +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cd4e9e46..cda658b7 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.7 +test_duration = 6.6 diff --git a/conformance/results/pyright/overloads_consistency.toml b/conformance/results/pyright/overloads_consistency.toml new file mode 100644 index 00000000..6fd49180 --- /dev/null +++ b/conformance/results/pyright/overloads_consistency.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:27:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Function return type "str" is incompatible with type "int" +    "str" is not assignable to "int" (reportInconsistentOverload) +overloads_consistency.py:42:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(x: int) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter 1: type "str" is incompatible with type "int" +      "str" is not assignable to "int" (reportInconsistentOverload) +""" diff --git a/conformance/results/pytype/overloads_consistency.toml b/conformance/results/pytype/overloads_consistency.toml new file mode 100644 index 00000000..564d670d --- /dev/null +++ b/conformance/results/pytype/overloads_consistency.toml @@ -0,0 +1,23 @@ +conformant = "Fail" +notes = """ +Doesn't appear to validate overload consistency at all. +""" +conformance_automated = "Fail" +errors_diff = """ +Lines 24, 27: Expected error (tag 'return_type') +Lines 39, 42: Expected error (tag 'parameter_type') +Line 28: Unexpected errors ['overloads_consistency.py:28:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in return_type: bad return type [bad-return-type]'] +Line 43: Unexpected errors ['overloads_consistency.py:43:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in parameter_type: bad return type [bad-return-type]'] +""" +output = """ +overloads_consistency.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return_type: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_consistency.py:43:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in parameter_type: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/overloads_definitions.toml b/conformance/results/pytype/overloads_definitions.toml index ccd16630..e5307735 100644 --- a/conformance/results/pytype/overloads_definitions.toml +++ b/conformance/results/pytype/overloads_definitions.toml @@ -139,16 +139,16 @@ class Child(Base): # E[override-final] \u001b[1m\u001b[31m\u001b[39m\u001b[0m @overload # E[override-final] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m - def final_method(self, x: int) -> int: # E[override-final] -\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m \u001b[1m\u001b[31m\u001b[39m\u001b[0m @overload \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m - def final_method(self, x: str) -> str: # E[override-final] -\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 5e9befee..c33e3893 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.0 +test_duration = 30.3 diff --git a/conformance/results/results.html b/conformance/results/results.html index 8abded90..d05a793f 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
+ + + + + + diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py new file mode 100644 index 00000000..ffb9f1c2 --- /dev/null +++ b/conformance/tests/overloads_consistency.py @@ -0,0 +1,43 @@ +""" +Tests consistency of overloads with implementation. +""" + +from typing import overload + +# > If an overload implementation is defined, type checkers should validate +# > that it is consistent with all of its associated overload signatures. +# > The implementation should accept all potential sets of arguments +# > that are accepted by the overloads and should produce all potential return +# > types produced by the overloads. In typing terms, this means the input +# > signature of the implementation should be :term: to the input +# > signatures of all overloads, and the return type of all overloads should be +# > assignable to the return type of the implementation. + +# Return type of all overloads must be assignable to return type of +# implementation: + +@overload +def return_type(x: int) -> int: + ... + +@overload +def return_type(x: str) -> str: # E[return_type] + ... + +def return_type(x: int | str) -> int: # E[return_type] an overload returns `str`, not assignable to `int` + return 1 + + +# Input signature of implementation must be assignable to signature of each +# overload: + +@overload +def parameter_type(x: int) -> int: + ... + +@overload +def parameter_type(x: str) -> str: # E[parameter_type] + ... + +def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x` + return 1 From f7bf384ded9d2c2ed878ec639deeb322c43dd677 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 16:36:15 -0800 Subject: [PATCH 23/38] add tests for mixed async-def --- .../results/mypy/overloads_consistency.toml | 2 +- .../results/pyre/overloads_consistency.toml | 2 +- conformance/results/pyre/version.toml | 2 +- .../pyright/overloads_consistency.toml | 2 +- .../results/pytype/overloads_consistency.toml | 23 ++++++-- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 ++--- conformance/tests/overloads_consistency.py | 52 ++++++++++++++++++- 8 files changed, 81 insertions(+), 16 deletions(-) diff --git a/conformance/results/mypy/overloads_consistency.toml b/conformance/results/mypy/overloads_consistency.toml index ae047845..6c02c196 100644 --- a/conformance/results/mypy/overloads_consistency.toml +++ b/conformance/results/mypy/overloads_consistency.toml @@ -4,5 +4,5 @@ errors_diff = """ """ output = """ overloads_consistency.py:27: error: Overloaded function implementation cannot produce return type of signature 2 [misc] -overloads_consistency.py:42: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_consistency.py:43: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] """ diff --git a/conformance/results/pyre/overloads_consistency.toml b/conformance/results/pyre/overloads_consistency.toml index 5dc6d632..a8688fd8 100644 --- a/conformance/results/pyre/overloads_consistency.toml +++ b/conformance/results/pyre/overloads_consistency.toml @@ -4,5 +4,5 @@ errors_diff = """ """ output = """ overloads_consistency.py:24:0 Incompatible overload [43]: The return type of overloaded function `return_type` (`str`) is incompatible with the return type of the implementation (`int`). -overloads_consistency.py:39:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `39`. +overloads_consistency.py:40:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `40`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cda658b7..028806c2 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.6 +test_duration = 6.5 diff --git a/conformance/results/pyright/overloads_consistency.toml b/conformance/results/pyright/overloads_consistency.toml index 6fd49180..56f06507 100644 --- a/conformance/results/pyright/overloads_consistency.toml +++ b/conformance/results/pyright/overloads_consistency.toml @@ -6,7 +6,7 @@ output = """ overloads_consistency.py:27:5 - error: Overloaded implementation is not consistent with signature of overload 2   Function return type "str" is incompatible with type "int"     "str" is not assignable to "int" (reportInconsistentOverload) -overloads_consistency.py:42:5 - error: Overloaded implementation is not consistent with signature of overload 2 +overloads_consistency.py:43:5 - error: Overloaded implementation is not consistent with signature of overload 2   Type "(x: int) -> (int | str)" is not assignable to type "(x: str) -> str"     Parameter 1: type "str" is incompatible with type "int"       "str" is not assignable to "int" (reportInconsistentOverload) diff --git a/conformance/results/pytype/overloads_consistency.toml b/conformance/results/pytype/overloads_consistency.toml index 564d670d..bc68c9cf 100644 --- a/conformance/results/pytype/overloads_consistency.toml +++ b/conformance/results/pytype/overloads_consistency.toml @@ -5,9 +5,11 @@ Doesn't appear to validate overload consistency at all. conformance_automated = "Fail" errors_diff = """ Lines 24, 27: Expected error (tag 'return_type') -Lines 39, 42: Expected error (tag 'parameter_type') +Lines 40, 43: Expected error (tag 'parameter_type') Line 28: Unexpected errors ['overloads_consistency.py:28:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in return_type: bad return type [bad-return-type]'] -Line 43: Unexpected errors ['overloads_consistency.py:43:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in parameter_type: bad return type [bad-return-type]'] +Line 44: Unexpected errors ['overloads_consistency.py:44:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in parameter_type: bad return type [bad-return-type]'] +Line 73: Unexpected errors ['overloads_consistency.py:73:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine: bad return type [bad-return-type]'] +Line 88: Unexpected errors ['overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]', 'overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]'] """ output = """ overloads_consistency.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return_type: bad return type [bad-return-type] @@ -15,9 +17,24 @@ overloads_consistency.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_consistency.py:43:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in parameter_type: bad return type [bad-return-type] +overloads_consistency.py:44:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in parameter_type: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m +overloads_consistency.py:73:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_consistency.py:88:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine_2: bad return type [bad-return-type] + + return _wrapped(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_consistency.py:88:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in returns_coroutine_2: bad return type [bad-return-type] + + return _wrapped(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index c33e3893..7dff3b18 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.3 +test_duration = 30.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index d05a793f..94181021 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
- - - - + + + + diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py index ffb9f1c2..b3a54eed 100644 --- a/conformance/tests/overloads_consistency.py +++ b/conformance/tests/overloads_consistency.py @@ -2,7 +2,7 @@ Tests consistency of overloads with implementation. """ -from typing import overload +from typing import Coroutine, overload # > If an overload implementation is defined, type checkers should validate # > that it is consistent with all of its associated overload signatures. @@ -29,7 +29,8 @@ def return_type(x: int | str) -> int: # E[return_type] an overload returns `str # Input signature of implementation must be assignable to signature of each -# overload: +# overload. We don't attempt a thorough testing of input signature +# assignability here; see `callables_subtyping.py` for that: @overload def parameter_type(x: int) -> int: @@ -41,3 +42,50 @@ def parameter_type(x: str) -> str: # E[parameter_type] def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x` return 1 + + +# > Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +# > within the same overload definition. Type checkers should convert +# > ``async def`` statements to a non-async signature (wrapping the return +# > type in a ``Coroutine``) before testing for implementation consistency +# > and overlapping overloads (described below). + +# ...and also... + +# > When a type checker checks the implementation for consistency with overloads, +# > it should first apply any transforms that change the effective type of the +# > implementation including the presence of a ``yield`` statement in the +# > implementation body, the use of ``async def``, and the presence of additional +# > decorators. + +# An overload can explicitly return `Coroutine`, while the implementation is an +# `async def`: + +@overload +def returns_coroutine(x: int) -> Coroutine[None, None, int]: + ... + +@overload +async def returns_coroutine(x: str) -> str: + ... + +async def returns_coroutine(x: int | str) -> int | str: + return 1 + + +# The implementation can explicitly return `Coroutine`, while overloads are +# `async def`: + +@overload +async def returns_coroutine_2(x: int) -> int: + ... + +@overload +async def returns_coroutine_2(x: str) -> str: + ... + +def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]: + return _wrapped(x) + +async def _wrapped(x: int | str) -> int | str: + return 2 From 4936ac120d1f6d8c031466270857aeb160ec9467 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 16:50:59 -0800 Subject: [PATCH 24/38] add tests for signature-transforming decorators --- conformance/results/mypy/version.toml | 2 +- .../results/pyre/overloads_consistency.toml | 14 ++++++++-- conformance/results/pyre/version.toml | 2 +- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_consistency.toml | 16 +++++++++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 8 +++--- conformance/tests/overloads_consistency.py | 28 ++++++++++++++++++- 8 files changed, 63 insertions(+), 11 deletions(-) diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 1012f096..9e6527ca 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.7 +test_duration = 1.8 diff --git a/conformance/results/pyre/overloads_consistency.toml b/conformance/results/pyre/overloads_consistency.toml index a8688fd8..eb38a818 100644 --- a/conformance/results/pyre/overloads_consistency.toml +++ b/conformance/results/pyre/overloads_consistency.toml @@ -1,8 +1,18 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not apply decorator transforms before checking overload consistency. +""" +conformance_automated = "Fail" errors_diff = """ +Line 107: Unexpected errors ['overloads_consistency.py:107: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:107:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `107`.'] +Line 111: Unexpected errors ['overloads_consistency.py:111: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:111:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `111`.', 'overloads_consistency.py:111:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] """ output = """ overloads_consistency.py:24:0 Incompatible overload [43]: The return type of overloaded function `return_type` (`str`) is incompatible with the return type of the implementation (`int`). overloads_consistency.py:40:0 Incompatible overload [43]: The implementation of `parameter_type` does not accept all possible arguments of overload defined on line `40`. +overloads_consistency.py:107: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:107:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `107`. +overloads_consistency.py:111: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:111:0 Incompatible overload [43]: The implementation of `decorated` does not accept all possible arguments of overload defined on line `111`. +overloads_consistency.py:111:0 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 028806c2..2904f5c8 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.5 +test_duration = 6.0 diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 1ae5a35b..324d1870 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.5 +test_duration = 1.7 diff --git a/conformance/results/pytype/overloads_consistency.toml b/conformance/results/pytype/overloads_consistency.toml index bc68c9cf..437e1cbb 100644 --- a/conformance/results/pytype/overloads_consistency.toml +++ b/conformance/results/pytype/overloads_consistency.toml @@ -10,6 +10,8 @@ Line 28: Unexpected errors ['overloads_consistency.py:28:12: \\x1b[1m\\x1b[31mer Line 44: Unexpected errors ['overloads_consistency.py:44:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in parameter_type: bad return type [bad-return-type]'] Line 73: Unexpected errors ['overloads_consistency.py:73:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine: bad return type [bad-return-type]'] Line 88: Unexpected errors ['overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]', 'overloads_consistency.py:88:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in returns_coroutine_2: bad return type [bad-return-type]'] +Line 107: Unexpected errors ['overloads_consistency.py:107:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Function decorated expects 0 arg(s), got 1 [wrong-arg-count]'] +Line 115: Unexpected errors ["overloads_consistency.py:115:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Missing parameter 'z' in call to function decorated [missing-parameter]"] """ output = """ overloads_consistency.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return_type: bad return type [bad-return-type] @@ -37,4 +39,18 @@ overloads_consistency.py:88:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in r return _wrapped(x) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_consistency.py:107:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function decorated expects 0 arg(s), got 1 [wrong-arg-count] + +def decorated() -> None: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~\u001b[39m\u001b[0m + +overloads_consistency.py:115:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'z' in call to function decorated [missing-parameter] + +def decorated(y: bytes, z: bytes) -> bytes: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + return b"" +\u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 7dff3b18..1da94b08 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.1 +test_duration = 31.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index 94181021..b3500ae3 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,16 +159,16 @@

Python Type System Conformance Test Results

 
mypy 1.14.0
-
1.6sec
+
mypy 1.14.1
+
1.7sec
pyright 1.1.391
-
1.2sec
+
1.5sec
pyre 0.9.23
-
7.3sec
+
5.9sec
pytype 2024.10.11
-
37.2sec
+
30.3sec
@@ -667,10 +667,10 @@

Python Type System Conformance Test Results

Overloads
     overloads_basicPassPassPass
Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`.

Does not error on overloads inconsistently decorated with `@staticmethod`.

Exceptions diff --git a/conformance/tests/overloads_basic.py b/conformance/tests/overloads_basic.py index 86c81765..c243883f 100644 --- a/conformance/tests/overloads_basic.py +++ b/conformance/tests/overloads_basic.py @@ -1,16 +1,20 @@ """ -Tests the basic typing.overload behavior described in PEP 484. +Tests the behavior of typing.overload. """ # Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload -# Note: The behavior of @overload is severely under-specified by PEP 484 leading -# to significant divergence in behavior across type checkers. This is something -# we will likely want to address in a future update to the typing spec. For now, -# this conformance test will cover only the most basic functionality described -# in PEP 484. - -from typing import Any, Callable, Iterable, Iterator, TypeVar, assert_type, overload +from abc import ABC +from typing import ( + Any, + Callable, + Iterable, + Iterator, + Protocol, + TypeVar, + assert_type, + overload, +) class Bytes: @@ -58,7 +62,7 @@ def map(func: Any, iter1: Any, iter2: Any = ...) -> Any: pass -# At least two overload signatures should be provided. +# > At least two @overload-decorated definitions must be present. @overload # E[func1] def func1() -> None: # E[func1]: At least two overloads must be present ... @@ -68,9 +72,9 @@ def func1() -> None: pass -# > In regular modules, a series of @overload-decorated definitions must be -# > followed by exactly one non-@overload-decorated definition (for the same -# > function/method). +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. @overload # E[func2] def func2(x: int) -> int: # E[func2]: no implementation ... @@ -79,3 +83,58 @@ def func2(x: int) -> int: # E[func2]: no implementation @overload def func2(x: str) -> str: ... + + +# > Overload definitions within stub files, protocols, and abstract base classes +# > are exempt from this check. +class MyProto(Protocol): + @overload + def func3(self, x: int) -> int: + ... + + + @overload + def func3(self, x: str) -> str: + ... + +class MyAbstractBase(ABC): + @overload + def func4(self, x: int) -> int: + ... + + + @overload + def func4(self, x: str) -> str: + ... + + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + @staticmethod + def func5(x: int) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str) -> str: # E[func5] + ... + + def func5(self, x: int | str) -> int | str: # E[func5] + return 1 + + @overload # E[func6] + @classmethod + def func6(cls, x: int) -> int: # E[func6] + ... + + @overload + @classmethod + def func6(cls, x: str) -> str: # E[func6] + ... + + def func6(cls, x: int | str) -> int | str: # E[func6] + return 1 From 27f1c796fac9f39186213f16779c83d9504520a3 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 9 Jan 2025 17:03:12 -0800 Subject: [PATCH 15/38] fix abstractmethod without implementation check --- conformance/results/mypy/overloads_basic.toml | 9 ++++---- conformance/results/pyre/overloads_basic.toml | 17 +++++++------- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_basic.toml | 18 +++++++-------- .../results/pytype/overloads_basic.toml | 23 ++++++++++++------- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 6 ++--- conformance/tests/overloads_basic.py | 20 +++++++++++++--- docs/spec/overload.rst | 4 ++-- 9 files changed, 61 insertions(+), 40 deletions(-) diff --git a/conformance/results/mypy/overloads_basic.toml b/conformance/results/mypy/overloads_basic.toml index 81cda133..c008ff6a 100644 --- a/conformance/results/mypy/overloads_basic.toml +++ b/conformance/results/mypy/overloads_basic.toml @@ -10,10 +10,11 @@ overloads_basic.py:41: note: def __getitem__(self, slice[Any, Any, Any], /) overloads_basic.py:66: error: Single overload definition, multiple required [misc] overloads_basic.py:78: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] overloads_basic.py:101: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_basic.py:116: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] -overloads_basic.py:126: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] -overloads_basic.py:126: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] -overloads_basic.py:129: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_basic.py:115: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_basic.py:130: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_basic.py:140: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_basic.py:140: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_basic.py:143: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] """ conformance_automated = "Fail" errors_diff = """ diff --git a/conformance/results/pyre/overloads_basic.toml b/conformance/results/pyre/overloads_basic.toml index bd7aa8fd..d30f2e9c 100644 --- a/conformance/results/pyre/overloads_basic.toml +++ b/conformance/results/pyre/overloads_basic.toml @@ -7,16 +7,17 @@ overloads_basic.py:41:2 Incompatible parameter type [6]: In call `Bytes.__getite overloads_basic.py:67:0 Incompatible overload [43]: At least two overload signatures must be present. overloads_basic.py:79:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. -overloads_basic.py:102:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. -overloads_basic.py:118:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `118`. -overloads_basic.py:123:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `123`. -overloads_basic.py:126:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_basic.py:131:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `131`. -overloads_basic.py:136:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `136`. -overloads_basic.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_basic.py:103:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_basic.py:116:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. +overloads_basic.py:132:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `132`. +overloads_basic.py:137:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `137`. +overloads_basic.py:140:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_basic.py:145:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `145`. +overloads_basic.py:150:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `150`. +overloads_basic.py:153:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ conformance_automated = "Fail" errors_diff = """ Line 92: Unexpected errors ['overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] -Line 102: Unexpected errors ['overloads_basic.py:102:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +Line 103: Unexpected errors ['overloads_basic.py:103:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index e22b34fd..e296ce8f 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 5.9 +test_duration = 6.2 diff --git a/conformance/results/pyright/overloads_basic.toml b/conformance/results/pyright/overloads_basic.toml index b425209b..b74f4092 100644 --- a/conformance/results/pyright/overloads_basic.toml +++ b/conformance/results/pyright/overloads_basic.toml @@ -1,6 +1,5 @@ -conformant = "Partial" +conformant = "Pass" notes = """ -Does not allow an overload with no implementation in an abstract base class. """ output = """ overloads_basic.py:41:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) @@ -8,24 +7,23 @@ overloads_basic.py:41:1 - error: Argument of type "Literal['']" cannot be assign   "Literal['']" is not assignable to "slice[Any, Any, Any]" (reportArgumentType) overloads_basic.py:67:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) overloads_basic.py:79:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_basic.py:102:9 - error: "func4" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_basic.py:118:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) -overloads_basic.py:126:9 - error: Overloaded implementation is not consistent with signature of overload 1 +overloads_basic.py:116:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_basic.py:132:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_basic.py:140:9 - error: Overloaded implementation is not consistent with signature of overload 1   Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int"     Parameter name mismatch: "x" versus "self"     Parameter 1: type "int" is incompatible with type "Self@C"       Type "int" is not assignable to type "Self@C"     Extra parameter "x" (reportInconsistentOverload) -overloads_basic.py:126:9 - error: Overloaded implementation is not consistent with signature of overload 2 +overloads_basic.py:140:9 - error: Overloaded implementation is not consistent with signature of overload 2   Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str"     Parameter name mismatch: "x" versus "self"     Parameter 1: type "str" is incompatible with type "Self@C"       Type "str" is not assignable to type "Self@C"     Extra parameter "x" (reportInconsistentOverload) -overloads_basic.py:131:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) -overloads_basic.py:139:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_basic.py:145:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_basic.py:153:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 102: Unexpected errors ['overloads_basic.py:102:9 - error: "func4" is marked as overload, but no implementation is provided (reportNoOverloadImplementation)'] """ diff --git a/conformance/results/pytype/overloads_basic.toml b/conformance/results/pytype/overloads_basic.toml index 4bcdb001..ce451452 100644 --- a/conformance/results/pytype/overloads_basic.toml +++ b/conformance/results/pytype/overloads_basic.toml @@ -27,17 +27,22 @@ overloads_basic.py:98:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:108:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] +overloads_basic.py:110:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:119:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_basic.py:122:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:124:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_basic.py:133:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_basic.py:138:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m @@ -47,12 +52,14 @@ conformance_automated = "Fail" errors_diff = """ Lines 66, 67: Expected error (tag 'func1') Lines 78, 79: Expected error (tag 'func2') -Lines 116, 118, 123, 126: Expected error (tag 'func5') -Lines 129, 131, 136, 139: Expected error (tag 'func6') +Lines 115, 116: Expected error (tag 'not_abstract') +Lines 130, 132, 137, 140: Expected error (tag 'func5') +Lines 143, 145, 150, 153: Expected error (tag 'func6') Line 35: Unexpected errors ['overloads_basic.py:35:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] Line 62: Unexpected errors ['overloads_basic.py:62:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] Line 98: Unexpected errors ["overloads_basic.py:98:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] -Line 108: Unexpected errors ["overloads_basic.py:108:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] -Line 119: Unexpected errors ['overloads_basic.py:119:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 124: Unexpected errors ['overloads_basic.py:124:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 110: Unexpected errors ["overloads_basic.py:110:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 122: Unexpected errors ["overloads_basic.py:122:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] +Line 133: Unexpected errors ['overloads_basic.py:133:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 138: Unexpected errors ['overloads_basic.py:138:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index c33e3893..f1ac7b3a 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.3 +test_duration = 30.5 diff --git a/conformance/results/results.html b/conformance/results/results.html index de3b3ff0..74a7a382 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
pyre 0.9.23
-
5.9sec
+
6.2sec
pytype 2024.10.11
-
30.3sec
+
30.5sec
@@ -670,7 +670,7 @@

Python Type System Conformance Test Results

Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`.

Does not error on overloads inconsistently decorated with `@staticmethod`.

Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Exceptions diff --git a/conformance/tests/overloads_basic.py b/conformance/tests/overloads_basic.py index c243883f..c3f63e82 100644 --- a/conformance/tests/overloads_basic.py +++ b/conformance/tests/overloads_basic.py @@ -4,7 +4,7 @@ # Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload -from abc import ABC +from abc import ABC, abstractmethod from typing import ( Any, Callable, @@ -85,8 +85,8 @@ def func2(x: str) -> str: ... -# > Overload definitions within stub files, protocols, and abstract base classes -# > are exempt from this check. +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. class MyProto(Protocol): @overload def func3(self, x: int) -> int: @@ -99,14 +99,28 @@ def func3(self, x: str) -> str: class MyAbstractBase(ABC): @overload + @abstractmethod def func4(self, x: int) -> int: ... @overload + @abstractmethod def func4(self, x: str) -> str: ... + # A non-abstract method in an abstract base class still requires an + # implementation: + + @overload # E[not_abstract] + def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation + ... + + + @overload + def not_abstract(self, x: str) -> str: + ... + # > If one overload signature is decorated with ``@staticmethod`` or # > ``@classmethod``, all overload signatures must be similarly decorated. The diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 78c4e501..c23d875b 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -127,8 +127,8 @@ one is present, an error should be reported. The ``@overload``-decorated definitions must be followed by an overload implementation, which does not include an ``@overload`` decorator. Type checkers should report an error or warning if an implementation is missing. -Overload definitions within stub files, protocols, and abstract base classes -are exempt from this check. +Overload definitions within stub files, protocols, and on abstract methods +within abstract base classes are exempt from this check. If one overload signature is decorated with ``@staticmethod`` or ``@classmethod``, all overload signatures must be similarly decorated. The From 535075ff1d594bf4555a209ff370e4f7e4790676 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 9 Jan 2025 17:29:57 -0800 Subject: [PATCH 16/38] split overloads_invalid.py from overloads_basic.py --- conformance/results/mypy/overloads_basic.toml | 24 ++--- .../results/mypy/overloads_invalid.toml | 18 ++++ conformance/results/pyre/overloads_basic.toml | 22 +--- .../results/pyre/overloads_invalid.toml | 22 ++++ conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_basic.toml | 22 +--- .../results/pyright/overloads_invalid.toml | 24 +++++ .../results/pytype/overloads_basic.toml | 50 +-------- .../results/pytype/overloads_invalid.toml | 47 ++++++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 14 ++- conformance/tests/overloads_basic.py | 93 ---------------- conformance/tests/overloads_invalid.py | 100 ++++++++++++++++++ 13 files changed, 239 insertions(+), 201 deletions(-) create mode 100644 conformance/results/mypy/overloads_invalid.toml create mode 100644 conformance/results/pyre/overloads_invalid.toml create mode 100644 conformance/results/pyright/overloads_invalid.toml create mode 100644 conformance/results/pytype/overloads_invalid.toml create mode 100644 conformance/tests/overloads_invalid.py diff --git a/conformance/results/mypy/overloads_basic.toml b/conformance/results/mypy/overloads_basic.toml index c008ff6a..f592ea60 100644 --- a/conformance/results/mypy/overloads_basic.toml +++ b/conformance/results/mypy/overloads_basic.toml @@ -1,22 +1,10 @@ -conformant = "Partial" -notes = """ -Does not allow an overload with no implementation in an abstract base class. -""" +conformant = "Pass" output = """ -overloads_basic.py:41: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] -overloads_basic.py:41: note: Possible overload variants: -overloads_basic.py:41: note: def __getitem__(self, int, /) -> int -overloads_basic.py:41: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes -overloads_basic.py:66: error: Single overload definition, multiple required [misc] -overloads_basic.py:78: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_basic.py:101: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_basic.py:115: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_basic.py:130: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] -overloads_basic.py:140: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] -overloads_basic.py:140: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] -overloads_basic.py:143: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_basic.py:39: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] +overloads_basic.py:39: note: Possible overload variants: +overloads_basic.py:39: note: def __getitem__(self, int, /) -> int +overloads_basic.py:39: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 101: Unexpected errors ['overloads_basic.py:101: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] """ diff --git a/conformance/results/mypy/overloads_invalid.toml b/conformance/results/mypy/overloads_invalid.toml new file mode 100644 index 00000000..0d351266 --- /dev/null +++ b/conformance/results/mypy/overloads_invalid.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in an abstract base class. +""" +errors_diff = """ +Line 47: Unexpected errors ['overloads_invalid.py:47: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] +""" +output = """ +overloads_invalid.py:12: error: Single overload definition, multiple required [misc] +overloads_invalid.py:24: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:47: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:61: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:76: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_invalid.py:86: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_invalid.py:86: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_invalid.py:89: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +""" diff --git a/conformance/results/pyre/overloads_basic.toml b/conformance/results/pyre/overloads_basic.toml index d30f2e9c..aebf2ded 100644 --- a/conformance/results/pyre/overloads_basic.toml +++ b/conformance/results/pyre/overloads_basic.toml @@ -1,23 +1,7 @@ -conformant = "Partial" -notes = """ -Does not allow an overload with no implementation in a Protocol or an abstract base class. -""" +conformant = "Pass" output = """ -overloads_basic.py:41:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. -overloads_basic.py:67:0 Incompatible overload [43]: At least two overload signatures must be present. -overloads_basic.py:79:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. -overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. -overloads_basic.py:103:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. -overloads_basic.py:116:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. -overloads_basic.py:132:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `132`. -overloads_basic.py:137:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `137`. -overloads_basic.py:140:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_basic.py:145:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `145`. -overloads_basic.py:150:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `150`. -overloads_basic.py:153:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_basic.py:39:2 Incompatible parameter type [6]: In call `Bytes.__getitem__`, for 1st positional argument, expected `int` but got `str`. """ -conformance_automated = "Fail" +conformance_automated = "Pass" errors_diff = """ -Line 92: Unexpected errors ['overloads_basic.py:92:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] -Line 103: Unexpected errors ['overloads_basic.py:103:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] """ diff --git a/conformance/results/pyre/overloads_invalid.toml b/conformance/results/pyre/overloads_invalid.toml new file mode 100644 index 00000000..13f38519 --- /dev/null +++ b/conformance/results/pyre/overloads_invalid.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in a Protocol or an abstract base class. +""" +errors_diff = """ +Line 38: Unexpected errors ['overloads_invalid.py:38:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] +Line 49: Unexpected errors ['overloads_invalid.py:49:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +""" +output = """ +overloads_invalid.py:13:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_invalid.py:25:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_invalid.py:38:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. +overloads_invalid.py:49:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_invalid.py:62:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. +overloads_invalid.py:78:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `78`. +overloads_invalid.py:83:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `83`. +overloads_invalid.py:86:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:91:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `91`. +overloads_invalid.py:96:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `96`. +overloads_invalid.py:99:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index e296ce8f..e22b34fd 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.2 +test_duration = 5.9 diff --git a/conformance/results/pyright/overloads_basic.toml b/conformance/results/pyright/overloads_basic.toml index b74f4092..14bfb58b 100644 --- a/conformance/results/pyright/overloads_basic.toml +++ b/conformance/results/pyright/overloads_basic.toml @@ -2,27 +2,9 @@ conformant = "Pass" notes = """ """ output = """ -overloads_basic.py:41:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) -overloads_basic.py:41:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__" +overloads_basic.py:39:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) +overloads_basic.py:39:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__"   "Literal['']" is not assignable to "slice[Any, Any, Any]" (reportArgumentType) -overloads_basic.py:67:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) -overloads_basic.py:79:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_basic.py:116:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_basic.py:132:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) -overloads_basic.py:140:9 - error: Overloaded implementation is not consistent with signature of overload 1 -  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" -    Parameter name mismatch: "x" versus "self" -    Parameter 1: type "int" is incompatible with type "Self@C" -      Type "int" is not assignable to type "Self@C" -    Extra parameter "x" (reportInconsistentOverload) -overloads_basic.py:140:9 - error: Overloaded implementation is not consistent with signature of overload 2 -  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" -    Parameter name mismatch: "x" versus "self" -    Parameter 1: type "str" is incompatible with type "Self@C" -      Type "str" is not assignable to type "Self@C" -    Extra parameter "x" (reportInconsistentOverload) -overloads_basic.py:145:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) -overloads_basic.py:153:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) """ conformance_automated = "Pass" errors_diff = """ diff --git a/conformance/results/pyright/overloads_invalid.toml b/conformance/results/pyright/overloads_invalid.toml new file mode 100644 index 00000000..fbe6cd20 --- /dev/null +++ b/conformance/results/pyright/overloads_invalid.toml @@ -0,0 +1,24 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_invalid.py:13:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_invalid.py:25:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_invalid.py:62:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_invalid.py:78:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_invalid.py:86:9 - error: Overloaded implementation is not consistent with signature of overload 1 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "int" is incompatible with type "Self@C" +      Type "int" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_invalid.py:86:9 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "str" is incompatible with type "Self@C" +      Type "str" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_invalid.py:91:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_invalid.py:99:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +""" diff --git a/conformance/results/pytype/overloads_basic.toml b/conformance/results/pytype/overloads_basic.toml index ce451452..d1e57904 100644 --- a/conformance/results/pytype/overloads_basic.toml +++ b/conformance/results/pytype/overloads_basic.toml @@ -1,65 +1,25 @@ conformant = "Partial" notes = """ -Does not reject a function with a single @overload signature. -Does not reject a function with @overload signature but no implementation. -Does not allow an overload with no implementation in a Protocol or an abstract base class. -Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`. -Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. """ output = """ -overloads_basic.py:35:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] +overloads_basic.py:33:20: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in __getitem__: bad return type [bad-return-type] return b"" \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_basic.py:41:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] +overloads_basic.py:39:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : unsupported operand type(s) for item retrieval: Bytes and str [unsupported-operands] b[""] # E: no matching overload \u001b[1m\u001b[31m~~~~~\u001b[39m\u001b[0m -overloads_basic.py:62:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] +overloads_basic.py:60:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in map: bad return type [bad-return-type] pass \u001b[1m\u001b[31m~~~~\u001b[39m\u001b[0m -overloads_basic.py:98:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] - - ... - \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m - -overloads_basic.py:110:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] - - ... - \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m - -overloads_basic.py:122:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] - - ... - \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m - -overloads_basic.py:133:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] - - ... - \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m - -overloads_basic.py:138:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] - - ... - \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m - """ conformance_automated = "Fail" errors_diff = """ -Lines 66, 67: Expected error (tag 'func1') -Lines 78, 79: Expected error (tag 'func2') -Lines 115, 116: Expected error (tag 'not_abstract') -Lines 130, 132, 137, 140: Expected error (tag 'func5') -Lines 143, 145, 150, 153: Expected error (tag 'func6') -Line 35: Unexpected errors ['overloads_basic.py:35:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] -Line 62: Unexpected errors ['overloads_basic.py:62:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] -Line 98: Unexpected errors ["overloads_basic.py:98:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] -Line 110: Unexpected errors ["overloads_basic.py:110:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] -Line 122: Unexpected errors ["overloads_basic.py:122:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] -Line 133: Unexpected errors ['overloads_basic.py:133:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 138: Unexpected errors ['overloads_basic.py:138:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 33: Unexpected errors ['overloads_basic.py:33:20: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in __getitem__: bad return type [bad-return-type]'] +Line 60: Unexpected errors ['overloads_basic.py:60:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in map: bad return type [bad-return-type]'] """ diff --git a/conformance/results/pytype/overloads_invalid.toml b/conformance/results/pytype/overloads_invalid.toml new file mode 100644 index 00000000..3aa6ebc7 --- /dev/null +++ b/conformance/results/pytype/overloads_invalid.toml @@ -0,0 +1,47 @@ +conformant = "Fail" +notes = """ +Does not reject a function with a single @overload signature. +Does not reject a function with @overload signature but no implementation. +Does not allow an overload with no implementation in a Protocol or an abstract base class. +Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. +""" +conformance_automated = "Fail" +errors_diff = """ +Lines 12, 13: Expected error (tag 'func1') +Lines 24, 25: Expected error (tag 'func2') +Lines 61, 62: Expected error (tag 'not_abstract') +Lines 76, 78, 83, 86: Expected error (tag 'func5') +Lines 89, 91, 96, 99: Expected error (tag 'func6') +Line 44: Unexpected errors ["overloads_invalid.py:44:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] +Line 56: Unexpected errors ["overloads_invalid.py:56:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 68: Unexpected errors ["overloads_invalid.py:68:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] +Line 79: Unexpected errors ['overloads_invalid.py:79:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 84: Unexpected errors ['overloads_invalid.py:84:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +""" +output = """ +overloads_invalid.py:44:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:56:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:68:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:79:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:84:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index f1ac7b3a..4eeecd3a 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.5 +test_duration = 29.9 diff --git a/conformance/results/results.html b/conformance/results/results.html index 74a7a382..be066bae 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
pyre 0.9.23
-
6.2sec
+
5.9sec
pytype 2024.10.11
-
30.5sec
+
29.9sec
@@ -667,10 +667,16 @@

Python Type System Conformance Test Results

Overloads
     overloads_basicPassPassPassPartial
     overloads_invalid
Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in an abstract base class.

Pass
Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Partial

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not exempt overloads from checking of return type in body, when also decorated with `@staticmethod`.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Exceptions diff --git a/conformance/tests/overloads_basic.py b/conformance/tests/overloads_basic.py index c3f63e82..ec0fd069 100644 --- a/conformance/tests/overloads_basic.py +++ b/conformance/tests/overloads_basic.py @@ -4,13 +4,11 @@ # Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload -from abc import ABC, abstractmethod from typing import ( Any, Callable, Iterable, Iterator, - Protocol, TypeVar, assert_type, overload, @@ -61,94 +59,3 @@ def map( def map(func: Any, iter1: Any, iter2: Any = ...) -> Any: pass - -# > At least two @overload-decorated definitions must be present. -@overload # E[func1] -def func1() -> None: # E[func1]: At least two overloads must be present - ... - - -def func1() -> None: - pass - - -# > The ``@overload``-decorated definitions must be followed by an overload -# > implementation, which does not include an ``@overload`` decorator. Type -# > checkers should report an error or warning if an implementation is missing. -@overload # E[func2] -def func2(x: int) -> int: # E[func2]: no implementation - ... - - -@overload -def func2(x: str) -> str: - ... - - -# > Overload definitions within stub files, protocols, and on abstract methods -# > within abstract base classes are exempt from this check. -class MyProto(Protocol): - @overload - def func3(self, x: int) -> int: - ... - - - @overload - def func3(self, x: str) -> str: - ... - -class MyAbstractBase(ABC): - @overload - @abstractmethod - def func4(self, x: int) -> int: - ... - - - @overload - @abstractmethod - def func4(self, x: str) -> str: - ... - - # A non-abstract method in an abstract base class still requires an - # implementation: - - @overload # E[not_abstract] - def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation - ... - - - @overload - def not_abstract(self, x: str) -> str: - ... - - -# > If one overload signature is decorated with ``@staticmethod`` or -# > ``@classmethod``, all overload signatures must be similarly decorated. The -# > implementation, if present, must also have a consistent decorator. Type -# > checkers should report an error if these conditions are not met. -class C: - @overload # E[func5] - @staticmethod - def func5(x: int) -> int: # E[func5] - ... - - @overload - @staticmethod - def func5(x: str) -> str: # E[func5] - ... - - def func5(self, x: int | str) -> int | str: # E[func5] - return 1 - - @overload # E[func6] - @classmethod - def func6(cls, x: int) -> int: # E[func6] - ... - - @overload - @classmethod - def func6(cls, x: str) -> str: # E[func6] - ... - - def func6(cls, x: int | str) -> int | str: # E[func6] - return 1 diff --git a/conformance/tests/overloads_invalid.py b/conformance/tests/overloads_invalid.py new file mode 100644 index 00000000..627db36f --- /dev/null +++ b/conformance/tests/overloads_invalid.py @@ -0,0 +1,100 @@ +""" +Tests errors on invalid `@typing.overload` usage. +""" + +from abc import ABC, abstractmethod +from typing import ( + Protocol, + overload, +) + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + + +def func1() -> None: + pass + + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +@overload # E[func2] +def func2(x: int) -> int: # E[func2]: no implementation + ... + + +@overload +def func2(x: str) -> str: + ... + + +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +class MyProto(Protocol): + @overload + def func3(self, x: int) -> int: + ... + + + @overload + def func3(self, x: str) -> str: + ... + +class MyAbstractBase(ABC): + @overload + @abstractmethod + def func4(self, x: int) -> int: + ... + + + @overload + @abstractmethod + def func4(self, x: str) -> str: + ... + + # A non-abstract method in an abstract base class still requires an + # implementation: + + @overload # E[not_abstract] + def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation + ... + + + @overload + def not_abstract(self, x: str) -> str: + ... + + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + @staticmethod + def func5(x: int) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str) -> str: # E[func5] + ... + + def func5(self, x: int | str) -> int | str: # E[func5] + return 1 + + @overload # E[func6] + @classmethod + def func6(cls, x: int) -> int: # E[func6] + ... + + @overload + @classmethod + def func6(cls, x: str) -> str: # E[func6] + ... + + def func6(cls, x: int | str) -> int | str: # E[func6] + return 1 From 8875a4aaa48466ac8d322a659212b22ed3686bf8 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 9 Jan 2025 18:08:22 -0800 Subject: [PATCH 17/38] add test for final with overload --- .../results/mypy/overloads_invalid.toml | 29 ++++-- .../results/pyre/overloads_invalid.toml | 31 +++--- .../results/pyright/overloads_invalid.toml | 30 ++++-- .../results/pytype/overloads_invalid.toml | 97 ++++++++++++++++--- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 8 +- conformance/tests/overloads_invalid.py | 58 ++++++++++- 7 files changed, 202 insertions(+), 53 deletions(-) diff --git a/conformance/results/mypy/overloads_invalid.toml b/conformance/results/mypy/overloads_invalid.toml index 0d351266..9d693146 100644 --- a/conformance/results/mypy/overloads_invalid.toml +++ b/conformance/results/mypy/overloads_invalid.toml @@ -4,15 +4,26 @@ notes = """ Does not allow an overload with no implementation in an abstract base class. """ errors_diff = """ -Line 47: Unexpected errors ['overloads_invalid.py:47: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] +Line 49: Unexpected errors ['overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] """ output = """ -overloads_invalid.py:12: error: Single overload definition, multiple required [misc] -overloads_invalid.py:24: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:47: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:61: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:76: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] -overloads_invalid.py:86: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] -overloads_invalid.py:86: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] -overloads_invalid.py:89: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_invalid.py:14: error: Single overload definition, multiple required [misc] +overloads_invalid.py:26: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:63: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_invalid.py:78: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_invalid.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_invalid.py:127: error: @final should be applied only to overload implementation [misc] +overloads_invalid.py:139: error: @final should be applied only to overload implementation [misc] +overloads_invalid.py:155: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_invalid.py:155: error: Signature of "final_method" incompatible with supertype "Base" [override] +overloads_invalid.py:155: note: Superclass: +overloads_invalid.py:155: note: @overload +overloads_invalid.py:155: note: def final_method(self, x: int) -> int +overloads_invalid.py:155: note: @overload +overloads_invalid.py:155: note: def final_method(self, x: str) -> str +overloads_invalid.py:155: note: Subclass: +overloads_invalid.py:155: note: def final_method(self, x: int | str) -> int | str """ diff --git a/conformance/results/pyre/overloads_invalid.toml b/conformance/results/pyre/overloads_invalid.toml index 13f38519..962dde08 100644 --- a/conformance/results/pyre/overloads_invalid.toml +++ b/conformance/results/pyre/overloads_invalid.toml @@ -2,21 +2,26 @@ conformant = "Partial" conformance_automated = "Fail" notes = """ Does not allow an overload with no implementation in a Protocol or an abstract base class. +Expects @final on all overloads and implementation, instead of implementation only. """ errors_diff = """ -Line 38: Unexpected errors ['overloads_invalid.py:38:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] -Line 49: Unexpected errors ['overloads_invalid.py:49:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +Lines 139, 141: Expected error (tag 'invalid_final_2') +Line 40: Unexpected errors ['overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] +Line 51: Unexpected errors ['overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +Line 124: Unexpected errors ['overloads_invalid.py:124:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] """ output = """ -overloads_invalid.py:13:0 Incompatible overload [43]: At least two overload signatures must be present. -overloads_invalid.py:25:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. -overloads_invalid.py:38:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. -overloads_invalid.py:49:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. -overloads_invalid.py:62:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. -overloads_invalid.py:78:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `78`. -overloads_invalid.py:83:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `83`. -overloads_invalid.py:86:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:91:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `91`. -overloads_invalid.py:96:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `96`. -overloads_invalid.py:99:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_invalid.py:27:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. +overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_invalid.py:64:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. +overloads_invalid.py:80:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `80`. +overloads_invalid.py:85:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `85`. +overloads_invalid.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:97:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `97`. +overloads_invalid.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:124:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:133:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:155:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. """ diff --git a/conformance/results/pyright/overloads_invalid.toml b/conformance/results/pyright/overloads_invalid.toml index fbe6cd20..48d24159 100644 --- a/conformance/results/pyright/overloads_invalid.toml +++ b/conformance/results/pyright/overloads_invalid.toml @@ -1,24 +1,34 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Allows @final on all overloads and implementation; should be implementation-only. +""" +conformance_automated = "Fail" errors_diff = """ +Lines 139, 141: Expected error (tag 'invalid_final_2') """ output = """ -overloads_invalid.py:13:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) -overloads_invalid.py:25:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_invalid.py:62:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_invalid.py:78:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) -overloads_invalid.py:86:9 - error: Overloaded implementation is not consistent with signature of overload 1 +overloads_invalid.py:15:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_invalid.py:27:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_invalid.py:64:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_invalid.py:80:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 1   Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int"     Parameter name mismatch: "x" versus "self"     Parameter 1: type "int" is incompatible with type "Self@C"       Type "int" is not assignable to type "Self@C"     Extra parameter "x" (reportInconsistentOverload) -overloads_invalid.py:86:9 - error: Overloaded implementation is not consistent with signature of overload 2 +overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 2   Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str"     Parameter name mismatch: "x" versus "self"     Parameter 1: type "str" is incompatible with type "Self@C"       Type "str" is not assignable to type "Self@C"     Extra parameter "x" (reportInconsistentOverload) -overloads_invalid.py:91:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) -overloads_invalid.py:99:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_invalid.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_invalid.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_invalid.py:129:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) +overloads_invalid.py:155:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_invalid.py:155:9 - error: Method "final_method" overrides class "Base" in an incompatible manner +  Return type mismatch: base method returns type "int", override returns type "int | str" +    Type "int | str" is not assignable to type "int" +      "str" is not assignable to "int" (reportIncompatibleMethodOverride) """ diff --git a/conformance/results/pytype/overloads_invalid.toml b/conformance/results/pytype/overloads_invalid.toml index 3aa6ebc7..6cf54404 100644 --- a/conformance/results/pytype/overloads_invalid.toml +++ b/conformance/results/pytype/overloads_invalid.toml @@ -4,42 +4,109 @@ Does not reject a function with a single @overload signature. Does not reject a function with @overload signature but no implementation. Does not allow an overload with no implementation in a Protocol or an abstract base class. Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. +Does not enforce any rules on location of @final decorator. """ conformance_automated = "Fail" errors_diff = """ -Lines 12, 13: Expected error (tag 'func1') -Lines 24, 25: Expected error (tag 'func2') -Lines 61, 62: Expected error (tag 'not_abstract') -Lines 76, 78, 83, 86: Expected error (tag 'func5') -Lines 89, 91, 96, 99: Expected error (tag 'func6') -Line 44: Unexpected errors ["overloads_invalid.py:44:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] -Line 56: Unexpected errors ["overloads_invalid.py:56:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] -Line 68: Unexpected errors ["overloads_invalid.py:68:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] -Line 79: Unexpected errors ['overloads_invalid.py:79:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 84: Unexpected errors ['overloads_invalid.py:84:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Lines 14, 15: Expected error (tag 'func1') +Lines 26, 27: Expected error (tag 'func2') +Lines 63, 64: Expected error (tag 'not_abstract') +Lines 78, 80, 85, 88: Expected error (tag 'func5') +Lines 91, 93, 97, 101: Expected error (tag 'func6') +Lines 127, 129, 133: Expected error (tag 'invalid_final') +Lines 139, 141: Expected error (tag 'invalid_final_2') +Line 6: Unexpected errors ['overloads_invalid.py:6:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.override not supported yet [not-supported-yet]'] +Line 46: Unexpected errors ["overloads_invalid.py:46:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] +Line 58: Unexpected errors ["overloads_invalid.py:58:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 70: Unexpected errors ["overloads_invalid.py:70:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] +Line 81: Unexpected errors ['overloads_invalid.py:81:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 86: Unexpected errors ['overloads_invalid.py:86:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 125: Unexpected errors ['overloads_invalid.py:125:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_invalid.py:125:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 137: Unexpected errors ['overloads_invalid.py:137:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_invalid.py:137:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] +Line 151: Unexpected errors ['overloads_invalid.py:151:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_invalid.py:151:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] +Line 156: Unexpected errors ['overloads_invalid.py:156:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] """ output = """ -overloads_invalid.py:44:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] +overloads_invalid.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] + +from typing import ( +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + final, +\u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m + Protocol, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + overload, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + override, +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m +) +\u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_invalid.py:46:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:56:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] +overloads_invalid.py:58:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:68:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] +overloads_invalid.py:70:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:79:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_invalid.py:81:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:84:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_invalid.py:86:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:125:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:125:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:137:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:137:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:151:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:151:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:154:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] + +class Child(Base): # E[override-final] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_invalid.py:156:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 4eeecd3a..6d5857e8 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 29.9 +test_duration = 30.4 diff --git a/conformance/results/results.html b/conformance/results/results.html index be066bae..93353265 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -168,7 +168,7 @@

Python Type System Conformance Test Results

5.9sec
pytype 2024.10.11
-
29.9sec
+
30.4sec
@@ -674,9 +674,9 @@

Python Type System Conformance Test Results

     overloads_invalid
Partial

Does not allow an overload with no implementation in an abstract base class.

Pass
Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Partial

Allows @final on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final on all overloads and implementation, instead of implementation only.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final decorator.

Exceptions diff --git a/conformance/tests/overloads_invalid.py b/conformance/tests/overloads_invalid.py index 627db36f..1439b301 100644 --- a/conformance/tests/overloads_invalid.py +++ b/conformance/tests/overloads_invalid.py @@ -4,8 +4,10 @@ from abc import ABC, abstractmethod from typing import ( + final, Protocol, overload, + override, ) # > At least two @overload-decorated definitions must be present. @@ -92,9 +94,63 @@ def func6(cls, x: int) -> int: # E[func6] ... @overload - @classmethod def func6(cls, x: str) -> str: # E[func6] ... + @classmethod def func6(cls, x: int | str) -> int | str: # E[func6] return 1 + + + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + @overload + def final_method(self, x: int) -> int: + ... + + @overload + def final_method(self, x: str) -> str: + ... + + @final + def final_method(self, x: int | str) -> int | str: + ... + + @overload # E[invalid_final] @final should be on implementation only + @final + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + + @overload + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + + def invalid_final(self, x: int | str) -> int | str: + ... + + @overload # E[invalid_final_2] @final should be on implementation only + @final + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] + ... + + @overload + @final + def invalid_final_2(self, x: str) -> str: + ... + + @final + def invalid_final_2(self, x: int | str) -> int | str: + ... + + +class Child(Base): # E[override-final] + def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method + ... From cb04dd674413fcfe46dbfc390c8cb687950fe5f5 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 9 Jan 2025 18:32:20 -0800 Subject: [PATCH 18/38] add tests for correct usage of override with an overload --- .../results/mypy/overloads_invalid.toml | 32 ++-- .../results/pyre/overloads_invalid.toml | 16 +- .../results/pyright/overloads_invalid.toml | 9 +- .../results/pytype/overloads_invalid.toml | 141 ++++++++++++++++-- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 4 +- conformance/tests/overloads_invalid.py | 53 +++++++ 7 files changed, 219 insertions(+), 38 deletions(-) diff --git a/conformance/results/mypy/overloads_invalid.toml b/conformance/results/mypy/overloads_invalid.toml index 9d693146..33b9360b 100644 --- a/conformance/results/mypy/overloads_invalid.toml +++ b/conformance/results/mypy/overloads_invalid.toml @@ -5,6 +5,7 @@ Does not allow an overload with no implementation in an abstract base class. """ errors_diff = """ Line 49: Unexpected errors ['overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] +Line 199: Unexpected errors ['overloads_invalid.py:199: error: Signature of "good_override" incompatible with supertype "Base" [override]'] """ output = """ overloads_invalid.py:14: error: Single overload definition, multiple required [misc] @@ -15,15 +16,24 @@ overloads_invalid.py:78: error: Overload does not consistently use the "@staticm overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] overloads_invalid.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] -overloads_invalid.py:127: error: @final should be applied only to overload implementation [misc] -overloads_invalid.py:139: error: @final should be applied only to overload implementation [misc] -overloads_invalid.py:155: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] -overloads_invalid.py:155: error: Signature of "final_method" incompatible with supertype "Base" [override] -overloads_invalid.py:155: note: Superclass: -overloads_invalid.py:155: note: @overload -overloads_invalid.py:155: note: def final_method(self, x: int) -> int -overloads_invalid.py:155: note: @overload -overloads_invalid.py:155: note: def final_method(self, x: str) -> str -overloads_invalid.py:155: note: Subclass: -overloads_invalid.py:155: note: def final_method(self, x: int | str) -> int | str +overloads_invalid.py:133: error: @final should be applied only to overload implementation [misc] +overloads_invalid.py:148: error: @final should be applied only to overload implementation [misc] +overloads_invalid.py:173: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_invalid.py:173: error: Signature of "final_method" incompatible with supertype "Base" [override] +overloads_invalid.py:173: note: Superclass: +overloads_invalid.py:173: note: @overload +overloads_invalid.py:173: note: def final_method(self, x: int) -> int +overloads_invalid.py:173: note: @overload +overloads_invalid.py:173: note: def final_method(self, x: str) -> str +overloads_invalid.py:173: note: Subclass: +overloads_invalid.py:173: note: def final_method(self, x: int | str) -> int | str +overloads_invalid.py:180: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +overloads_invalid.py:199: error: Signature of "good_override" incompatible with supertype "Base" [override] +overloads_invalid.py:199: note: Superclass: +overloads_invalid.py:199: note: def good_override(self, x: int | str) -> int | str +overloads_invalid.py:199: note: Subclass: +overloads_invalid.py:199: note: @overload +overloads_invalid.py:199: note: def good_override(self, x: int) -> int +overloads_invalid.py:199: note: @overload +overloads_invalid.py:199: note: def good_override(self, x: str) -> str """ diff --git a/conformance/results/pyre/overloads_invalid.toml b/conformance/results/pyre/overloads_invalid.toml index 962dde08..3fb24ed1 100644 --- a/conformance/results/pyre/overloads_invalid.toml +++ b/conformance/results/pyre/overloads_invalid.toml @@ -2,13 +2,14 @@ conformant = "Partial" conformance_automated = "Fail" notes = """ Does not allow an overload with no implementation in a Protocol or an abstract base class. -Expects @final on all overloads and implementation, instead of implementation only. +Expects @final/@override on all overloads and implementation, instead of implementation only. """ errors_diff = """ -Lines 139, 141: Expected error (tag 'invalid_final_2') +Lines 148, 150: Expected error (tag 'invalid_final_2') Line 40: Unexpected errors ['overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] Line 51: Unexpected errors ['overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] -Line 124: Unexpected errors ['overloads_invalid.py:124:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 128: Unexpected errors ['overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 208: Unexpected errors ['overloads_invalid.py:208:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] """ output = """ overloads_invalid.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. @@ -21,7 +22,10 @@ overloads_invalid.py:85:4 Incompatible overload [43]: The implementation of `C.f overloads_invalid.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). overloads_invalid.py:97:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `97`. overloads_invalid.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:124:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:133:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:155:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. +overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:173:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. +overloads_invalid.py:189:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:189:4 Invalid override [40]: `overloads_invalid.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. +overloads_invalid.py:208:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ diff --git a/conformance/results/pyright/overloads_invalid.toml b/conformance/results/pyright/overloads_invalid.toml index 48d24159..e2fa4ed8 100644 --- a/conformance/results/pyright/overloads_invalid.toml +++ b/conformance/results/pyright/overloads_invalid.toml @@ -4,7 +4,7 @@ Allows @final on all overloads and implementation; should be implementation-only """ conformance_automated = "Fail" errors_diff = """ -Lines 139, 141: Expected error (tag 'invalid_final_2') +Lines 148, 150: Expected error (tag 'invalid_final_2') """ output = """ overloads_invalid.py:15:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) @@ -25,10 +25,11 @@ overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent w     Extra parameter "x" (reportInconsistentOverload) overloads_invalid.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) overloads_invalid.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) -overloads_invalid.py:129:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) -overloads_invalid.py:155:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) -overloads_invalid.py:155:9 - error: Method "final_method" overrides class "Base" in an incompatible manner +overloads_invalid.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) +overloads_invalid.py:173:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_invalid.py:173:9 - error: Method "final_method" overrides class "Base" in an incompatible manner   Return type mismatch: base method returns type "int", override returns type "int | str"     Type "int | str" is not assignable to type "int"       "str" is not assignable to "int" (reportIncompatibleMethodOverride) +overloads_invalid.py:189:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) """ diff --git a/conformance/results/pytype/overloads_invalid.toml b/conformance/results/pytype/overloads_invalid.toml index 6cf54404..c6ecafd6 100644 --- a/conformance/results/pytype/overloads_invalid.toml +++ b/conformance/results/pytype/overloads_invalid.toml @@ -13,18 +13,21 @@ Lines 26, 27: Expected error (tag 'func2') Lines 63, 64: Expected error (tag 'not_abstract') Lines 78, 80, 85, 88: Expected error (tag 'func5') Lines 91, 93, 97, 101: Expected error (tag 'func6') -Lines 127, 129, 133: Expected error (tag 'invalid_final') -Lines 139, 141: Expected error (tag 'invalid_final_2') +Lines 133, 135, 139: Expected error (tag 'invalid_final') +Lines 148, 150: Expected error (tag 'invalid_final_2') Line 6: Unexpected errors ['overloads_invalid.py:6:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.override not supported yet [not-supported-yet]'] Line 46: Unexpected errors ["overloads_invalid.py:46:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] Line 58: Unexpected errors ["overloads_invalid.py:58:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] Line 70: Unexpected errors ["overloads_invalid.py:70:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] Line 81: Unexpected errors ['overloads_invalid.py:81:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] Line 86: Unexpected errors ['overloads_invalid.py:86:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 125: Unexpected errors ['overloads_invalid.py:125:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_invalid.py:125:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] -Line 137: Unexpected errors ['overloads_invalid.py:137:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_invalid.py:137:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] -Line 151: Unexpected errors ['overloads_invalid.py:151:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_invalid.py:151:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] -Line 156: Unexpected errors ['overloads_invalid.py:156:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 129: Unexpected errors ['overloads_invalid.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_invalid.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 143: Unexpected errors ['overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] +Line 160: Unexpected errors ['overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] +Line 165: Unexpected errors ['overloads_invalid.py:165:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 174: Unexpected errors ['overloads_invalid.py:174:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 190: Unexpected errors ['overloads_invalid.py:190:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_invalid.py:190:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] +Line 209: Unexpected errors ['overloads_invalid.py:209:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_invalid.py:209:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] """ output = """ overloads_invalid.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] @@ -67,46 +70,156 @@ overloads_invalid.py:86:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5 ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:125:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_invalid.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:125:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_invalid.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:137:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] +overloads_invalid.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:137:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] +overloads_invalid.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:151:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] +overloads_invalid.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:151:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] +overloads_invalid.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:154:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] +overloads_invalid.py:165:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:168:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] class Child(Base): # E[override-final] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # The correctly-decorated @final method `Base.final_method` should cause an +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # error if overridden in a child class: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_invalid.py:156:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is the right way to mark an overload as @override (decorate +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # implementation only), so the use of @override should cause an error +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # (because there's no `Base.bad_override` method): +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E[bad_override] marked as override but doesn't exist in base +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: int) -> int: # E[bad_override] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def bad_override(self, x: int | str) -> int | str: # E[bad_override] +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is also a correctly-decorated overloaded @override, which is +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # overriding a method that does exist in the base, so there should be no +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # error. We need both this test and the previous one, because in the +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # previous test, an incorrect error about the use of @override decorator +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # could appear on the same line as the expected error about overriding a +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # method that doesn't exist in base: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def good_override(self, x: int | str) -> int | str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_invalid.py:174:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:181:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] + + def bad_override(self, x: int) -> int: # E[bad_override] + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_invalid.py:190:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:190:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:209:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:209:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 6d5857e8..a7bacc6c 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.4 +test_duration = 30.6 diff --git a/conformance/results/results.html b/conformance/results/results.html index 93353265..6ca870b4 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -168,7 +168,7 @@

Python Type System Conformance Test Results

5.9sec
pytype 2024.10.11
-
30.4sec
+
30.6sec
@@ -675,7 +675,7 @@

Python Type System Conformance Test Results

     overloads_invalid
Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Allows @final on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final on all overloads and implementation, instead of implementation only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final/@override on all overloads and implementation, instead of implementation only.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final decorator.

diff --git a/conformance/tests/overloads_invalid.py b/conformance/tests/overloads_invalid.py index 1439b301..1af39d13 100644 --- a/conformance/tests/overloads_invalid.py +++ b/conformance/tests/overloads_invalid.py @@ -112,6 +112,10 @@ def func6(cls, x: int | str) -> int | str: # E[func6] # > ``@override`` decorator follows these rules, a type checker should treat the # > decorator as if it is present on all overloads. class Base: + + # This is a good definition of an overloaded final method (@final decorator + # on implementation only): + @overload def final_method(self, x: int) -> int: ... @@ -124,6 +128,8 @@ def final_method(self, x: str) -> str: def final_method(self, x: int | str) -> int | str: ... + # The @final decorator should not be on one of the overloads: + @overload # E[invalid_final] @final should be on implementation only @final def invalid_final(self, x: int) -> int: # E[invalid_final] @@ -136,6 +142,9 @@ def invalid_final(self, x: str) -> str: # E[invalid_final] def invalid_final(self, x: int | str) -> int | str: ... + # The @final decorator should not be on multiple overloads and + # implementation: + @overload # E[invalid_final_2] @final should be on implementation only @final def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] @@ -150,7 +159,51 @@ def invalid_final_2(self, x: str) -> str: def invalid_final_2(self, x: int | str) -> int | str: ... + # This method is just here for the @override test below: + + def good_override(self, x: int | str) -> int | str: + ... + class Child(Base): # E[override-final] + + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class: + def final_method(self, x: int | str) -> int | str: # E[override-final] can't override final method ... + + # This is the right way to mark an overload as @override (decorate + # implementation only), so the use of @override should cause an error + # (because there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + def bad_override(self, x: int) -> int: # E[bad_override] + ... + + @overload + def bad_override(self, x: str) -> str: + ... + + @override + def bad_override(self, x: int | str) -> int | str: # E[bad_override] + ... + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + def good_override(self, x: int) -> int: + ... + + @overload + def good_override(self, x: str) -> str: + ... + + @override + def good_override(self, x: int | str) -> int | str: + ... From 5eabe5366b140a90fc607c0950faed9d5532219f Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 9 Jan 2025 18:43:04 -0800 Subject: [PATCH 19/38] add test for wrong use of override with overload --- .../results/mypy/overloads_invalid.toml | 46 +++++++----- .../results/pyre/overloads_invalid.toml | 11 +-- .../results/pyright/overloads_invalid.toml | 9 ++- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_invalid.toml | 74 ++++++++++++++++--- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 8 +- conformance/tests/overloads_invalid.py | 23 +++++- 8 files changed, 129 insertions(+), 46 deletions(-) diff --git a/conformance/results/mypy/overloads_invalid.toml b/conformance/results/mypy/overloads_invalid.toml index 33b9360b..09598d02 100644 --- a/conformance/results/mypy/overloads_invalid.toml +++ b/conformance/results/mypy/overloads_invalid.toml @@ -5,7 +5,7 @@ Does not allow an overload with no implementation in an abstract base class. """ errors_diff = """ Line 49: Unexpected errors ['overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] -Line 199: Unexpected errors ['overloads_invalid.py:199: error: Signature of "good_override" incompatible with supertype "Base" [override]'] +Line 202: Unexpected errors ['overloads_invalid.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override]'] """ output = """ overloads_invalid.py:14: error: Single overload definition, multiple required [misc] @@ -18,22 +18,30 @@ overloads_invalid.py:88: error: Overloaded function implementation does not acce overloads_invalid.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] overloads_invalid.py:133: error: @final should be applied only to overload implementation [misc] overloads_invalid.py:148: error: @final should be applied only to overload implementation [misc] -overloads_invalid.py:173: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] -overloads_invalid.py:173: error: Signature of "final_method" incompatible with supertype "Base" [override] -overloads_invalid.py:173: note: Superclass: -overloads_invalid.py:173: note: @overload -overloads_invalid.py:173: note: def final_method(self, x: int) -> int -overloads_invalid.py:173: note: @overload -overloads_invalid.py:173: note: def final_method(self, x: str) -> str -overloads_invalid.py:173: note: Subclass: -overloads_invalid.py:173: note: def final_method(self, x: int | str) -> int | str -overloads_invalid.py:180: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] -overloads_invalid.py:199: error: Signature of "good_override" incompatible with supertype "Base" [override] -overloads_invalid.py:199: note: Superclass: -overloads_invalid.py:199: note: def good_override(self, x: int | str) -> int | str -overloads_invalid.py:199: note: Subclass: -overloads_invalid.py:199: note: @overload -overloads_invalid.py:199: note: def good_override(self, x: int) -> int -overloads_invalid.py:199: note: @overload -overloads_invalid.py:199: note: def good_override(self, x: str) -> str +overloads_invalid.py:176: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_invalid.py:176: error: Signature of "final_method" incompatible with supertype "Base" [override] +overloads_invalid.py:176: note: Superclass: +overloads_invalid.py:176: note: @overload +overloads_invalid.py:176: note: def final_method(self, x: int) -> int +overloads_invalid.py:176: note: @overload +overloads_invalid.py:176: note: def final_method(self, x: str) -> str +overloads_invalid.py:176: note: Subclass: +overloads_invalid.py:176: note: def final_method(self, x: int | str) -> int | str +overloads_invalid.py:183: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +overloads_invalid.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override] +overloads_invalid.py:202: note: Superclass: +overloads_invalid.py:202: note: def good_override(self, x: int | str) -> int | str +overloads_invalid.py:202: note: Subclass: +overloads_invalid.py:202: note: @overload +overloads_invalid.py:202: note: def good_override(self, x: int) -> int +overloads_invalid.py:202: note: @overload +overloads_invalid.py:202: note: def good_override(self, x: str) -> str +overloads_invalid.py:217: error: Signature of "to_override" incompatible with supertype "Base" [override] +overloads_invalid.py:217: note: Superclass: +overloads_invalid.py:217: note: def to_override(self, x: int | str) -> int | str +overloads_invalid.py:217: note: Subclass: +overloads_invalid.py:217: note: @overload +overloads_invalid.py:217: note: def to_override(self, x: int) -> int +overloads_invalid.py:217: note: @overload +overloads_invalid.py:217: note: def to_override(self, x: str) -> str """ diff --git a/conformance/results/pyre/overloads_invalid.toml b/conformance/results/pyre/overloads_invalid.toml index 3fb24ed1..faa561d7 100644 --- a/conformance/results/pyre/overloads_invalid.toml +++ b/conformance/results/pyre/overloads_invalid.toml @@ -5,11 +5,12 @@ Does not allow an overload with no implementation in a Protocol or an abstract b Expects @final/@override on all overloads and implementation, instead of implementation only. """ errors_diff = """ +Line 217: Expected 1 errors Lines 148, 150: Expected error (tag 'invalid_final_2') Line 40: Unexpected errors ['overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] Line 51: Unexpected errors ['overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] Line 128: Unexpected errors ['overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] -Line 208: Unexpected errors ['overloads_invalid.py:208:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +Line 211: Unexpected errors ['overloads_invalid.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] """ output = """ overloads_invalid.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. @@ -24,8 +25,8 @@ overloads_invalid.py:97:4 Incompatible overload [43]: The implementation of `C.f overloads_invalid.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). overloads_invalid.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:173:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. -overloads_invalid.py:189:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:189:4 Invalid override [40]: `overloads_invalid.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. -overloads_invalid.py:208:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:176:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. +overloads_invalid.py:192:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_invalid.py:192:4 Invalid override [40]: `overloads_invalid.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. +overloads_invalid.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). """ diff --git a/conformance/results/pyright/overloads_invalid.toml b/conformance/results/pyright/overloads_invalid.toml index e2fa4ed8..e650c117 100644 --- a/conformance/results/pyright/overloads_invalid.toml +++ b/conformance/results/pyright/overloads_invalid.toml @@ -1,9 +1,10 @@ conformant = "Partial" notes = """ -Allows @final on all overloads and implementation; should be implementation-only. +Allows @final/@override on all overloads and implementation; should be implementation-only. """ conformance_automated = "Fail" errors_diff = """ +Line 217: Expected 1 errors Lines 148, 150: Expected error (tag 'invalid_final_2') """ output = """ @@ -26,10 +27,10 @@ overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent w overloads_invalid.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) overloads_invalid.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) overloads_invalid.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) -overloads_invalid.py:173:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) -overloads_invalid.py:173:9 - error: Method "final_method" overrides class "Base" in an incompatible manner +overloads_invalid.py:176:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_invalid.py:176:9 - error: Method "final_method" overrides class "Base" in an incompatible manner   Return type mismatch: base method returns type "int", override returns type "int | str"     Type "int | str" is not assignable to type "int"       "str" is not assignable to "int" (reportIncompatibleMethodOverride) -overloads_invalid.py:189:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +overloads_invalid.py:192:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 1ae5a35b..eef86e05 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.5 +test_duration = 1.6 diff --git a/conformance/results/pytype/overloads_invalid.toml b/conformance/results/pytype/overloads_invalid.toml index c6ecafd6..bab69e93 100644 --- a/conformance/results/pytype/overloads_invalid.toml +++ b/conformance/results/pytype/overloads_invalid.toml @@ -4,10 +4,11 @@ Does not reject a function with a single @overload signature. Does not reject a function with @overload signature but no implementation. Does not allow an overload with no implementation in a Protocol or an abstract base class. Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`. -Does not enforce any rules on location of @final decorator. +Does not enforce any rules on location of @final or @override decorators. """ conformance_automated = "Fail" errors_diff = """ +Line 217: Expected 1 errors Lines 14, 15: Expected error (tag 'func1') Lines 26, 27: Expected error (tag 'func2') Lines 63, 64: Expected error (tag 'not_abstract') @@ -25,9 +26,11 @@ Line 129: Unexpected errors ['overloads_invalid.py:129:9: \\x1b[1m\\x1b[31merror Line 143: Unexpected errors ['overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] Line 160: Unexpected errors ['overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] Line 165: Unexpected errors ['overloads_invalid.py:165:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] -Line 174: Unexpected errors ['overloads_invalid.py:174:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] -Line 190: Unexpected errors ['overloads_invalid.py:190:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_invalid.py:190:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] -Line 209: Unexpected errors ['overloads_invalid.py:209:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_invalid.py:209:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 168: Unexpected errors ['overloads_invalid.py:168:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 177: Unexpected errors ['overloads_invalid.py:177:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 193: Unexpected errors ['overloads_invalid.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_invalid.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] +Line 212: Unexpected errors ['overloads_invalid.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_invalid.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 229: Unexpected errors ['overloads_invalid.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_invalid.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] """ output = """ overloads_invalid.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] @@ -105,7 +108,12 @@ overloads_invalid.py:165:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:168:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] +overloads_invalid.py:168:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:171:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] class Child(Base): # E[override-final] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m @@ -192,34 +200,78 @@ class Child(Base): # E[override-final] ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_invalid.py:174:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + # This is the wrong way to use @override with an overloaded method, and +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + # should emit an error: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload # E: @override should appear only on implementation +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: int) -> int: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @overload +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: str) -> str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +\u001b[1m\u001b[31m\u001b[39m\u001b[0m + @override +\u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + def to_override(self, x: int | str) -> int | str: +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + ... +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_invalid.py:177:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:181:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] +overloads_invalid.py:184:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] def bad_override(self, x: int) -> int: # E[bad_override] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_invalid.py:190:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_invalid.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] + + ... + \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m + +overloads_invalid.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:190:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_invalid.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:209:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_invalid.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:209:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_invalid.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index a7bacc6c..add97f41 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.6 +test_duration = 30.7 diff --git a/conformance/results/results.html b/conformance/results/results.html index 6ca870b4..2144c28b 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -162,13 +162,13 @@

Python Type System Conformance Test Results

1.7sec
pyright 1.1.391
-
1.5sec
+
1.6sec
pyre 0.9.23
5.9sec
pytype 2024.10.11
-
30.6sec
+
30.7sec
@@ -674,9 +674,9 @@

Python Type System Conformance Test Results

     overloads_invalid
Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Allows @final on all overloads and implementation; should be implementation-only.

Partial

Allows @final/@override on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final/@override on all overloads and implementation, instead of implementation only.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final decorator.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final or @override decorators.

Exceptions diff --git a/conformance/tests/overloads_invalid.py b/conformance/tests/overloads_invalid.py index 1af39d13..83ce3873 100644 --- a/conformance/tests/overloads_invalid.py +++ b/conformance/tests/overloads_invalid.py @@ -159,11 +159,14 @@ def invalid_final_2(self, x: str) -> str: def invalid_final_2(self, x: int | str) -> int | str: ... - # This method is just here for the @override test below: + # These methods are just here for the @override test below: def good_override(self, x: int | str) -> int | str: ... + def to_override(self, x: int | str) -> int | str: + ... + class Child(Base): # E[override-final] @@ -207,3 +210,21 @@ def good_override(self, x: str) -> str: @override def good_override(self, x: int | str) -> int | str: ... + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E: @override should appear only on implementation + @override + def to_override(self, x: int) -> int: + ... + + @overload + @override + def to_override(self, x: str) -> str: + ... + + @override + def to_override(self, x: int | str) -> int | str: + ... + From 484b03ca7a0df2b72953bbee1fe99eb06cb1ad38 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 14:09:13 -0800 Subject: [PATCH 20/38] rename overloads_invalid to overloads_definitions --- .../results/mypy/overloads_definitions.toml | 47 ++++++++++++ .../results/mypy/overloads_invalid.toml | 47 ------------ .../results/pyre/overloads_definitions.toml | 32 ++++++++ .../results/pyre/overloads_invalid.toml | 32 -------- .../pyright/overloads_definitions.toml | 36 +++++++++ .../results/pyright/overloads_invalid.toml | 36 --------- conformance/results/pyright/version.toml | 2 +- ...nvalid.toml => overloads_definitions.toml} | 76 +++++++++---------- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 6 +- conformance/src/test_groups.py | 3 +- ...ds_invalid.py => overloads_definitions.py} | 2 +- 12 files changed, 161 insertions(+), 160 deletions(-) create mode 100644 conformance/results/mypy/overloads_definitions.toml delete mode 100644 conformance/results/mypy/overloads_invalid.toml create mode 100644 conformance/results/pyre/overloads_definitions.toml delete mode 100644 conformance/results/pyre/overloads_invalid.toml create mode 100644 conformance/results/pyright/overloads_definitions.toml delete mode 100644 conformance/results/pyright/overloads_invalid.toml rename conformance/results/pytype/{overloads_invalid.toml => overloads_definitions.toml} (57%) rename conformance/tests/{overloads_invalid.py => overloads_definitions.py} (99%) diff --git a/conformance/results/mypy/overloads_definitions.toml b/conformance/results/mypy/overloads_definitions.toml new file mode 100644 index 00000000..6a76b7f3 --- /dev/null +++ b/conformance/results/mypy/overloads_definitions.toml @@ -0,0 +1,47 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in an abstract base class. +""" +errors_diff = """ +Line 49: Unexpected errors ['overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] +Line 202: Unexpected errors ['overloads_definitions.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override]'] +""" +output = """ +overloads_definitions.py:14: error: Single overload definition, multiple required [misc] +overloads_definitions.py:26: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:63: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:78: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] +overloads_definitions.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] +overloads_definitions.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions.py:133: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:148: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:176: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions.py:176: error: Signature of "final_method" incompatible with supertype "Base" [override] +overloads_definitions.py:176: note: Superclass: +overloads_definitions.py:176: note: @overload +overloads_definitions.py:176: note: def final_method(self, x: int) -> int +overloads_definitions.py:176: note: @overload +overloads_definitions.py:176: note: def final_method(self, x: str) -> str +overloads_definitions.py:176: note: Subclass: +overloads_definitions.py:176: note: def final_method(self, x: int | str) -> int | str +overloads_definitions.py:183: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +overloads_definitions.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override] +overloads_definitions.py:202: note: Superclass: +overloads_definitions.py:202: note: def good_override(self, x: int | str) -> int | str +overloads_definitions.py:202: note: Subclass: +overloads_definitions.py:202: note: @overload +overloads_definitions.py:202: note: def good_override(self, x: int) -> int +overloads_definitions.py:202: note: @overload +overloads_definitions.py:202: note: def good_override(self, x: str) -> str +overloads_definitions.py:217: error: Signature of "to_override" incompatible with supertype "Base" [override] +overloads_definitions.py:217: note: Superclass: +overloads_definitions.py:217: note: def to_override(self, x: int | str) -> int | str +overloads_definitions.py:217: note: Subclass: +overloads_definitions.py:217: note: @overload +overloads_definitions.py:217: note: def to_override(self, x: int) -> int +overloads_definitions.py:217: note: @overload +overloads_definitions.py:217: note: def to_override(self, x: str) -> str +""" diff --git a/conformance/results/mypy/overloads_invalid.toml b/conformance/results/mypy/overloads_invalid.toml deleted file mode 100644 index 09598d02..00000000 --- a/conformance/results/mypy/overloads_invalid.toml +++ /dev/null @@ -1,47 +0,0 @@ -conformant = "Partial" -conformance_automated = "Fail" -notes = """ -Does not allow an overload with no implementation in an abstract base class. -""" -errors_diff = """ -Line 49: Unexpected errors ['overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]'] -Line 202: Unexpected errors ['overloads_invalid.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override]'] -""" -output = """ -overloads_invalid.py:14: error: Single overload definition, multiple required [misc] -overloads_invalid.py:26: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:49: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:63: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] -overloads_invalid.py:78: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] -overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 1 [misc] -overloads_invalid.py:88: error: Overloaded function implementation does not accept all possible arguments of signature 2 [misc] -overloads_invalid.py:91: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] -overloads_invalid.py:133: error: @final should be applied only to overload implementation [misc] -overloads_invalid.py:148: error: @final should be applied only to overload implementation [misc] -overloads_invalid.py:176: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] -overloads_invalid.py:176: error: Signature of "final_method" incompatible with supertype "Base" [override] -overloads_invalid.py:176: note: Superclass: -overloads_invalid.py:176: note: @overload -overloads_invalid.py:176: note: def final_method(self, x: int) -> int -overloads_invalid.py:176: note: @overload -overloads_invalid.py:176: note: def final_method(self, x: str) -> str -overloads_invalid.py:176: note: Subclass: -overloads_invalid.py:176: note: def final_method(self, x: int | str) -> int | str -overloads_invalid.py:183: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] -overloads_invalid.py:202: error: Signature of "good_override" incompatible with supertype "Base" [override] -overloads_invalid.py:202: note: Superclass: -overloads_invalid.py:202: note: def good_override(self, x: int | str) -> int | str -overloads_invalid.py:202: note: Subclass: -overloads_invalid.py:202: note: @overload -overloads_invalid.py:202: note: def good_override(self, x: int) -> int -overloads_invalid.py:202: note: @overload -overloads_invalid.py:202: note: def good_override(self, x: str) -> str -overloads_invalid.py:217: error: Signature of "to_override" incompatible with supertype "Base" [override] -overloads_invalid.py:217: note: Superclass: -overloads_invalid.py:217: note: def to_override(self, x: int | str) -> int | str -overloads_invalid.py:217: note: Subclass: -overloads_invalid.py:217: note: @overload -overloads_invalid.py:217: note: def to_override(self, x: int) -> int -overloads_invalid.py:217: note: @overload -overloads_invalid.py:217: note: def to_override(self, x: str) -> str -""" diff --git a/conformance/results/pyre/overloads_definitions.toml b/conformance/results/pyre/overloads_definitions.toml new file mode 100644 index 00000000..b9aaf9e4 --- /dev/null +++ b/conformance/results/pyre/overloads_definitions.toml @@ -0,0 +1,32 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Does not allow an overload with no implementation in a Protocol or an abstract base class. +Expects @final/@override on all overloads and implementation, instead of implementation only. +""" +errors_diff = """ +Line 217: Expected 1 errors +Lines 148, 150: Expected error (tag 'invalid_final_2') +Line 40: Unexpected errors ['overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] +Line 51: Unexpected errors ['overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] +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).'] +Line 211: Unexpected errors ['overloads_definitions.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] +""" +output = """ +overloads_definitions.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. +overloads_definitions.py:27:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. +overloads_definitions.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. +overloads_definitions.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. +overloads_definitions.py:64:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. +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`. +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`. +overloads_definitions.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +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`. +overloads_definitions.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:176:4 Invalid override [40]: `overloads_definitions.Child.final_method` cannot override final method defined in `Base`. +overloads_definitions.py:192:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +overloads_definitions.py:192: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`. +overloads_definitions.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). +""" diff --git a/conformance/results/pyre/overloads_invalid.toml b/conformance/results/pyre/overloads_invalid.toml deleted file mode 100644 index faa561d7..00000000 --- a/conformance/results/pyre/overloads_invalid.toml +++ /dev/null @@ -1,32 +0,0 @@ -conformant = "Partial" -conformance_automated = "Fail" -notes = """ -Does not allow an overload with no implementation in a Protocol or an abstract base class. -Expects @final/@override on all overloads and implementation, instead of implementation only. -""" -errors_diff = """ -Line 217: Expected 1 errors -Lines 148, 150: Expected error (tag 'invalid_final_2') -Line 40: Unexpected errors ['overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation.'] -Line 51: Unexpected errors ['overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation.'] -Line 128: Unexpected errors ['overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] -Line 211: Unexpected errors ['overloads_invalid.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s).'] -""" -output = """ -overloads_invalid.py:15:0 Incompatible overload [43]: At least two overload signatures must be present. -overloads_invalid.py:27:0 Missing overload implementation [42]: Overloaded function `func2` must have an implementation. -overloads_invalid.py:40:4 Missing overload implementation [42]: Overloaded function `MyProto.func3` must have an implementation. -overloads_invalid.py:51:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.func4` must have an implementation. -overloads_invalid.py:64:4 Missing overload implementation [42]: Overloaded function `MyAbstractBase.not_abstract` must have an implementation. -overloads_invalid.py:80:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `80`. -overloads_invalid.py:85:4 Incompatible overload [43]: The implementation of `C.func5` does not accept all possible arguments of overload defined on line `85`. -overloads_invalid.py:88:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:97:4 Incompatible overload [43]: The implementation of `C.func6` does not accept all possible arguments of overload defined on line `97`. -overloads_invalid.py:97:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:128:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:139:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:176:4 Invalid override [40]: `overloads_invalid.Child.final_method` cannot override final method defined in `Base`. -overloads_invalid.py:192:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -overloads_invalid.py:192:4 Invalid override [40]: `overloads_invalid.Child.bad_override` is decorated with @override, but no method of the same name exists in superclasses of `Child`. -overloads_invalid.py:211:4 Incompatible overload [43]: This definition does not have the same decorators as the preceding overload(s). -""" diff --git a/conformance/results/pyright/overloads_definitions.toml b/conformance/results/pyright/overloads_definitions.toml new file mode 100644 index 00000000..252e6fb0 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions.toml @@ -0,0 +1,36 @@ +conformant = "Partial" +notes = """ +Allows @final/@override on all overloads and implementation; should be implementation-only. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 217: Expected 1 errors +Lines 148, 150: Expected error (tag 'invalid_final_2') +""" +output = """ +overloads_definitions.py:15:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions.py:27:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:64:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:80:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 1 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "int" is incompatible with type "Self@C" +      Type "int" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_definitions.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter name mismatch: "x" versus "self" +    Parameter 1: type "str" is incompatible with type "Self@C" +      Type "str" is not assignable to type "Self@C" +    Extra parameter "x" (reportInconsistentOverload) +overloads_definitions.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) +overloads_definitions.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) +overloads_definitions.py:176:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_definitions.py:176:9 - error: Method "final_method" overrides class "Base" in an incompatible manner +  Return type mismatch: base method returns type "int", override returns type "int | str" +    Type "int | str" is not assignable to type "int" +      "str" is not assignable to "int" (reportIncompatibleMethodOverride) +overloads_definitions.py:192:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +""" diff --git a/conformance/results/pyright/overloads_invalid.toml b/conformance/results/pyright/overloads_invalid.toml deleted file mode 100644 index e650c117..00000000 --- a/conformance/results/pyright/overloads_invalid.toml +++ /dev/null @@ -1,36 +0,0 @@ -conformant = "Partial" -notes = """ -Allows @final/@override on all overloads and implementation; should be implementation-only. -""" -conformance_automated = "Fail" -errors_diff = """ -Line 217: Expected 1 errors -Lines 148, 150: Expected error (tag 'invalid_final_2') -""" -output = """ -overloads_invalid.py:15:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) -overloads_invalid.py:27:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_invalid.py:64:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) -overloads_invalid.py:80:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) -overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 1 -  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: int) -> int" -    Parameter name mismatch: "x" versus "self" -    Parameter 1: type "int" is incompatible with type "Self@C" -      Type "int" is not assignable to type "Self@C" -    Extra parameter "x" (reportInconsistentOverload) -overloads_invalid.py:88:9 - error: Overloaded implementation is not consistent with signature of overload 2 -  Type "(self: Self@C, x: int | str) -> (int | str)" is not assignable to type "(x: str) -> str" -    Parameter name mismatch: "x" versus "self" -    Parameter 1: type "str" is incompatible with type "Self@C" -      Type "str" is not assignable to type "Self@C" -    Extra parameter "x" (reportInconsistentOverload) -overloads_invalid.py:93:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) -overloads_invalid.py:97:15 - warning: Instance methods should take a "self" parameter (reportSelfClsParameterName) -overloads_invalid.py:135:9 - error: Overload for "invalid_final" is marked @final but implementation is not (reportInconsistentOverload) -overloads_invalid.py:176:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) -overloads_invalid.py:176:9 - error: Method "final_method" overrides class "Base" in an incompatible manner -  Return type mismatch: base method returns type "int", override returns type "int | str" -    Type "int | str" is not assignable to type "int" -      "str" is not assignable to "int" (reportIncompatibleMethodOverride) -overloads_invalid.py:192:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) -""" diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index eef86e05..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.6 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_invalid.toml b/conformance/results/pytype/overloads_definitions.toml similarity index 57% rename from conformance/results/pytype/overloads_invalid.toml rename to conformance/results/pytype/overloads_definitions.toml index bab69e93..33ab8bd7 100644 --- a/conformance/results/pytype/overloads_invalid.toml +++ b/conformance/results/pytype/overloads_definitions.toml @@ -16,24 +16,24 @@ Lines 78, 80, 85, 88: Expected error (tag 'func5') Lines 91, 93, 97, 101: Expected error (tag 'func6') Lines 133, 135, 139: Expected error (tag 'invalid_final') Lines 148, 150: Expected error (tag 'invalid_final_2') -Line 6: Unexpected errors ['overloads_invalid.py:6:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.override not supported yet [not-supported-yet]'] -Line 46: Unexpected errors ["overloads_invalid.py:46:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] -Line 58: Unexpected errors ["overloads_invalid.py:58:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] -Line 70: Unexpected errors ["overloads_invalid.py:70:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] -Line 81: Unexpected errors ['overloads_invalid.py:81:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 86: Unexpected errors ['overloads_invalid.py:86:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] -Line 129: Unexpected errors ['overloads_invalid.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_invalid.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] -Line 143: Unexpected errors ['overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_invalid.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] -Line 160: Unexpected errors ['overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_invalid.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] -Line 165: Unexpected errors ['overloads_invalid.py:165:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] -Line 168: Unexpected errors ['overloads_invalid.py:168:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] -Line 177: Unexpected errors ['overloads_invalid.py:177:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] -Line 193: Unexpected errors ['overloads_invalid.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_invalid.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] -Line 212: Unexpected errors ['overloads_invalid.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_invalid.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] -Line 229: Unexpected errors ['overloads_invalid.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_invalid.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 6: Unexpected errors ['overloads_definitions.py:6:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : typing.override not supported yet [not-supported-yet]'] +Line 46: Unexpected errors ["overloads_definitions.py:46:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable]"] +Line 58: Unexpected errors ["overloads_definitions.py:58:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable]"] +Line 70: Unexpected errors ["overloads_definitions.py:70:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable]"] +Line 81: Unexpected errors ['overloads_definitions.py:81:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 86: Unexpected errors ['overloads_definitions.py:86:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in func5: bad return type [bad-return-type]'] +Line 129: Unexpected errors ['overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]', 'overloads_definitions.py:129:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 143: Unexpected errors ['overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]', 'overloads_definitions.py:143:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final: bad return type [bad-return-type]'] +Line 160: Unexpected errors ['overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]', 'overloads_definitions.py:160:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in invalid_final_2: bad return type [bad-return-type]'] +Line 165: Unexpected errors ['overloads_definitions.py:165:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 168: Unexpected errors ['overloads_definitions.py:168:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] +Line 177: Unexpected errors ['overloads_definitions.py:177:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in final_method: bad return type [bad-return-type]'] +Line 193: Unexpected errors ['overloads_definitions.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]', 'overloads_definitions.py:193:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in bad_override: bad return type [bad-return-type]'] +Line 212: Unexpected errors ['overloads_definitions.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]', 'overloads_definitions.py:212:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in good_override: bad return type [bad-return-type]'] +Line 229: Unexpected errors ['overloads_definitions.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]', 'overloads_definitions.py:229:9: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in to_override: bad return type [bad-return-type]'] """ output = """ -overloads_invalid.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] +overloads_definitions.py:6:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : typing.override not supported yet [not-supported-yet] from typing import ( \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m @@ -48,72 +48,72 @@ from typing import ( ) \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_invalid.py:46:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] +overloads_definitions.py:46:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func3: @typing.overload-decorated 'MyProto.func3' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:58:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] +overloads_definitions.py:58:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func4: @typing.overload-decorated 'MyAbstractBase.func4' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:70:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] +overloads_definitions.py:70:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in not_abstract: @typing.overload-decorated 'MyAbstractBase.not_abstract' object is not callable [not-callable] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:81:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_definitions.py:81:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:86:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] +overloads_definitions.py:86:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in func5: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_definitions.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_definitions.py:129:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] +overloads_definitions.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] +overloads_definitions.py:143:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] +overloads_definitions.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] +overloads_definitions.py:160:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in invalid_final_2: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:165:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:165:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:168:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:168:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:171:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] +overloads_definitions.py:171:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Class Child overrides final method final_method, defined in base class Base [final-error] class Child(Base): # E[override-final] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m @@ -234,44 +234,44 @@ class Child(Base): # E[override-final] ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_invalid.py:177:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] +overloads_definitions.py:177:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in final_method: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:184:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] +overloads_definitions.py:184:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in Child: Attribute 'bad_override' not found on any parent class [override-error] def bad_override(self, x: int) -> int: # E[bad_override] \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m ... \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_invalid.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_definitions.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] +overloads_definitions.py:193:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in bad_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] +overloads_definitions.py:212:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in good_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m -overloads_invalid.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] +overloads_definitions.py:229:9: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in to_override: bad return type [bad-return-type] ... \u001b[1m\u001b[31m~~~\u001b[39m\u001b[0m diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index add97f41..7dff3b18 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.7 +test_duration = 30.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index 2144c28b..be3f3a9b 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -162,13 +162,13 @@

Python Type System Conformance Test Results

1.7sec
pyright 1.1.391
-
1.6sec
+
1.5sec
pyre 0.9.23
5.9sec
pytype 2024.10.11
-
30.7sec
+
30.1sec
@@ -672,7 +672,7 @@

Python Type System Conformance Test Results

Pass Partial
     overloads_invalid
     overloads_definitions
Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Allows @final/@override on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final/@override on all overloads and implementation, instead of implementation only.

pyre 0.9.23
-
5.9sec
+
6.7sec
pytype 2024.10.11
-
30.1sec
+
30.0sec
@@ -673,11 +673,17 @@

Python Type System Conformance Test Results

Partial
     overloads_definitions
Partial

Does not allow an overload with no implementation in an abstract base class.

Partial

Does not allow an overload with no implementation in an abstract base class.

Allows @override to be on all overloads and implementation, instead of just implementation.

Partial

Allows @final/@override on all overloads and implementation; should be implementation-only.

Partial

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Expects @final/@override on all overloads and implementation, instead of implementation only.

Fail

Does not reject a function with a single @overload signature.

Does not reject a function with @overload signature but no implementation.

Does not allow an overload with no implementation in a Protocol or an abstract base class.

Does not error on overloads inconsistently decorated with `@staticmethod` or `@classmethod`.

Does not enforce any rules on location of @final or @override decorators.

     overloads_definitions_stub
Partial

Allows @override to appear in a stub file not on the first overload.

Partial

Does not enforce that @final/@override in a stub should be only on first overload.

Does not enforce @override when correctly used with an overloaded method in a stub file.

Partial

Expects @final and @override to be present on all overloads, not just first.

Fail

Does not enforce any of the specified rules regarding overload definitions.

Exceptions
pyre 0.9.23
-
6.7sec
+
6.6sec
pytype 2024.10.11
-
30.0sec
+
30.3sec
@@ -672,6 +672,12 @@

Python Type System Conformance Test Results

Pass Partial
     overloads_consistencyUnknownUnknownUnknownUnknown
     overloads_definitions
Partial

Does not allow an overload with no implementation in an abstract base class.

Allows @override to be on all overloads and implementation, instead of just implementation.

Partial

Allows @final/@override on all overloads and implementation; should be implementation-only.

pyre 0.9.23
-
6.6sec
+
6.5sec
pytype 2024.10.11
-
30.3sec
+
30.1sec
@@ -673,10 +673,10 @@

Python Type System Conformance Test Results

Partial
     overloads_consistencyUnknownUnknownUnknownUnknownPassPassPass
Fail

Doesn't appear to validate overload consistency at all.

     overloads_definitions
Partial

Does not allow an overload with no implementation in an abstract base class.

Allows @override to be on all overloads and implementation, instead of just implementation.

 
mypy 1.14.1
-
1.7sec
+
1.8sec
pyright 1.1.391
-
1.5sec
+
1.7sec
pyre 0.9.23
-
6.5sec
+
6.0sec
pytype 2024.10.11
-
30.1sec
+
31.1sec
diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py index b3a54eed..559fe114 100644 --- a/conformance/tests/overloads_consistency.py +++ b/conformance/tests/overloads_consistency.py @@ -2,7 +2,7 @@ Tests consistency of overloads with implementation. """ -from typing import Coroutine, overload +from typing import Callable, Coroutine, overload # > If an overload implementation is defined, type checkers should validate # > that it is consistent with all of its associated overload signatures. @@ -89,3 +89,29 @@ def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]: async def _wrapped(x: int | str) -> int | str: return 2 + +# Decorator transforms are applied before checking overload consistency: + +def _deco_1(f: Callable) -> Callable[[int], int]: + def wrapped(_x: int, /) -> int: + return 1 + return wrapped + +def _deco_2(f: Callable) -> Callable[[int | str], int | str]: + def wrapped(_x: int | str, /) -> int | str: + return 1 + return wrapped + +@overload +@_deco_1 +def decorated() -> None: + ... + +@overload +def decorated(x: str, /) -> str: + ... + +@_deco_2 +def decorated(y: bytes, z: bytes) -> bytes: + return b"" + From e0e0b8a9f4872c42fcca34d5d8bccf70ce03eb9b Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 17:07:50 -0800 Subject: [PATCH 25/38] add test for partially overlapping overloads --- .../results/mypy/overloads_overlap.toml | 7 ++++++ conformance/results/mypy/version.toml | 2 +- .../results/pyre/overloads_overlap.toml | 10 +++++++++ conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_overlap.toml | 7 ++++++ conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_overlap.toml | 16 ++++++++++++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 16 +++++++++----- conformance/tests/overloads_overlap.py | 22 +++++++++++++++++++ 10 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 conformance/results/mypy/overloads_overlap.toml create mode 100644 conformance/results/pyre/overloads_overlap.toml create mode 100644 conformance/results/pyright/overloads_overlap.toml create mode 100644 conformance/results/pytype/overloads_overlap.toml create mode 100644 conformance/tests/overloads_overlap.py diff --git a/conformance/results/mypy/overloads_overlap.toml b/conformance/results/mypy/overloads_overlap.toml new file mode 100644 index 00000000..9e1c158c --- /dev/null +++ b/conformance/results/mypy/overloads_overlap.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_overlap.py:14: error: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] +""" diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 9e6527ca..1012f096 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.8 +test_duration = 1.7 diff --git a/conformance/results/pyre/overloads_overlap.toml b/conformance/results/pyre/overloads_overlap.toml new file mode 100644 index 00000000..cde7d35c --- /dev/null +++ b/conformance/results/pyre/overloads_overlap.toml @@ -0,0 +1,10 @@ +conformant = "Fail" +notes = """ +Does not check for overlapping overloads with inconsistent return type. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 14: Expected 1 errors +""" +output = """ +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 2904f5c8..cd4e9e46 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.0 +test_duration = 6.7 diff --git a/conformance/results/pyright/overloads_overlap.toml b/conformance/results/pyright/overloads_overlap.toml new file mode 100644 index 00000000..d67e2d8d --- /dev/null +++ b/conformance/results/pyright/overloads_overlap.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_overlap.py:14:5 - error: Overload 1 for "is_one" overlaps overload 2 and returns an incompatible type (reportOverlappingOverload) +""" diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 324d1870..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.7 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_overlap.toml b/conformance/results/pytype/overloads_overlap.toml new file mode 100644 index 00000000..87b04425 --- /dev/null +++ b/conformance/results/pytype/overloads_overlap.toml @@ -0,0 +1,16 @@ +conformant = "Fail" +notes = """ +Does not check for overlapping overloads with inconsistent return type. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 14: Expected 1 errors +Line 22: Unexpected errors ['overloads_overlap.py:22:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in is_one: bad return type [bad-return-type]'] +""" +output = """ +overloads_overlap.py:22:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in is_one: bad return type [bad-return-type] + + return x == 1 + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 1da94b08..3b470ff7 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.1 +test_duration = 32.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index b3500ae3..d67cc9eb 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,16 +159,16 @@

Python Type System Conformance Test Results

- + @@ -690,6 +690,12 @@

Python Type System Conformance Test Results

+ + + + + + diff --git a/conformance/tests/overloads_overlap.py b/conformance/tests/overloads_overlap.py new file mode 100644 index 00000000..7fe57f31 --- /dev/null +++ b/conformance/tests/overloads_overlap.py @@ -0,0 +1,22 @@ +""" +Tests checks for overlapping overloads. +""" + +from typing import Literal, overload + +# > If two overloads can accept the same set of arguments, they are said +# > to "partially overlap". If two overloads partially overlap, the return type +# > of the former overload should be assignable to the return type of the +# > latter overload. If this condition doesn't hold, it is indicative of a +# > programming error and should be reported by type checkers. + +@overload +def is_one(x: Literal[1]) -> Literal[True]: # E: overlapping overloads, inconsistent return type + ... + +@overload +def is_one(x: int) -> Literal[False]: + ... + +def is_one(x: int) -> bool: + return x == 1 From cc748d3427a68b4a4a8e8a0e8b5117dd64399fb7 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 17:16:21 -0800 Subject: [PATCH 26/38] add test for fully overlapping overloads --- .../results/mypy/overloads_overlap.toml | 1 + .../results/pyre/overloads_overlap.toml | 5 +++-- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_overlap.toml | 1 + .../results/pytype/overloads_overlap.toml | 9 ++++++++- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 8 ++++---- conformance/tests/overloads_overlap.py | 18 ++++++++++++++++++ 8 files changed, 37 insertions(+), 9 deletions(-) diff --git a/conformance/results/mypy/overloads_overlap.toml b/conformance/results/mypy/overloads_overlap.toml index 9e1c158c..dd72a284 100644 --- a/conformance/results/mypy/overloads_overlap.toml +++ b/conformance/results/mypy/overloads_overlap.toml @@ -4,4 +4,5 @@ errors_diff = """ """ output = """ overloads_overlap.py:14: error: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] +overloads_overlap.py:36: error: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] """ diff --git a/conformance/results/pyre/overloads_overlap.toml b/conformance/results/pyre/overloads_overlap.toml index cde7d35c..24996c18 100644 --- a/conformance/results/pyre/overloads_overlap.toml +++ b/conformance/results/pyre/overloads_overlap.toml @@ -1,10 +1,11 @@ -conformant = "Fail" +conformant = "Partial" notes = """ -Does not check for overlapping overloads with inconsistent return type. +Does not check for partially overlapping overloads with inconsistent return type. """ conformance_automated = "Fail" errors_diff = """ Line 14: Expected 1 errors """ output = """ +overloads_overlap.py:36:0 Incompatible overload [43]: The overloaded function `full_overlap` on line 36 will never be matched. The signature `(x: bool) -> bool` is the same or broader. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cd4e9e46..cda658b7 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.7 +test_duration = 6.6 diff --git a/conformance/results/pyright/overloads_overlap.toml b/conformance/results/pyright/overloads_overlap.toml index d67e2d8d..8527f525 100644 --- a/conformance/results/pyright/overloads_overlap.toml +++ b/conformance/results/pyright/overloads_overlap.toml @@ -4,4 +4,5 @@ errors_diff = """ """ output = """ overloads_overlap.py:14:5 - error: Overload 1 for "is_one" overlaps overload 2 and returns an incompatible type (reportOverlappingOverload) +overloads_overlap.py:36:5 - error: Overload 2 for "full_overlap" will never be used because its parameters overlap overload 1 (reportOverlappingOverload) """ diff --git a/conformance/results/pytype/overloads_overlap.toml b/conformance/results/pytype/overloads_overlap.toml index 87b04425..598427b7 100644 --- a/conformance/results/pytype/overloads_overlap.toml +++ b/conformance/results/pytype/overloads_overlap.toml @@ -1,11 +1,13 @@ conformant = "Fail" notes = """ -Does not check for overlapping overloads with inconsistent return type. +Does not check for partially or fully overlapping overloads. """ conformance_automated = "Fail" errors_diff = """ Line 14: Expected 1 errors +Line 36: Expected 1 errors Line 22: Unexpected errors ['overloads_overlap.py:22:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in is_one: bad return type [bad-return-type]'] +Line 40: Unexpected errors ['overloads_overlap.py:40:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in full_overlap: bad return type [bad-return-type]'] """ output = """ overloads_overlap.py:22:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in is_one: bad return type [bad-return-type] @@ -13,4 +15,9 @@ overloads_overlap.py:22:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in is_on return x == 1 \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_overlap.py:40:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in full_overlap: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 3b470ff7..7dff3b18 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 32.1 +test_duration = 30.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index d67cc9eb..71692ca0 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
- - + + + + + + + + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py new file mode 100644 index 00000000..2f69a4a8 --- /dev/null +++ b/conformance/tests/overloads_evaluation.py @@ -0,0 +1,44 @@ +""" +Tests for evaluation of calls to overloaded functions. +""" + +from typing import assert_type, overload + + +# > Step 1: Examine the argument list to determine the number of +# > positional and keyword arguments. Use this information to eliminate any +# > overload candidates that are not plausible based on their +# > input signatures. + +# > - If no candidate overloads remain, generate an error and stop. + +@overload +def num_args(x: int, y: str) -> int: + ... + +@overload +def num_args(x: str) -> str: + ... + +def num_args(x: int | str, y: str = "") -> int | str: + return 1 + +num_args() # E: no matching overload + + +# > - If only one candidate overload remains, it is the winning match. Evaluate +# > it as if it were a non-overloaded function call and stop. + +ret1 = num_args(1, "") +assert_type(ret1, int) + +ret2 = num_args(1, 1) # E: Literal[1] not assignable to str +assert_type(ret2, int) + +ret3 = num_args("") +assert_type(ret3, str) + +ret4 = num_args(1) # E: Literal[1] not assignable to str +assert_type(ret4, str) + + From 02f0652484b77e6fb5e5f7c877d2dfecf9db3f7e Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 18:46:54 -0800 Subject: [PATCH 28/38] add tests for steps 2 and 3 of overload evaluation --- .../results/mypy/overloads_evaluation.toml | 32 +++++----- .../results/pyre/overloads_evaluation.toml | 8 ++- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 13 +++- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_evaluation.toml | 40 ++++++++---- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 8 +-- conformance/tests/overloads_evaluation.py | 62 ++++++++++++++++--- 9 files changed, 118 insertions(+), 51 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index ed5346a5..1d3399d2 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -4,22 +4,24 @@ Does not pick a winning overload based on arity, prior to considering argument t """ conformance_automated = "Fail" errors_diff = """ -Line 36: Unexpected errors ['overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type]'] -Line 42: Unexpected errors ['overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type]'] +Line 35: Unexpected errors ['overloads_evaluation.py:35: error: Expression is of type "Any", not "int" [assert-type]'] +Line 41: Unexpected errors ['overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type]'] """ output = """ -overloads_evaluation.py:26: error: All overload variants of "num_args" require at least one argument [call-overload] +overloads_evaluation.py:26: error: All overload variants of "example1" require at least one argument [call-overload] overloads_evaluation.py:26: note: Possible overload variants: -overloads_evaluation.py:26: note: def num_args(x: int, y: str) -> int -overloads_evaluation.py:26: note: def num_args(x: str) -> str -overloads_evaluation.py:35: error: No overload variant of "num_args" matches argument types "int", "int" [call-overload] -overloads_evaluation.py:35: note: Possible overload variants: -overloads_evaluation.py:35: note: def num_args(x: int, y: str) -> int -overloads_evaluation.py:35: note: def num_args(x: str) -> str -overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type] -overloads_evaluation.py:41: error: No overload variant of "num_args" matches argument type "int" [call-overload] -overloads_evaluation.py:41: note: Possible overload variants: -overloads_evaluation.py:41: note: def num_args(x: int, y: str) -> int -overloads_evaluation.py:41: note: def num_args(x: str) -> str -overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type] +overloads_evaluation.py:26: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:26: note: def example1(x: str) -> str +overloads_evaluation.py:34: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:34: note: Possible overload variants: +overloads_evaluation.py:34: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:34: note: def example1(x: str) -> str +overloads_evaluation.py:35: error: Expression is of type "Any", not "int" [assert-type] +overloads_evaluation.py:40: error: No overload variant of "example1" matches argument type "int" [call-overload] +overloads_evaluation.py:40: note: Possible overload variants: +overloads_evaluation.py:40: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:40: note: def example1(x: str) -> str +overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type] +overloads_evaluation.py:85: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:85: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] """ diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index 311020b4..a21200ce 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -3,7 +3,9 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ -overloads_evaluation.py:26:0 Missing argument [20]: Call `num_args` expects argument `x`. -overloads_evaluation.py:35:19 Incompatible parameter type [6]: In call `num_args`, for 2nd positional argument, expected `str` but got `int`. -overloads_evaluation.py:41:16 Incompatible parameter type [6]: In call `num_args`, for 1st positional argument, expected `str` but got `int`. +overloads_evaluation.py:26:0 Missing argument [20]: Call `example1` expects argument `x`. +overloads_evaluation.py:34:19 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. +overloads_evaluation.py:40:16 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. +overloads_evaluation.py:85:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. +overloads_evaluation.py:85:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cd4e9e46..439cd1ec 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.7 +test_duration = 6.1 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 9ef2de0b..66b302a6 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -3,10 +3,17 @@ conformance_automated = "Pass" errors_diff = """ """ output = """ -overloads_evaluation.py:26:1 - error: No overloads for "num_args" match the provided arguments +overloads_evaluation.py:26:1 - error: No overloads for "example1" match the provided arguments   Argument types: () (reportCallIssue) -overloads_evaluation.py:35:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "num_args" +overloads_evaluation.py:34:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:41:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "num_args" +overloads_evaluation.py:40:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:85:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) +overloads_evaluation.py:85:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:85:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 1ae5a35b..eef86e05 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.5 +test_duration = 1.6 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 5ae1b37d..6db642ed 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -1,44 +1,58 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. +Does not perform argument expansion on unions and union return types of all matching overloads. """ conformance_automated = "Fail" errors_diff = """ -Line 24: Unexpected errors ['overloads_evaluation.py:24:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in num_args: bad return type [bad-return-type]'] -Line 36: Unexpected errors ['overloads_evaluation.py:36:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] -Line 42: Unexpected errors ['overloads_evaluation.py:42:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 85: Expected 1 errors +Line 22: Unexpected errors ['overloads_evaluation.py:22:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] +Line 35: Unexpected errors ['overloads_evaluation.py:35:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 41: Unexpected errors ['overloads_evaluation.py:41:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 59: Unexpected errors ['overloads_evaluation.py:59:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] +Line 78: Unexpected errors ['overloads_evaluation.py:78:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: str [assert-type]'] """ output = """ -overloads_evaluation.py:24:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in num_args: bad return type [bad-return-type] +overloads_evaluation.py:22:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m Called from (traceback): - line 38, in current file -overloads_evaluation.py:26:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function num_args [missing-parameter] + line 37, in current file +overloads_evaluation.py:26:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] -num_args() # E: no matching overload +example1() # E: no matching overload \u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:35:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function num_args was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:34:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] -ret2 = num_args(1, 1) # E: Literal[1] not assignable to str +ret2 = example1(1, 1) # E: Literal[1] not assignable to str \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:36:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] +overloads_evaluation.py:35:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] assert_type(ret2, int) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:41:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function num_args was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:40:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] -ret4 = num_args(1) # E: Literal[1] not assignable to str +ret4 = example1(1) # E: Literal[1] not assignable to str \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:42:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] +overloads_evaluation.py:41:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] assert_type(ret4, str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_evaluation.py:59:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:78:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: str [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index add97f41..13554aa5 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.7 +test_duration = 31.5 diff --git a/conformance/results/results.html b/conformance/results/results.html index 414eee34..be317fb5 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -162,13 +162,13 @@

Python Type System Conformance Test Results

1.7sec
- + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 2f69a4a8..b33f6ded 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -10,35 +10,77 @@ # > overload candidates that are not plausible based on their # > input signatures. -# > - If no candidate overloads remain, generate an error and stop. - @overload -def num_args(x: int, y: str) -> int: +def example1(x: int, y: str) -> int: ... @overload -def num_args(x: str) -> str: +def example1(x: str) -> str: ... -def num_args(x: int | str, y: str = "") -> int | str: +def example1(x: int | str, y: str = "") -> int | str: return 1 -num_args() # E: no matching overload +# > - If no candidate overloads remain, generate an error and stop. +example1() # E: no matching overload # > - If only one candidate overload remains, it is the winning match. Evaluate # > it as if it were a non-overloaded function call and stop. -ret1 = num_args(1, "") +ret1 = example1(1, "") assert_type(ret1, int) -ret2 = num_args(1, 1) # E: Literal[1] not assignable to str +ret2 = example1(1, 1) # E: Literal[1] not assignable to str assert_type(ret2, int) -ret3 = num_args("") +ret3 = example1("") assert_type(ret3, str) -ret4 = num_args(1) # E: Literal[1] not assignable to str +ret4 = example1(1) # E: Literal[1] not assignable to str assert_type(ret4, str) +# > Step 2: Evaluate each remaining overload as a regular (non-overloaded) +# > call to determine whether it is compatible with the supplied +# > argument list. Unlike step 1, this step considers the types of the parameters +# > and arguments. During this step, do not generate any user-visible errors. +# > Simply record which of the overloads result in evaluation errors. + +@overload +def example2(x: int, y: str, z: int) -> str: + ... + +@overload +def example2(x: int, y: int, z: int) -> int: + ... + +def example2(x: int, y: int | str, z: int) -> int | str: + return 1 + +# > - If only one overload evaluates without error, it is the winning match. +# > Evaluate it as if it were a non-overloaded function call and stop. + +ret5 = example2(1, 2, 3) +assert_type(ret5, int) + +# > Step 3: If step 2 produces errors for all overloads, perform +# > "argument type expansion". Union types can be expanded +# > into their constituent subtypes. For example, the type ``int | str`` can +# > be expanded into ``int`` and ``str``. + +# > - If all argument lists evaluate successfully, combine their +# > respective return types by union to determine the final return type +# > for the call, and stop. + +def _(v: int | str) -> None: + ret1 = example2(1, v, 1) + assert_type(ret1, int | str) + +# > - If argument expansion has been applied to all arguments and one or +# > more of the expanded argument lists cannot be evaluated successfully, +# > generate an error and stop. + +def _(v: int | str) -> None: + example2(v, v, 1) # E: no overload matches (str, ..., ...) + From f5bee93ad3df790da6cd2d5cba619d1b1e0b4405 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 18:56:02 -0800 Subject: [PATCH 29/38] add tests for bool expansion (no checker does this) --- .../results/mypy/overloads_evaluation.toml | 8 ++++++ .../results/pyre/overloads_evaluation.toml | 14 ++++++++--- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 13 ++++++++-- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_evaluation.toml | 25 ++++++++++++++++++- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 14 +++++------ conformance/tests/overloads_evaluation.py | 19 +++++++++++++- 9 files changed, 81 insertions(+), 18 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 1d3399d2..a96a2d37 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -1,11 +1,14 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. +Does not expand boolean arguments to Literal[True] and Literal[False]. """ conformance_automated = "Fail" errors_diff = """ Line 35: Unexpected errors ['overloads_evaluation.py:35: error: Expression is of type "Any", not "int" [assert-type]'] Line 41: Unexpected errors ['overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type]'] +Line 102: Unexpected errors ['overloads_evaluation.py:102: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] """ output = """ overloads_evaluation.py:26: error: All overload variants of "example1" require at least one argument [call-overload] @@ -24,4 +27,9 @@ overloads_evaluation.py:40: note: def example1(x: str) -> str overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type] overloads_evaluation.py:85: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] overloads_evaluation.py:85: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:102: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] +overloads_evaluation.py:102: note: Possible overload variants: +overloads_evaluation.py:102: note: def expand_bool(x: Literal[False]) -> Literal[0] +overloads_evaluation.py:102: note: def expand_bool(x: Literal[True]) -> Literal[1] +overloads_evaluation.py:103: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] """ diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index a21200ce..e2631719 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -1,11 +1,17 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +""" +conformance_automated = "Fail" errors_diff = """ +Line 85: Expected 1 errors +Line 102: Unexpected errors ['overloads_evaluation.py:102:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] """ output = """ overloads_evaluation.py:26:0 Missing argument [20]: Call `example1` expects argument `x`. overloads_evaluation.py:34:19 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. overloads_evaluation.py:40:16 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. -overloads_evaluation.py:85:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. -overloads_evaluation.py:85:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:102:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. +overloads_evaluation.py:103:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 439cd1ec..cd4e9e46 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.1 +test_duration = 6.7 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 66b302a6..4e2ea03c 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -1,6 +1,11 @@ -conformant = "Pass" -conformance_automated = "Pass" +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +""" +conformance_automated = "Fail" errors_diff = """ +Line 102: Unexpected errors ['overloads_evaluation.py:102:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:102:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:26:1 - error: No overloads for "example1" match the provided arguments @@ -16,4 +21,8 @@ overloads_evaluation.py:85:14 - error: Argument of type "int | str" cannot be as overloads_evaluation.py:85:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:102:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) +overloads_evaluation.py:102:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool" +  "bool" is not assignable to type "Literal[True]" (reportArgumentType) +overloads_evaluation.py:103:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index eef86e05..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.6 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 6db642ed..b1d6ad88 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -1,7 +1,7 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. -Does not perform argument expansion on unions and union return types of all matching overloads. +Does not perform argument expansion and union return types of all matching overloads. """ conformance_automated = "Fail" errors_diff = """ @@ -11,6 +11,9 @@ Line 35: Unexpected errors ['overloads_evaluation.py:35:1: \\x1b[1m\\x1b[31merro Line 41: Unexpected errors ['overloads_evaluation.py:41:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] Line 59: Unexpected errors ['overloads_evaluation.py:59:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] Line 78: Unexpected errors ['overloads_evaluation.py:78:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: str [assert-type]'] +Line 99: Unexpected errors ['overloads_evaluation.py:99:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:99:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] +Line 102: Unexpected errors ['overloads_evaluation.py:102:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: Any [assert-type]'] """ output = """ overloads_evaluation.py:22:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] @@ -55,4 +58,24 @@ overloads_evaluation.py:78:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_evaluation.py:99:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] + + return int(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:99:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] + + return int(x) + \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:102:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: Function expand_bool was called with the wrong arguments [wrong-arg-types] + + ret1 = expand_bool(v) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: Any [assert-type] + + assert_type(ret1, Literal[0, 1]) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 13554aa5..ab86ac89 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.5 +test_duration = 31.2 diff --git a/conformance/results/results.html b/conformance/results/results.html index be317fb5..2ed4ed13 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -162,13 +162,13 @@

Python Type System Conformance Test Results

1.7sec
- - - - + + + + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index b33f6ded..14a32519 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -2,7 +2,7 @@ Tests for evaluation of calls to overloaded functions. """ -from typing import assert_type, overload +from typing import assert_type, Literal, overload # > Step 1: Examine the argument list to determine the number of @@ -84,3 +84,20 @@ def _(v: int | str) -> None: def _(v: int | str) -> None: example2(v, v, 1) # E: no overload matches (str, ..., ...) + +# > 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + +@overload +def expand_bool(x: Literal[False]) -> Literal[0]: + ... + +@overload +def expand_bool(x: Literal[True]) -> Literal[1]: + ... + +def expand_bool(x: bool) -> int: + return int(x) + +def _(v: bool) -> None: + ret1 = expand_bool(v) + assert_type(ret1, Literal[0, 1]) From 169fa584234e83b1d39314db4987d6fd467d1e2d Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 19:10:40 -0800 Subject: [PATCH 30/38] add tests for enum and tuple expansion --- .../results/mypy/overloads_evaluation.toml | 63 +++++++++------ .../results/pyre/overloads_evaluation.toml | 31 ++++++-- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 37 ++++++--- .../results/pytype/overloads_evaluation.toml | 76 +++++++++++++------ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 +-- conformance/tests/overloads_evaluation.py | 52 ++++++++++++- 8 files changed, 197 insertions(+), 78 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index a96a2d37..2b911de2 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -2,34 +2,47 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" errors_diff = """ -Line 35: Unexpected errors ['overloads_evaluation.py:35: error: Expression is of type "Any", not "int" [assert-type]'] -Line 41: Unexpected errors ['overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type]'] -Line 102: Unexpected errors ['overloads_evaluation.py:102: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] -Line 103: Unexpected errors ['overloads_evaluation.py:103: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 36: Unexpected errors ['overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type]'] +Line 42: Unexpected errors ['overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] +Line 104: Unexpected errors ['overloads_evaluation.py:104: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 126: Unexpected errors ['overloads_evaluation.py:126: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] +Line 127: Unexpected errors ['overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 147: Unexpected errors ['overloads_evaluation.py:147: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] +Line 148: Unexpected errors ['overloads_evaluation.py:148: error: Expression is of type "int", not "int | str" [assert-type]'] """ output = """ -overloads_evaluation.py:26: error: All overload variants of "example1" require at least one argument [call-overload] -overloads_evaluation.py:26: note: Possible overload variants: -overloads_evaluation.py:26: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:26: note: def example1(x: str) -> str -overloads_evaluation.py:34: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] -overloads_evaluation.py:34: note: Possible overload variants: -overloads_evaluation.py:34: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:34: note: def example1(x: str) -> str -overloads_evaluation.py:35: error: Expression is of type "Any", not "int" [assert-type] -overloads_evaluation.py:40: error: No overload variant of "example1" matches argument type "int" [call-overload] -overloads_evaluation.py:40: note: Possible overload variants: -overloads_evaluation.py:40: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:40: note: def example1(x: str) -> str -overloads_evaluation.py:41: error: Expression is of type "Any", not "str" [assert-type] -overloads_evaluation.py:85: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] -overloads_evaluation.py:85: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] -overloads_evaluation.py:102: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] -overloads_evaluation.py:102: note: Possible overload variants: -overloads_evaluation.py:102: note: def expand_bool(x: Literal[False]) -> Literal[0] -overloads_evaluation.py:102: note: def expand_bool(x: Literal[True]) -> Literal[1] -overloads_evaluation.py:103: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:27: error: All overload variants of "example1" require at least one argument [call-overload] +overloads_evaluation.py:27: note: Possible overload variants: +overloads_evaluation.py:27: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:27: note: def example1(x: str) -> str +overloads_evaluation.py:35: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:35: note: Possible overload variants: +overloads_evaluation.py:35: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:35: note: def example1(x: str) -> str +overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type] +overloads_evaluation.py:41: error: No overload variant of "example1" matches argument type "int" [call-overload] +overloads_evaluation.py:41: note: Possible overload variants: +overloads_evaluation.py:41: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:41: note: def example1(x: str) -> str +overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type] +overloads_evaluation.py:86: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:86: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:103: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] +overloads_evaluation.py:103: note: Possible overload variants: +overloads_evaluation.py:103: note: def expand_bool(x: Literal[False]) -> Literal[0] +overloads_evaluation.py:103: note: def expand_bool(x: Literal[True]) -> Literal[1] +overloads_evaluation.py:104: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:126: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload] +overloads_evaluation.py:126: note: Possible overload variants: +overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] +overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] +overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:147: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] +overloads_evaluation.py:148: error: Expression is of type "int", not "int | str" [assert-type] """ diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index e2631719..49bfb38c 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -1,17 +1,32 @@ conformant = "Partial" notes = """ Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" errors_diff = """ -Line 85: Expected 1 errors -Line 102: Unexpected errors ['overloads_evaluation.py:102:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] -Line 103: Unexpected errors ['overloads_evaluation.py:103:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 78: Unexpected errors ['overloads_evaluation.py:78:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`.'] +Line 79: Unexpected errors ['overloads_evaluation.py:79:4 Assert type [70]: Expected `Union[int, str]` but got `str`.'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] +Line 104: Unexpected errors ['overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 126: Unexpected errors ['overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`.'] +Line 127: Unexpected errors ['overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 147: Unexpected errors ['overloads_evaluation.py:147:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] """ output = """ -overloads_evaluation.py:26:0 Missing argument [20]: Call `example1` expects argument `x`. -overloads_evaluation.py:34:19 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. -overloads_evaluation.py:40:16 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. -overloads_evaluation.py:102:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. -overloads_evaluation.py:103:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:27:0 Missing argument [20]: Call `example1` expects argument `x`. +overloads_evaluation.py:35:19 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. +overloads_evaluation.py:41:16 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. +overloads_evaluation.py:78:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:79:4 Assert type [70]: Expected `Union[int, str]` but got `str`. +overloads_evaluation.py:86:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. +overloads_evaluation.py:86:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:103:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. +overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`. +overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:147:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. +overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cd4e9e46..439cd1ec 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.7 +test_duration = 6.1 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 4e2ea03c..5b7de3cc 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -1,28 +1,43 @@ conformant = "Partial" notes = """ Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" errors_diff = """ -Line 102: Unexpected errors ['overloads_evaluation.py:102:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:102:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] -Line 103: Unexpected errors ['overloads_evaluation.py:103:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:103:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] +Line 104: Unexpected errors ['overloads_evaluation.py:104:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 126: Unexpected errors ['overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"'] +Line 127: Unexpected errors ['overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 147: Unexpected errors ['overloads_evaluation.py:147:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:147:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] """ output = """ -overloads_evaluation.py:26:1 - error: No overloads for "example1" match the provided arguments +overloads_evaluation.py:27:1 - error: No overloads for "example1" match the provided arguments   Argument types: () (reportCallIssue) -overloads_evaluation.py:34:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1" +overloads_evaluation.py:35:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:40:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1" +overloads_evaluation.py:41:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:85:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) -overloads_evaluation.py:85:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2" +overloads_evaluation.py:86:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) +overloads_evaluation.py:86:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) -overloads_evaluation.py:85:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2" +overloads_evaluation.py:86:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) -overloads_evaluation.py:102:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) -overloads_evaluation.py:102:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool" +overloads_evaluation.py:103:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) +overloads_evaluation.py:103:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"   "bool" is not assignable to type "Literal[True]" (reportArgumentType) -overloads_evaluation.py:103:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:104:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue) +overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum" +  "Color" is not assignable to type "Literal[Color.BLUE]" (reportArgumentType) +overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:147:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) +overloads_evaluation.py:147:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple" +  Type "int | str" is not assignable to type "str" +    "int" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:148:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) """ diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index b1d6ad88..915cf401 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -1,81 +1,111 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. -Does not perform argument expansion and union return types of all matching overloads. +Does not perform argument expansion (on any types) when matching overloads. """ conformance_automated = "Fail" errors_diff = """ -Line 85: Expected 1 errors -Line 22: Unexpected errors ['overloads_evaluation.py:22:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] -Line 35: Unexpected errors ['overloads_evaluation.py:35:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] -Line 41: Unexpected errors ['overloads_evaluation.py:41:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] -Line 59: Unexpected errors ['overloads_evaluation.py:59:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] -Line 78: Unexpected errors ['overloads_evaluation.py:78:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: str [assert-type]'] -Line 99: Unexpected errors ['overloads_evaluation.py:99:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:99:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] -Line 102: Unexpected errors ['overloads_evaluation.py:102:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] -Line 103: Unexpected errors ['overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in _: Any [assert-type]'] +Line 86: Expected 1 errors +Line 23: Unexpected errors ['overloads_evaluation.py:23:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] +Line 36: Unexpected errors ['overloads_evaluation.py:36:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 42: Unexpected errors ['overloads_evaluation.py:42:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 60: Unexpected errors ['overloads_evaluation.py:60:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] +Line 79: Unexpected errors ['overloads_evaluation.py:79:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_union: str [assert-type]'] +Line 100: Unexpected errors ['overloads_evaluation.py:100:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:100:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] +Line 104: Unexpected errors ['overloads_evaluation.py:104:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Any [assert-type]'] +Line 123: Unexpected errors ['overloads_evaluation.py:123:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_enum: bad return type [bad-return-type]'] +Line 126: Unexpected errors ['overloads_evaluation.py:126:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types]'] +Line 127: Unexpected errors ['overloads_evaluation.py:127:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Any [assert-type]'] +Line 144: Unexpected errors ['overloads_evaluation.py:144:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] """ output = """ -overloads_evaluation.py:22:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] +overloads_evaluation.py:23:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m Called from (traceback): - line 37, in current file -overloads_evaluation.py:26:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] + line 38, in current file +overloads_evaluation.py:27:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] example1() # E: no matching overload \u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:34:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:35:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] ret2 = example1(1, 1) # E: Literal[1] not assignable to str \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:35:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] +overloads_evaluation.py:36:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] assert_type(ret2, int) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:40:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:41:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] ret4 = example1(1) # E: Literal[1] not assignable to str \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:41:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] +overloads_evaluation.py:42:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] assert_type(ret4, str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:59:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] +overloads_evaluation.py:60:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:78:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: str [assert-type] +overloads_evaluation.py:79:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_union: str [assert-type] assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:99:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] +overloads_evaluation.py:100:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] return int(x) \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:99:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] +overloads_evaluation.py:100:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] return int(x) \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:102:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: Function expand_bool was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:103:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types] ret1 = expand_bool(v) \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in _: Any [assert-type] +overloads_evaluation.py:104:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Any [assert-type] assert_type(ret1, Literal[0, 1]) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_evaluation.py:123:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_enum: bad return type [bad-return-type] + + return x.value + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:126:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types] + + ret1 = expand_enum(v) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:127:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Any [assert-type] + + assert_type(ret1, Literal[0, 1]) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:144:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:148:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index ab86ac89..5c983231 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.2 +test_duration = 31.0 diff --git a/conformance/results/results.html b/conformance/results/results.html index 2ed4ed13..8d7f3d17 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
- - - - + + + + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 14a32519..f20831c2 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -2,6 +2,7 @@ Tests for evaluation of calls to overloaded functions. """ +from enum import Enum from typing import assert_type, Literal, overload @@ -73,7 +74,7 @@ def example2(x: int, y: int | str, z: int) -> int | str: # > respective return types by union to determine the final return type # > for the call, and stop. -def _(v: int | str) -> None: +def check_expand_union(v: int | str) -> None: ret1 = example2(1, v, 1) assert_type(ret1, int | str) @@ -81,7 +82,7 @@ def _(v: int | str) -> None: # > more of the expanded argument lists cannot be evaluated successfully, # > generate an error and stop. -def _(v: int | str) -> None: +def check_expand_union_2(v: int | str) -> None: example2(v, v, 1) # E: no overload matches (str, ..., ...) @@ -98,6 +99,51 @@ def expand_bool(x: Literal[True]) -> Literal[1]: def expand_bool(x: bool) -> int: return int(x) -def _(v: bool) -> None: +def check_expand_bool(v: bool) -> None: ret1 = expand_bool(v) assert_type(ret1, Literal[0, 1]) + + +# > 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should +# > be expanded into their literal members. + +class Color(Enum): + RED = 1 + BLUE = 1 + +@overload +def expand_enum(x: Literal[Color.RED]) -> Literal[0]: + ... + +@overload +def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]: + ... + +def expand_enum(x: Color) -> int: + return x.value + +def check_expand_enum(v: Color) -> None: + ret1 = expand_enum(v) + assert_type(ret1, Literal[0, 1]) + +# > 5. Tuples of known length that contain expandable types should be expanded +# > into all possible combinations of their element types. For example, the type +# > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, +# > ``(int, Literal[False])``, ``(str, Literal[True])``, and +# > ``(str, Literal[False])``. + +@overload +def expand_tuple(x: tuple[int, int]) -> int: + ... + +@overload +def expand_tuple(x: tuple[int, str]) -> str: + ... + +def expand_tuple(x: tuple[int, int | str]) -> int | str: + return 1 + +def check_expand_tuple(v: int | str) -> None: + ret1 = expand_tuple((1, v)) + assert_type(ret1, int | str) + From c041484145c4be36ec8ea5aa58013c90275e85aa Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 19:18:46 -0800 Subject: [PATCH 31/38] add test for type[A | B] expansion --- .../results/mypy/overloads_evaluation.toml | 8 ++++---- .../results/pyre/overloads_evaluation.toml | 13 ++++++++---- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 10 +++++----- .../results/pytype/overloads_evaluation.toml | 20 +++++++++++++++---- conformance/results/results.html | 8 ++++---- conformance/tests/overloads_evaluation.py | 19 ++++++++++++++++++ 7 files changed, 58 insertions(+), 22 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 2b911de2..23d82075 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -13,8 +13,8 @@ Line 103: Unexpected errors ['overloads_evaluation.py:103: error: No overload va Line 104: Unexpected errors ['overloads_evaluation.py:104: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] Line 126: Unexpected errors ['overloads_evaluation.py:126: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] Line 127: Unexpected errors ['overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] -Line 147: Unexpected errors ['overloads_evaluation.py:147: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] -Line 148: Unexpected errors ['overloads_evaluation.py:148: error: Expression is of type "int", not "int | str" [assert-type]'] +Line 166: Unexpected errors ['overloads_evaluation.py:166: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] +Line 167: Unexpected errors ['overloads_evaluation.py:167: error: Expression is of type "int", not "int | str" [assert-type]'] """ output = """ overloads_evaluation.py:27: error: All overload variants of "example1" require at least one argument [call-overload] @@ -43,6 +43,6 @@ overloads_evaluation.py:126: note: Possible overload variants: overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] -overloads_evaluation.py:147: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] -overloads_evaluation.py:148: error: Expression is of type "int", not "int | str" [assert-type] +overloads_evaluation.py:166: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] +overloads_evaluation.py:167: error: Expression is of type "int", not "int | str" [assert-type] """ diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index 49bfb38c..79dfb530 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -2,6 +2,7 @@ conformant = "Partial" notes = """ Does not expand boolean arguments to Literal[True] and Literal[False]. Does not expand enum arguments to literal variants. +Does not expand type[A | B] to type[A] and type[B]. Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" @@ -12,8 +13,10 @@ Line 103: Unexpected errors ['overloads_evaluation.py:103:23 Incompatible parame Line 104: Unexpected errors ['overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] Line 126: Unexpected errors ['overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`.'] Line 127: Unexpected errors ['overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] -Line 147: Unexpected errors ['overloads_evaluation.py:147:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] -Line 148: Unexpected errors ['overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 144: Unexpected errors ['overloads_evaluation.py:144:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`.'] +Line 145: Unexpected errors ['overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 166: Unexpected errors ['overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] +Line 167: Unexpected errors ['overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] """ output = """ overloads_evaluation.py:27:0 Missing argument [20]: Call `example1` expects argument `x`. @@ -27,6 +30,8 @@ overloads_evaluation.py:103:23 Incompatible parameter type [6]: In call `expand_ overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`. overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. -overloads_evaluation.py:147:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. -overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:144:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`. +overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. +overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 439cd1ec..e22b34fd 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.1 +test_duration = 5.9 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 5b7de3cc..d38b282c 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -10,8 +10,8 @@ Line 103: Unexpected errors ['overloads_evaluation.py:103:12 - error: No overloa Line 104: Unexpected errors ['overloads_evaluation.py:104:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] Line 126: Unexpected errors ['overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"'] Line 127: Unexpected errors ['overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] -Line 147: Unexpected errors ['overloads_evaluation.py:147:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:147:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] -Line 148: Unexpected errors ['overloads_evaluation.py:148:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] +Line 166: Unexpected errors ['overloads_evaluation.py:166:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:166:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] +Line 167: Unexpected errors ['overloads_evaluation.py:167:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:27:1 - error: No overloads for "example1" match the provided arguments @@ -35,9 +35,9 @@ overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"   "Color" is not assignable to type "Literal[Color.BLUE]" (reportArgumentType) overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) -overloads_evaluation.py:147:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) -overloads_evaluation.py:147:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple" +overloads_evaluation.py:166:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) +overloads_evaluation.py:166:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"   Type "int | str" is not assignable to type "str"     "int" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:148:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:167:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) """ diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 915cf401..10f0f163 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -17,8 +17,10 @@ Line 104: Unexpected errors ['overloads_evaluation.py:104:5: \\x1b[1m\\x1b[31mer Line 123: Unexpected errors ['overloads_evaluation.py:123:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_enum: bad return type [bad-return-type]'] Line 126: Unexpected errors ['overloads_evaluation.py:126:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types]'] Line 127: Unexpected errors ['overloads_evaluation.py:127:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Any [assert-type]'] -Line 144: Unexpected errors ['overloads_evaluation.py:144:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] -Line 148: Unexpected errors ['overloads_evaluation.py:148:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] +Line 141: Unexpected errors ['overloads_evaluation.py:141:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_type_union: bad return type [bad-return-type]'] +Line 145: Unexpected errors ['overloads_evaluation.py:145:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_type_union: int [assert-type]'] +Line 163: Unexpected errors ['overloads_evaluation.py:163:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] +Line 167: Unexpected errors ['overloads_evaluation.py:167:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] """ output = """ overloads_evaluation.py:23:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] @@ -98,12 +100,22 @@ overloads_evaluation.py:127:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in c assert_type(ret1, Literal[0, 1]) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:144:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] +overloads_evaluation.py:141:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_type_union: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:148:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] +overloads_evaluation.py:145:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_type_union: int [assert-type] + + assert_type(ret1, int | str) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:163:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:167:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m diff --git a/conformance/results/results.html b/conformance/results/results.html index 8d7f3d17..d1d5a037 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,7 +165,7 @@

Python Type System Conformance Test Results

1.5sec
- - - + + + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index f20831c2..f73c27fd 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -126,6 +126,25 @@ def check_expand_enum(v: Color) -> None: ret1 = expand_enum(v) assert_type(ret1, Literal[0, 1]) + +# > 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + +@overload +def expand_type_union(x: type[int]) -> int: + ... + +@overload +def expand_type_union(x: type[str]) -> str: + ... + +def expand_type_union(x: type[int] | type[str]) -> int | str: + return 1 + +def check_expand_type_union(v: type[int | str]) -> None: + ret1 = expand_type_union(v) + assert_type(ret1, int | str) + + # > 5. Tuples of known length that contain expandable types should be expanded # > into all possible combinations of their element types. For example, the type # > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, From c10a72db8f953bd86e070d8cd8f997ba9afb2faa Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 19:27:53 -0800 Subject: [PATCH 32/38] add test for step 4 in overload matching --- conformance/results/mypy/version.toml | 2 +- .../results/pyre/overloads_evaluation.toml | 3 +++ conformance/results/pyre/version.toml | 2 +- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_evaluation.toml | 6 +++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 10 ++++---- conformance/tests/overloads_evaluation.py | 25 +++++++++++++++++++ 8 files changed, 43 insertions(+), 9 deletions(-) diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 1012f096..9e6527ca 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.7 +test_duration = 1.8 diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index 79dfb530..ef2abadb 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -4,6 +4,7 @@ Does not expand boolean arguments to Literal[True] and Literal[False]. Does not expand enum arguments to literal variants. Does not expand type[A | B] to type[A] and type[B]. Does not expand tuple arguments to possible combinations. +Does not prefer variadic match to indeterminate-length unpacked argument. """ conformance_automated = "Fail" errors_diff = """ @@ -17,6 +18,7 @@ Line 144: Unexpected errors ['overloads_evaluation.py:144:29 Incompatible parame Line 145: Unexpected errors ['overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] Line 166: Unexpected errors ['overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] Line 167: Unexpected errors ['overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 193: Unexpected errors ['overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`.'] """ output = """ overloads_evaluation.py:27:0 Missing argument [20]: Call `example1` expects argument `x`. @@ -34,4 +36,5 @@ overloads_evaluation.py:144:29 Incompatible parameter type [6]: In call `expand_ overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`. overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index e22b34fd..2904f5c8 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 5.9 +test_duration = 6.0 diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 1ae5a35b..eef86e05 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.5 +test_duration = 1.6 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 10f0f163..917b4461 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -21,6 +21,7 @@ Line 141: Unexpected errors ['overloads_evaluation.py:141:12: \\x1b[1m\\x1b[31me Line 145: Unexpected errors ['overloads_evaluation.py:145:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_type_union: int [assert-type]'] Line 163: Unexpected errors ['overloads_evaluation.py:163:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] Line 167: Unexpected errors ['overloads_evaluation.py:167:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] +Line 185: Unexpected errors ['overloads_evaluation.py:185:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] """ output = """ overloads_evaluation.py:23:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] @@ -120,4 +121,9 @@ overloads_evaluation.py:167:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in c assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +overloads_evaluation.py:185:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in variadic: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 5c983231..ab86ac89 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.0 +test_duration = 31.2 diff --git a/conformance/results/results.html b/conformance/results/results.html index d1d5a037..c039dd23 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,16 +159,16 @@

Python Type System Conformance Test Results

 
mypy 1.14.1
-
1.8sec
+
1.7sec
pyright 1.1.391
-
1.7sec
+
1.5sec
pyre 0.9.23
-
6.0sec
+
6.7sec
pytype 2024.10.11
-
31.1sec
+
32.1sec
@@ -675,7 +675,7 @@

Python Type System Conformance Test Results

     overloads_consistency Pass PassPass
Partial

Does not apply decorator transforms before checking overload consistency.

Fail

Doesn't appear to validate overload consistency at all.

     overloads_definitions
Partial

Expects @final and @override to be present on all overloads, not just first.

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_overlapPassPass
Fail

Does not check for overlapping overloads with inconsistent return type.

Fail

Does not check for overlapping overloads with inconsistent return type.

Exceptions
pyre 0.9.23
-
6.7sec
+
6.6sec
pytype 2024.10.11
-
32.1sec
+
30.1sec
@@ -693,8 +693,8 @@

Python Type System Conformance Test Results

     overloads_overlap Pass Pass
Fail

Does not check for overlapping overloads with inconsistent return type.

Fail

Does not check for overlapping overloads with inconsistent return type.

Partial

Does not check for partially overlapping overloads with inconsistent return type.

Fail

Does not check for partially or fully overlapping overloads.

Exceptions diff --git a/conformance/tests/overloads_overlap.py b/conformance/tests/overloads_overlap.py index 7fe57f31..b65fa871 100644 --- a/conformance/tests/overloads_overlap.py +++ b/conformance/tests/overloads_overlap.py @@ -20,3 +20,21 @@ def is_one(x: int) -> Literal[False]: def is_one(x: int) -> bool: return x == 1 + + +# > If all possible sets of arguments accepted by an overload are also always +# > accepted by an earlier overload, the two overloads are said to "fully overlap". +# > In this case, the latter overload will never be used. This condition +# > is indicative of a programming error and should be reported by type +# > checkers. + +@overload +def full_overlap(x: bool) -> bool: + ... + +@overload +def full_overlap(x: Literal[False]) -> int: # E: overload will never be used due to full overlap + ... + +def full_overlap(x: bool) -> int: + return 1 From 17d3e15b10564c89e9c02e56957271a77a25e5b6 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 18:33:27 -0800 Subject: [PATCH 27/38] add tests for step-1 of overload evaluation --- .../results/mypy/overloads_evaluation.toml | 25 +++++++++++ .../results/pyre/overloads_evaluation.toml | 9 ++++ conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 12 +++++ .../results/pytype/overloads_evaluation.toml | 44 +++++++++++++++++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 10 ++++- conformance/tests/overloads_evaluation.py | 44 +++++++++++++++++++ 8 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 conformance/results/mypy/overloads_evaluation.toml create mode 100644 conformance/results/pyre/overloads_evaluation.toml create mode 100644 conformance/results/pyright/overloads_evaluation.toml create mode 100644 conformance/results/pytype/overloads_evaluation.toml create mode 100644 conformance/tests/overloads_evaluation.py diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml new file mode 100644 index 00000000..ed5346a5 --- /dev/null +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -0,0 +1,25 @@ +conformant = "Partial" +notes = """ +Does not pick a winning overload based on arity, prior to considering argument types. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 36: Unexpected errors ['overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type]'] +Line 42: Unexpected errors ['overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type]'] +""" +output = """ +overloads_evaluation.py:26: error: All overload variants of "num_args" require at least one argument [call-overload] +overloads_evaluation.py:26: note: Possible overload variants: +overloads_evaluation.py:26: note: def num_args(x: int, y: str) -> int +overloads_evaluation.py:26: note: def num_args(x: str) -> str +overloads_evaluation.py:35: error: No overload variant of "num_args" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:35: note: Possible overload variants: +overloads_evaluation.py:35: note: def num_args(x: int, y: str) -> int +overloads_evaluation.py:35: note: def num_args(x: str) -> str +overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type] +overloads_evaluation.py:41: error: No overload variant of "num_args" matches argument type "int" [call-overload] +overloads_evaluation.py:41: note: Possible overload variants: +overloads_evaluation.py:41: note: def num_args(x: int, y: str) -> int +overloads_evaluation.py:41: note: def num_args(x: str) -> str +overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type] +""" diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml new file mode 100644 index 00000000..311020b4 --- /dev/null +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_evaluation.py:26:0 Missing argument [20]: Call `num_args` expects argument `x`. +overloads_evaluation.py:35:19 Incompatible parameter type [6]: In call `num_args`, for 2nd positional argument, expected `str` but got `int`. +overloads_evaluation.py:41:16 Incompatible parameter type [6]: In call `num_args`, for 1st positional argument, expected `str` but got `int`. +""" diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cda658b7..cd4e9e46 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.6 +test_duration = 6.7 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml new file mode 100644 index 00000000..9ef2de0b --- /dev/null +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_evaluation.py:26:1 - error: No overloads for "num_args" match the provided arguments +  Argument types: () (reportCallIssue) +overloads_evaluation.py:35:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "num_args" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:41:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "num_args" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +""" diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml new file mode 100644 index 00000000..5ae1b37d --- /dev/null +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -0,0 +1,44 @@ +conformant = "Partial" +notes = """ +Does not pick a winning overload based on arity, prior to considering argument types. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 24: Unexpected errors ['overloads_evaluation.py:24:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in num_args: bad return type [bad-return-type]'] +Line 36: Unexpected errors ['overloads_evaluation.py:36:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +Line 42: Unexpected errors ['overloads_evaluation.py:42:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] +""" +output = """ +overloads_evaluation.py:24:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in num_args: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +Called from (traceback): + line 38, in current file +overloads_evaluation.py:26:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function num_args [missing-parameter] + +num_args() # E: no matching overload +\u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:35:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function num_args was called with the wrong arguments [wrong-arg-types] + +ret2 = num_args(1, 1) # E: Literal[1] not assignable to str + \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:36:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] + +assert_type(ret2, int) +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:41:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function num_args was called with the wrong arguments [wrong-arg-types] + +ret4 = num_args(1) # E: Literal[1] not assignable to str + \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m + +overloads_evaluation.py:42:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] + +assert_type(ret4, str) +\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + +""" diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 7dff3b18..add97f41 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 30.1 +test_duration = 30.7 diff --git a/conformance/results/results.html b/conformance/results/results.html index 71692ca0..414eee34 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -165,10 +165,10 @@

Python Type System Conformance Test Results

1.5sec
pyre 0.9.23
-
6.6sec
+
6.7sec
pytype 2024.10.11
-
30.1sec
+
30.7sec
@@ -690,6 +690,12 @@

Python Type System Conformance Test Results

Partial

Expects @final and @override to be present on all overloads, not just first.

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

PassPass
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

     overloads_overlap Pass Pass
pyright 1.1.391
-
1.5sec
+
1.6sec
pyre 0.9.23
-
6.7sec
+
6.1sec
pytype 2024.10.11
-
30.7sec
+
31.5sec
@@ -694,7 +694,7 @@

Python Type System Conformance Test Results

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Pass Pass
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion on unions and union return types of all matching overloads.

     overloads_overlap Pass
pyright 1.1.391
-
1.6sec
+
1.5sec
pyre 0.9.23
-
6.1sec
+
6.7sec
pytype 2024.10.11
-
31.5sec
+
31.2sec
@@ -691,10 +691,10 @@

Python Type System Conformance Test Results

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

PassPass
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion on unions and union return types of all matching overloads.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion and union return types of all matching overloads.

     overloads_overlap Pass
pyre 0.9.23
-
6.7sec
+
6.1sec
pytype 2024.10.11
-
31.2sec
+
31.0sec
@@ -691,10 +691,10 @@

Python Type System Conformance Test Results

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion and union return types of all matching overloads.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

     overloads_overlap Pass
pyre 0.9.23
-
6.1sec
+
5.9sec
pytype 2024.10.11
31.0sec
@@ -691,9 +691,9 @@

Python Type System Conformance Test Results

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

     overloads_overlap
- + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index f73c27fd..76150923 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -166,3 +166,28 @@ def check_expand_tuple(v: int | str) -> None: ret1 = expand_tuple((1, v)) assert_type(ret1, int | str) + +# > Step 4: If the argument list is compatible with two or more overloads, +# > determine whether one or more of the overloads has a variadic parameter +# > (either ``*args`` or ``**kwargs``) that maps to a corresponding argument +# > that supplies an indeterminate number of positional or keyword arguments. +# > If so, eliminate overloads that do not have a variadic parameter. + +@overload +def variadic(x: int, /) -> Literal[0]: + ... + +@overload +def variadic(*args: int) -> int: + ... + +def variadic(*args: int) -> int | str: + return 1 + +# > - If this results in only one remaining candidate overload, it is +# > the winning match. Evaluate it as if it were a non-overloaded function +# > call and stop. + +def check_variadic(v: list[int]) -> None: + ret1 = variadic(*v) + assert_type(ret1, int) From 8a98eae07398bf9153096241ba89f699ee4ab1c6 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Fri, 10 Jan 2025 19:39:24 -0800 Subject: [PATCH 33/38] add test for steps 5/6 in overload matching --- conformance/results/mypy/version.toml | 2 +- .../results/pyre/overloads_evaluation.toml | 3 ++ conformance/results/pyre/version.toml | 2 +- .../results/pytype/overloads_evaluation.toml | 13 ++++++++ conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 10 +++---- conformance/tests/overloads_evaluation.py | 30 ++++++++++++++++++- 7 files changed, 53 insertions(+), 9 deletions(-) diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 9e6527ca..1012f096 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.8 +test_duration = 1.7 diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index ef2abadb..e5aadd7f 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -5,6 +5,7 @@ Does not expand enum arguments to literal variants. Does not expand type[A | B] to type[A] and type[B]. Does not expand tuple arguments to possible combinations. Does not prefer variadic match to indeterminate-length unpacked argument. +Does not treat multiple matches due to gradual types as ambiguous. """ conformance_automated = "Fail" errors_diff = """ @@ -19,6 +20,7 @@ Line 145: Unexpected errors ['overloads_evaluation.py:145:4 Assert type [70]: Ex Line 166: Unexpected errors ['overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] Line 167: Unexpected errors ['overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] Line 193: Unexpected errors ['overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`.'] +Line 221: Unexpected errors ['overloads_evaluation.py:221:4 Assert type [70]: Expected `typing.Any` but got `int`.'] """ output = """ overloads_evaluation.py:27:0 Missing argument [20]: Call `example1` expects argument `x`. @@ -37,4 +39,5 @@ overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but g overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`. overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:221:4 Assert type [70]: Expected `typing.Any` but got `int`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 2904f5c8..439cd1ec 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.0 +test_duration = 6.1 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 917b4461..88d3dea3 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -2,6 +2,7 @@ conformant = "Partial" notes = """ Does not pick a winning overload based on arity, prior to considering argument types. Does not perform argument expansion (on any types) when matching overloads. +Does not treat multiple matches due to gradual types as ambiguous. """ conformance_automated = "Fail" errors_diff = """ @@ -22,6 +23,8 @@ Line 145: Unexpected errors ['overloads_evaluation.py:145:5: \\x1b[1m\\x1b[31mer Line 163: Unexpected errors ['overloads_evaluation.py:163:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] Line 167: Unexpected errors ['overloads_evaluation.py:167:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] Line 185: Unexpected errors ['overloads_evaluation.py:185:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] +Line 214: Unexpected errors ['overloads_evaluation.py:214:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example4: bad return type [bad-return-type]'] +Line 221: Unexpected errors ['overloads_evaluation.py:221:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_example4: int [assert-type]'] """ output = """ overloads_evaluation.py:23:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] @@ -126,4 +129,14 @@ overloads_evaluation.py:185:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m +overloads_evaluation.py:214:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example4: bad return type [bad-return-type] + + return 1 + \u001b[1m\u001b[31m~\u001b[39m\u001b[0m + +overloads_evaluation.py:221:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_example4: int [assert-type] + + assert_type(ret2, Any) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + """ diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index ab86ac89..1da94b08 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.2 +test_duration = 31.1 diff --git a/conformance/results/results.html b/conformance/results/results.html index c039dd23..0ecf45cc 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,16 +159,16 @@

Python Type System Conformance Test Results

 
mypy 1.14.1
-
1.7sec
+
1.8sec
pyright 1.1.391
-
1.5sec
+
1.6sec
pyre 0.9.23
-
5.9sec
+
6.0sec
pytype 2024.10.11
-
31.0sec
+
31.2sec
@@ -693,7 +693,7 @@

Python Type System Conformance Test Results

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

     overloads_overlap
- - + + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 76150923..27a993d2 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -3,7 +3,7 @@ """ from enum import Enum -from typing import assert_type, Literal, overload +from typing import Any, assert_type, Literal, overload # > Step 1: Examine the argument list to determine the number of @@ -191,3 +191,31 @@ def variadic(*args: int) -> int | str: def check_variadic(v: list[int]) -> None: ret1 = variadic(*v) assert_type(ret1, int) + + +# > Step 5: For each argument, determine whether all possible +# > :term:`materializations ` of the argument's type are assignable to +# > the corresponding parameter type for each of the remaining overloads. If so, +# > eliminate all of the subsequent remaining overloads. + +@overload +def example4(x: list[int], y: int) -> int: + ... + +@overload +def example4(x: list[str], y: str) -> int: + ... + +@overload +def example4(x: int, y: int) -> list[int]: + ... + +def example4(x: list[int] | list[str] | int, y: int | str) -> int | list[int]: + return 1 + +def check_example4(v1: list[Any], v2: Any): + ret1 = example4(v1, v2) + assert_type(ret1, int) + + ret2 = example4(v2, 1) + assert_type(ret2, Any) From 5d22e8dacdf9eabcc8827285c36e6dd4bef27761 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 13 Jan 2025 09:33:05 -0800 Subject: [PATCH 34/38] no expectation of return type if there are call errors --- .../results/mypy/overloads_evaluation.toml | 69 +++++++------ conformance/results/mypy/version.toml | 2 +- .../results/pyre/overloads_evaluation.toml | 58 +++++------ conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 42 ++++---- conformance/results/pyright/version.toml | 2 +- .../results/pytype/overloads_evaluation.toml | 96 ++++++++----------- conformance/results/results.html | 8 +- conformance/tests/overloads_evaluation.py | 11 ++- 9 files changed, 138 insertions(+), 152 deletions(-) diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml index 23d82075..b1d7d783 100644 --- a/conformance/results/mypy/overloads_evaluation.toml +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -1,48 +1,43 @@ conformant = "Partial" notes = """ -Does not pick a winning overload based on arity, prior to considering argument types. Does not expand boolean arguments to Literal[True] and Literal[False]. Does not expand enum arguments to literal variants. Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" errors_diff = """ -Line 36: Unexpected errors ['overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type]'] -Line 42: Unexpected errors ['overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type]'] -Line 103: Unexpected errors ['overloads_evaluation.py:103: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] -Line 104: Unexpected errors ['overloads_evaluation.py:104: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] -Line 126: Unexpected errors ['overloads_evaluation.py:126: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] -Line 127: Unexpected errors ['overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] -Line 166: Unexpected errors ['overloads_evaluation.py:166: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] -Line 167: Unexpected errors ['overloads_evaluation.py:167: error: Expression is of type "int", not "int | str" [assert-type]'] +Line 106: Unexpected errors ['overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] +Line 107: Unexpected errors ['overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 129: Unexpected errors ['overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] +Line 130: Unexpected errors ['overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +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]'] +Line 170: Unexpected errors ['overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type]'] """ output = """ -overloads_evaluation.py:27: error: All overload variants of "example1" require at least one argument [call-overload] -overloads_evaluation.py:27: note: Possible overload variants: -overloads_evaluation.py:27: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:27: note: def example1(x: str) -> str -overloads_evaluation.py:35: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] -overloads_evaluation.py:35: note: Possible overload variants: -overloads_evaluation.py:35: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:35: note: def example1(x: str) -> str -overloads_evaluation.py:36: error: Expression is of type "Any", not "int" [assert-type] -overloads_evaluation.py:41: error: No overload variant of "example1" matches argument type "int" [call-overload] -overloads_evaluation.py:41: note: Possible overload variants: -overloads_evaluation.py:41: note: def example1(x: int, y: str) -> int -overloads_evaluation.py:41: note: def example1(x: str) -> str -overloads_evaluation.py:42: error: Expression is of type "Any", not "str" [assert-type] -overloads_evaluation.py:86: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] -overloads_evaluation.py:86: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] -overloads_evaluation.py:103: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] -overloads_evaluation.py:103: note: Possible overload variants: -overloads_evaluation.py:103: note: def expand_bool(x: Literal[False]) -> Literal[0] -overloads_evaluation.py:103: note: def expand_bool(x: Literal[True]) -> Literal[1] -overloads_evaluation.py:104: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] -overloads_evaluation.py:126: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload] -overloads_evaluation.py:126: note: Possible overload variants: -overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] -overloads_evaluation.py:126: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] -overloads_evaluation.py:127: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] -overloads_evaluation.py:166: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] -overloads_evaluation.py:167: error: Expression is of type "int", not "int | str" [assert-type] +overloads_evaluation.py:32: error: All overload variants of "example1" require at least one argument [call-overload] +overloads_evaluation.py:32: note: Possible overload variants: +overloads_evaluation.py:32: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:32: note: def example1(x: str) -> str +overloads_evaluation.py:40: error: No overload variant of "example1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:40: note: Possible overload variants: +overloads_evaluation.py:40: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:40: note: def example1(x: str) -> str +overloads_evaluation.py:45: error: No overload variant of "example1" matches argument type "int" [call-overload] +overloads_evaluation.py:45: note: Possible overload variants: +overloads_evaluation.py:45: note: def example1(x: int, y: str) -> int +overloads_evaluation.py:45: note: def example1(x: str) -> str +overloads_evaluation.py:89: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:89: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:106: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] +overloads_evaluation.py:106: note: Possible overload variants: +overloads_evaluation.py:106: note: def expand_bool(x: Literal[False]) -> Literal[0] +overloads_evaluation.py:106: note: def expand_bool(x: Literal[True]) -> Literal[1] +overloads_evaluation.py:107: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:129: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload] +overloads_evaluation.py:129: note: Possible overload variants: +overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] +overloads_evaluation.py:129: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] +overloads_evaluation.py:130: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:169: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] +overloads_evaluation.py:170: error: Expression is of type "int", not "int | str" [assert-type] """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 1012f096..9e6527ca 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.7 +test_duration = 1.8 diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index e5aadd7f..7b950d72 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -9,35 +9,35 @@ Does not treat multiple matches due to gradual types as ambiguous. """ conformance_automated = "Fail" errors_diff = """ -Line 78: Unexpected errors ['overloads_evaluation.py:78:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`.'] -Line 79: Unexpected errors ['overloads_evaluation.py:79:4 Assert type [70]: Expected `Union[int, str]` but got `str`.'] -Line 103: Unexpected errors ['overloads_evaluation.py:103:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] -Line 104: Unexpected errors ['overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] -Line 126: Unexpected errors ['overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`.'] -Line 127: Unexpected errors ['overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] -Line 144: Unexpected errors ['overloads_evaluation.py:144:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`.'] -Line 145: Unexpected errors ['overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] -Line 166: Unexpected errors ['overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] -Line 167: Unexpected errors ['overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] -Line 193: Unexpected errors ['overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`.'] -Line 221: Unexpected errors ['overloads_evaluation.py:221:4 Assert type [70]: Expected `typing.Any` but got `int`.'] +Line 81: Unexpected errors ['overloads_evaluation.py:81:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`.'] +Line 82: Unexpected errors ['overloads_evaluation.py:82:4 Assert type [70]: Expected `Union[int, str]` but got `str`.'] +Line 106: Unexpected errors ['overloads_evaluation.py:106:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`.'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`.'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`.'] +Line 147: Unexpected errors ['overloads_evaluation.py:147:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`.'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 169: Unexpected errors ['overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`.'] +Line 224: Unexpected errors ['overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`.'] """ output = """ -overloads_evaluation.py:27:0 Missing argument [20]: Call `example1` expects argument `x`. -overloads_evaluation.py:35:19 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. -overloads_evaluation.py:41:16 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. -overloads_evaluation.py:78:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. -overloads_evaluation.py:79:4 Assert type [70]: Expected `Union[int, str]` but got `str`. -overloads_evaluation.py:86:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. -overloads_evaluation.py:86:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. -overloads_evaluation.py:103:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. -overloads_evaluation.py:104:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. -overloads_evaluation.py:126:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`. -overloads_evaluation.py:127:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. -overloads_evaluation.py:144:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`. -overloads_evaluation.py:145:4 Assert type [70]: Expected `Union[int, str]` but got `int`. -overloads_evaluation.py:166:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. -overloads_evaluation.py:167:4 Assert type [70]: Expected `Union[int, str]` but got `int`. -overloads_evaluation.py:193:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`. -overloads_evaluation.py:221:4 Assert type [70]: Expected `typing.Any` but got `int`. +overloads_evaluation.py:32:0 Missing argument [20]: Call `example1` expects argument `x`. +overloads_evaluation.py:40:12 Incompatible parameter type [6]: In call `example1`, for 2nd positional argument, expected `str` but got `int`. +overloads_evaluation.py:45:9 Incompatible parameter type [6]: In call `example1`, for 1st positional argument, expected `str` but got `int`. +overloads_evaluation.py:81:23 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:82:4 Assert type [70]: Expected `Union[int, str]` but got `str`. +overloads_evaluation.py:89:13 Incompatible parameter type [6]: In call `example2`, for 1st positional argument, expected `int` but got `Union[int, str]`. +overloads_evaluation.py:89:16 Incompatible parameter type [6]: In call `example2`, for 2nd positional argument, expected `str` but got `Union[int, str]`. +overloads_evaluation.py:106:23 Incompatible parameter type [6]: In call `expand_bool`, for 1st positional argument, expected `typing_extensions.Literal[False]` but got `bool`. +overloads_evaluation.py:107:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:129:23 Incompatible parameter type [6]: In call `expand_enum`, for 1st positional argument, expected `typing_extensions.Literal[Color.RED]` but got `Color`. +overloads_evaluation.py:130:4 Assert type [70]: Expected `Union[typing_extensions.Literal[0], typing_extensions.Literal[1]]` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:147:29 Incompatible parameter type [6]: In call `expand_type_union`, for 1st positional argument, expected `Type[int]` but got `Type[Union[int, str]]`. +overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. +overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`. +overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index 439cd1ec..cd4e9e46 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.1 +test_duration = 6.7 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index d38b282c..8684e650 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -6,38 +6,38 @@ Does not expand tuple arguments to possible combinations. """ conformance_automated = "Fail" errors_diff = """ -Line 103: Unexpected errors ['overloads_evaluation.py:103:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:103:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] -Line 104: Unexpected errors ['overloads_evaluation.py:104:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] -Line 126: Unexpected errors ['overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"'] -Line 127: Unexpected errors ['overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] -Line 166: Unexpected errors ['overloads_evaluation.py:166:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:166:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] -Line 167: Unexpected errors ['overloads_evaluation.py:167:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] +Line 106: Unexpected errors ['overloads_evaluation.py:106:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:106:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:129:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] +Line 169: Unexpected errors ['overloads_evaluation.py:169:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] """ output = """ -overloads_evaluation.py:27:1 - error: No overloads for "example1" match the provided arguments +overloads_evaluation.py:32:1 - error: No overloads for "example1" match the provided arguments   Argument types: () (reportCallIssue) -overloads_evaluation.py:35:20 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1" +overloads_evaluation.py:40:13 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:41:17 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1" +overloads_evaluation.py:45:10 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1"   "Literal[1]" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:86:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) -overloads_evaluation.py:86:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2" +overloads_evaluation.py:89:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) +overloads_evaluation.py:89:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) -overloads_evaluation.py:86:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2" +overloads_evaluation.py:89:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2"   Type "int | str" is not assignable to type "int"     "str" is not assignable to "int" (reportArgumentType) -overloads_evaluation.py:103:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) -overloads_evaluation.py:103:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool" +overloads_evaluation.py:106:12 - error: No overloads for "expand_bool" match the provided arguments (reportCallIssue) +overloads_evaluation.py:106:24 - error: Argument of type "bool" cannot be assigned to parameter "x" of type "Literal[True]" in function "expand_bool"   "bool" is not assignable to type "Literal[True]" (reportArgumentType) -overloads_evaluation.py:104:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) -overloads_evaluation.py:126:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue) -overloads_evaluation.py:126:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum" +overloads_evaluation.py:107:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:129:12 - error: No overloads for "expand_enum" match the provided arguments (reportCallIssue) +overloads_evaluation.py:129:24 - error: Argument of type "Color" cannot be assigned to parameter "x" of type "Literal[Color.BLUE]" in function "expand_enum"   "Color" is not assignable to type "Literal[Color.BLUE]" (reportArgumentType) -overloads_evaluation.py:127:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) -overloads_evaluation.py:166:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) -overloads_evaluation.py:166:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple" +overloads_evaluation.py:130:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:169:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue) +overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"   Type "int | str" is not assignable to type "str"     "int" is not assignable to "str" (reportArgumentType) -overloads_evaluation.py:167:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index eef86e05..324d1870 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.6 +test_duration = 1.7 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index 88d3dea3..c9df7902 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -6,135 +6,123 @@ Does not treat multiple matches due to gradual types as ambiguous. """ conformance_automated = "Fail" errors_diff = """ -Line 86: Expected 1 errors -Line 23: Unexpected errors ['overloads_evaluation.py:23:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] -Line 36: Unexpected errors ['overloads_evaluation.py:36:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] -Line 42: Unexpected errors ['overloads_evaluation.py:42:1: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in : Any [assert-type]'] -Line 60: Unexpected errors ['overloads_evaluation.py:60:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] -Line 79: Unexpected errors ['overloads_evaluation.py:79:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_union: str [assert-type]'] -Line 100: Unexpected errors ['overloads_evaluation.py:100:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:100:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] -Line 103: Unexpected errors ['overloads_evaluation.py:103:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] -Line 104: Unexpected errors ['overloads_evaluation.py:104:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Any [assert-type]'] -Line 123: Unexpected errors ['overloads_evaluation.py:123:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_enum: bad return type [bad-return-type]'] -Line 126: Unexpected errors ['overloads_evaluation.py:126:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types]'] -Line 127: Unexpected errors ['overloads_evaluation.py:127:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Any [assert-type]'] -Line 141: Unexpected errors ['overloads_evaluation.py:141:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_type_union: bad return type [bad-return-type]'] -Line 145: Unexpected errors ['overloads_evaluation.py:145:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_type_union: int [assert-type]'] -Line 163: Unexpected errors ['overloads_evaluation.py:163:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] -Line 167: Unexpected errors ['overloads_evaluation.py:167:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] -Line 185: Unexpected errors ['overloads_evaluation.py:185:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] -Line 214: Unexpected errors ['overloads_evaluation.py:214:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example4: bad return type [bad-return-type]'] -Line 221: Unexpected errors ['overloads_evaluation.py:221:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_example4: int [assert-type]'] +Line 89: Expected 1 errors +Line 28: Unexpected errors ['overloads_evaluation.py:28:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example1: bad return type [bad-return-type]'] +Line 63: Unexpected errors ['overloads_evaluation.py:63:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example2: bad return type [bad-return-type]'] +Line 82: Unexpected errors ['overloads_evaluation.py:82:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_union: str [assert-type]'] +Line 103: Unexpected errors ['overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]', 'overloads_evaluation.py:103:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_bool: bad return type [bad-return-type]'] +Line 106: Unexpected errors ['overloads_evaluation.py:106:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types]'] +Line 107: Unexpected errors ['overloads_evaluation.py:107:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_bool: Any [assert-type]'] +Line 126: Unexpected errors ['overloads_evaluation.py:126:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_enum: bad return type [bad-return-type]'] +Line 129: Unexpected errors ['overloads_evaluation.py:129:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types]'] +Line 130: Unexpected errors ['overloads_evaluation.py:130:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_enum: Any [assert-type]'] +Line 144: Unexpected errors ['overloads_evaluation.py:144:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_type_union: bad return type [bad-return-type]'] +Line 148: Unexpected errors ['overloads_evaluation.py:148:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_type_union: int [assert-type]'] +Line 166: Unexpected errors ['overloads_evaluation.py:166:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] +Line 170: Unexpected errors ['overloads_evaluation.py:170:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] +Line 188: Unexpected errors ['overloads_evaluation.py:188:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] +Line 217: Unexpected errors ['overloads_evaluation.py:217:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example4: bad return type [bad-return-type]'] +Line 224: Unexpected errors ['overloads_evaluation.py:224:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_example4: int [assert-type]'] """ output = """ -overloads_evaluation.py:23:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] +overloads_evaluation.py:28:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example1: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m Called from (traceback): - line 38, in current file -overloads_evaluation.py:27:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] + line 42, in current file +overloads_evaluation.py:32:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Missing parameter 'x' in call to function example1 [missing-parameter] example1() # E: no matching overload \u001b[1m\u001b[31m~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:35:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:40:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] -ret2 = example1(1, 1) # E: Literal[1] not assignable to str - \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m +example1(1, 1) # E: Literal[1] not assignable to str +\u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:36:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] +overloads_evaluation.py:45:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] -assert_type(ret2, int) -\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m +example1(1) # E: Literal[1] not assignable to str +\u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:41:8: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Function example1 was called with the wrong arguments [wrong-arg-types] - -ret4 = example1(1) # E: Literal[1] not assignable to str - \u001b[1m\u001b[31m~~~~~~~~~~~\u001b[39m\u001b[0m - -overloads_evaluation.py:42:1: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in : Any [assert-type] - -assert_type(ret4, str) -\u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m - -overloads_evaluation.py:60:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] +overloads_evaluation.py:63:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example2: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:79:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_union: str [assert-type] +overloads_evaluation.py:82:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_union: str [assert-type] assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:100:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] +overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] return int(x) \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:100:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] +overloads_evaluation.py:103:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_bool: bad return type [bad-return-type] return int(x) \u001b[1m\u001b[31m~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:103:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:106:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Function expand_bool was called with the wrong arguments [wrong-arg-types] ret1 = expand_bool(v) \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:104:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Any [assert-type] +overloads_evaluation.py:107:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_bool: Any [assert-type] assert_type(ret1, Literal[0, 1]) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:123:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_enum: bad return type [bad-return-type] +overloads_evaluation.py:126:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_enum: bad return type [bad-return-type] return x.value \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:126:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types] +overloads_evaluation.py:129:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Function expand_enum was called with the wrong arguments [wrong-arg-types] ret1 = expand_enum(v) \u001b[1m\u001b[31m~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:127:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Any [assert-type] +overloads_evaluation.py:130:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_enum: Any [assert-type] assert_type(ret1, Literal[0, 1]) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:141:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_type_union: bad return type [bad-return-type] +overloads_evaluation.py:144:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_type_union: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:145:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_type_union: int [assert-type] +overloads_evaluation.py:148:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_type_union: int [assert-type] assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:163:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] +overloads_evaluation.py:166:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in expand_tuple: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:167:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] +overloads_evaluation.py:170:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_expand_tuple: int [assert-type] assert_type(ret1, int | str) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m -overloads_evaluation.py:185:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in variadic: bad return type [bad-return-type] +overloads_evaluation.py:188:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in variadic: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:214:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example4: bad return type [bad-return-type] +overloads_evaluation.py:217:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example4: bad return type [bad-return-type] return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m -overloads_evaluation.py:221:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_example4: int [assert-type] +overloads_evaluation.py:224:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_example4: int [assert-type] assert_type(ret2, Any) \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m diff --git a/conformance/results/results.html b/conformance/results/results.html index 0ecf45cc..9a6bc90e 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,13 +159,13 @@

Python Type System Conformance Test Results

 
mypy 1.14.1
-
1.8sec
+
1.7sec
pyright 1.1.391
1.6sec
pyre 0.9.23
-
6.0sec
+
6.1sec
pytype 2024.10.11
-
31.2sec
+
31.1sec
@@ -693,8 +693,8 @@

Python Type System Conformance Test Results

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Does not treat multiple matches due to gradual types as ambiguous.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

Does not treat multiple matches due to gradual types as ambiguous.

     overloads_overlap Pass
- + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index 27a993d2..fc6ce698 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -11,6 +11,11 @@ # > overload candidates that are not plausible based on their # > input signatures. +# (There is no way to observe via conformance tests whether an implementation +# performs this step separately from the argument-type-testing step 2 below, so +# the separation of step 1 from step 2 is purely a presentation choice for the +# algorithm, not a conformance requirement.) + @overload def example1(x: int, y: str) -> int: ... @@ -32,14 +37,12 @@ def example1(x: int | str, y: str = "") -> int | str: ret1 = example1(1, "") assert_type(ret1, int) -ret2 = example1(1, 1) # E: Literal[1] not assignable to str -assert_type(ret2, int) +example1(1, 1) # E: Literal[1] not assignable to str ret3 = example1("") assert_type(ret3, str) -ret4 = example1(1) # E: Literal[1] not assignable to str -assert_type(ret4, str) +example1(1) # E: Literal[1] not assignable to str # > Step 2: Evaluate each remaining overload as a regular (non-overloaded) From 98f36e83e2aaee837b9e5dd5d89276b6c345ff0b Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 13 Jan 2025 13:00:35 -0800 Subject: [PATCH 35/38] improve variadic test to not use overlapping overloads --- conformance/results/mypy/version.toml | 2 +- conformance/results/pyre/overloads_evaluation.toml | 4 ++-- conformance/results/pyre/version.toml | 2 +- .../results/pyright/overloads_evaluation.toml | 3 +++ conformance/results/pyright/version.toml | 2 +- conformance/results/pytype/overloads_evaluation.toml | 8 +++++++- conformance/results/pytype/version.toml | 2 +- conformance/results/results.html | 12 ++++++------ conformance/tests/overloads_evaluation.py | 4 ++-- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index 9e6527ca..1012f096 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.14.1" -test_duration = 1.8 +test_duration = 1.7 diff --git a/conformance/results/pyre/overloads_evaluation.toml b/conformance/results/pyre/overloads_evaluation.toml index 7b950d72..9a848686 100644 --- a/conformance/results/pyre/overloads_evaluation.toml +++ b/conformance/results/pyre/overloads_evaluation.toml @@ -19,7 +19,7 @@ Line 147: Unexpected errors ['overloads_evaluation.py:147:29 Incompatible parame Line 148: Unexpected errors ['overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] Line 169: Unexpected errors ['overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`.'] Line 170: Unexpected errors ['overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`.'] -Line 196: Unexpected errors ['overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`.'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `str`.'] Line 224: Unexpected errors ['overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`.'] """ output = """ @@ -38,6 +38,6 @@ overloads_evaluation.py:147:29 Incompatible parameter type [6]: In call `expand_ overloads_evaluation.py:148:4 Assert type [70]: Expected `Union[int, str]` but got `int`. overloads_evaluation.py:169:24 Incompatible parameter type [6]: In call `expand_tuple`, for 1st positional argument, expected `Tuple[int, int]` but got `Tuple[int, Union[int, str]]`. overloads_evaluation.py:170:4 Assert type [70]: Expected `Union[int, str]` but got `int`. -overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `typing_extensions.Literal[0]`. +overloads_evaluation.py:196:4 Assert type [70]: Expected `int` but got `str`. overloads_evaluation.py:224:4 Assert type [70]: Expected `typing.Any` but got `int`. """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index cd4e9e46..2904f5c8 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ version = "pyre 0.9.23" -test_duration = 6.7 +test_duration = 6.0 diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml index 8684e650..15c1a772 100644 --- a/conformance/results/pyright/overloads_evaluation.toml +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -3,6 +3,7 @@ notes = """ Does not expand boolean arguments to Literal[True] and Literal[False]. Does not expand enum arguments to literal variants. Does not expand tuple arguments to possible combinations. +Does not prefer variadic match to indeterminate-length unpacked argument. """ conformance_automated = "Fail" errors_diff = """ @@ -12,6 +13,7 @@ Line 129: Unexpected errors ['overloads_evaluation.py:129:12 - error: No overloa Line 130: Unexpected errors ['overloads_evaluation.py:130:17 - error: "assert_type" mismatch: expected "Literal[0, 1]" but received "Unknown" (reportAssertTypeFailure)'] Line 169: Unexpected errors ['overloads_evaluation.py:169:12 - error: No overloads for "expand_tuple" match the provided arguments (reportCallIssue)', 'overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int | str]" cannot be assigned to parameter "x" of type "tuple[int, str]" in function "expand_tuple"'] Line 170: Unexpected errors ['overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure)'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:17 - error: "assert_type" mismatch: expected "int" but received "str" (reportAssertTypeFailure)'] """ output = """ overloads_evaluation.py:32:1 - error: No overloads for "example1" match the provided arguments @@ -40,4 +42,5 @@ overloads_evaluation.py:169:29 - error: Argument of type "tuple[Literal[1], int   Type "int | str" is not assignable to type "str"     "int" is not assignable to "str" (reportArgumentType) overloads_evaluation.py:170:17 - error: "assert_type" mismatch: expected "int | str" but received "Unknown" (reportAssertTypeFailure) +overloads_evaluation.py:196:17 - error: "assert_type" mismatch: expected "int" but received "str" (reportAssertTypeFailure) """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index 324d1870..1ae5a35b 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ version = "pyright 1.1.391" -test_duration = 1.7 +test_duration = 1.5 diff --git a/conformance/results/pytype/overloads_evaluation.toml b/conformance/results/pytype/overloads_evaluation.toml index c9df7902..004eea70 100644 --- a/conformance/results/pytype/overloads_evaluation.toml +++ b/conformance/results/pytype/overloads_evaluation.toml @@ -1,8 +1,8 @@ conformant = "Partial" notes = """ -Does not pick a winning overload based on arity, prior to considering argument types. Does not perform argument expansion (on any types) when matching overloads. Does not treat multiple matches due to gradual types as ambiguous. +Does not prefer variadic match to indeterminate-length unpacked argument. """ conformance_automated = "Fail" errors_diff = """ @@ -21,6 +21,7 @@ Line 148: Unexpected errors ['overloads_evaluation.py:148:5: \\x1b[1m\\x1b[31mer Line 166: Unexpected errors ['overloads_evaluation.py:166:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in expand_tuple: bad return type [bad-return-type]'] Line 170: Unexpected errors ['overloads_evaluation.py:170:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_expand_tuple: int [assert-type]'] Line 188: Unexpected errors ['overloads_evaluation.py:188:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in variadic: bad return type [bad-return-type]'] +Line 196: Unexpected errors ['overloads_evaluation.py:196:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_variadic: str [assert-type]'] Line 217: Unexpected errors ['overloads_evaluation.py:217:12: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in example4: bad return type [bad-return-type]'] Line 224: Unexpected errors ['overloads_evaluation.py:224:5: \\x1b[1m\\x1b[31merror\\x1b[39m\\x1b[0m: in check_example4: int [assert-type]'] """ @@ -117,6 +118,11 @@ overloads_evaluation.py:188:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in return 1 \u001b[1m\u001b[31m~\u001b[39m\u001b[0m +overloads_evaluation.py:196:5: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in check_variadic: str [assert-type] + + assert_type(ret1, int) + \u001b[1m\u001b[31m~~~~~~~~~~~~~~~~~~~~~~\u001b[39m\u001b[0m + overloads_evaluation.py:217:12: \u001b[1m\u001b[31merror\u001b[39m\u001b[0m: in example4: bad return type [bad-return-type] return 1 diff --git a/conformance/results/pytype/version.toml b/conformance/results/pytype/version.toml index 1da94b08..ce6c9dce 100644 --- a/conformance/results/pytype/version.toml +++ b/conformance/results/pytype/version.toml @@ -1,2 +1,2 @@ version = "pytype 2024.10.11" -test_duration = 31.1 +test_duration = 30.8 diff --git a/conformance/results/results.html b/conformance/results/results.html index 9a6bc90e..c55cd345 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,16 +159,16 @@

Python Type System Conformance Test Results

 
mypy 1.14.1
-
1.7sec
+
1.8sec
pyright 1.1.391
-
1.6sec
+
1.7sec
pyre 0.9.23
-
6.1sec
+
6.7sec
pytype 2024.10.11
31.1sec
@@ -691,7 +691,7 @@

Python Type System Conformance Test Results

Fail

Does not enforce any of the specified rules regarding overload definitions.

     overloads_evaluation
Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Does not treat multiple matches due to gradual types as ambiguous.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

Does not treat multiple matches due to gradual types as ambiguous.

- + - + diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py index fc6ce698..1c14cbd2 100644 --- a/conformance/tests/overloads_evaluation.py +++ b/conformance/tests/overloads_evaluation.py @@ -177,11 +177,11 @@ def check_expand_tuple(v: int | str) -> None: # > If so, eliminate overloads that do not have a variadic parameter. @overload -def variadic(x: int, /) -> Literal[0]: +def variadic(x: int, /) -> str: ... @overload -def variadic(*args: int) -> int: +def variadic(x: int, y: int, /, *args: int) -> int: ... def variadic(*args: int) -> int | str: From d4143860a02b827637db73b41931d7946f669a59 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 13 Feb 2025 20:56:54 -0800 Subject: [PATCH 36/38] Apply suggestions from code review Co-authored-by: Alex Waygood --- docs/spec/overload.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index c23d875b..5accc990 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -159,7 +159,7 @@ that it is consistent with all of its associated overload signatures. The implementation should accept all potential sets of arguments that are accepted by the overloads and should produce all potential return types produced by the overloads. In typing terms, this means the input -signature of the implementation should be :term: to the input +signature of the implementation should be :term:`assignable` to the input signatures of all overloads, and the return type of all overloads should be assignable to the return type of the implementation. @@ -174,7 +174,7 @@ should report an error:: # This implementation is inconsistent with the second overload # because it does not accept a keyword argument ``x`` and the # the overload's return type ``int`` is not assignable to the - implementation's return type ``str``. + # implementation's return type ``str``. def func(x: int | str, /) -> str: return str(x) @@ -244,7 +244,7 @@ When a type checker evaluates the call of an overloaded function, it attempts to "match" the supplied arguments with one or more overloads. This section describes the algorithm that type checkers should use for overload matching. This algorithm should be applied even in the -presence of :ref:. +presence of :ref:`overlapping overloads`. Only the overloads (the ``@overload``-decorated signatures) should be considered for matching purposes. The implementation, if provided, @@ -331,7 +331,7 @@ because there are possible materializations of ``list[Any]`` (for example, Once this filtering process is applied for all arguments, examine the return types of the remaining overloads. If these return types include type variables, they should be replaced with their solved types. If the resulting return types -for all remaining overloads are :term:, proceed to step 6. +for all remaining overloads are :term:`equivalent`, proceed to step 6. If the return types are not equivalent, overload matching is ambiguous. In this case, assume a return type of ``Any`` and stop. @@ -437,21 +437,21 @@ a union of a finite set of subtypes should be expanded into its constituent subtypes. This includes the following cases. 1. Explicit unions: Each subtype of the union should be considered as a -separate argument type. For example, the type ``int | str`` should be expanded -into ``int`` and ``str``. + separate argument type. For example, the type ``int | str`` should be expanded + into ``int`` and ``str``. 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should -be expanded into their literal members. + be expanded into their literal members. 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. 5. Tuples of known length that contain expandable types should be expanded -into all possible combinations of their element types. For example, the type -``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, -``(int, Literal[False])``, ``(str, Literal[True])``, and -``(str, Literal[False])``. + into all possible combinations of their element types. For example, the type + ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, + ``(int, Literal[False])``, ``(str, Literal[True])``, and + ``(str, Literal[False])``. The above list may not be exhaustive, and additional cases may be added in the future as the type system evolves. From f4293e8a33ba209d37006d949011cc623784d7a9 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 14 Feb 2025 11:43:17 +0000 Subject: [PATCH 37/38] Update conformance/tests/overloads_consistency.py --- conformance/tests/overloads_consistency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py index 559fe114..e6c0614a 100644 --- a/conformance/tests/overloads_consistency.py +++ b/conformance/tests/overloads_consistency.py @@ -9,7 +9,7 @@ # > The implementation should accept all potential sets of arguments # > that are accepted by the overloads and should produce all potential return # > types produced by the overloads. In typing terms, this means the input -# > signature of the implementation should be :term: to the input +# > signature of the implementation should be :term:`assignable` to the input # > signatures of all overloads, and the return type of all overloads should be # > assignable to the return type of the implementation. From e835221469f881e40d0948c14c684fa09417d8e0 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 14 Feb 2025 10:25:58 -0800 Subject: [PATCH 38/38] 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. --- conformance/tests/overloads_overlap.py | 40 -------------------- docs/spec/overload.rst | 52 -------------------------- 2 files changed, 92 deletions(-) delete mode 100644 conformance/tests/overloads_overlap.py diff --git a/conformance/tests/overloads_overlap.py b/conformance/tests/overloads_overlap.py deleted file mode 100644 index b65fa871..00000000 --- a/conformance/tests/overloads_overlap.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Tests checks for overlapping overloads. -""" - -from typing import Literal, overload - -# > If two overloads can accept the same set of arguments, they are said -# > to "partially overlap". If two overloads partially overlap, the return type -# > of the former overload should be assignable to the return type of the -# > latter overload. If this condition doesn't hold, it is indicative of a -# > programming error and should be reported by type checkers. - -@overload -def is_one(x: Literal[1]) -> Literal[True]: # E: overlapping overloads, inconsistent return type - ... - -@overload -def is_one(x: int) -> Literal[False]: - ... - -def is_one(x: int) -> bool: - return x == 1 - - -# > If all possible sets of arguments accepted by an overload are also always -# > accepted by an earlier overload, the two overloads are said to "fully overlap". -# > In this case, the latter overload will never be used. This condition -# > is indicative of a programming error and should be reported by type -# > checkers. - -@overload -def full_overlap(x: bool) -> bool: - ... - -@overload -def full_overlap(x: Literal[False]) -> int: # E: overload will never be used due to full overlap - ... - -def full_overlap(x: bool) -> int: - return 1 diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst index 5accc990..d6815573 100644 --- a/docs/spec/overload.rst +++ b/docs/spec/overload.rst @@ -185,58 +185,6 @@ implementation body, the use of ``async def``, and the presence of additional decorators. -Overlapping overloads -^^^^^^^^^^^^^^^^^^^^^ - -If two overloads can accept the same set of arguments, they are said -to "partially overlap". If two overloads partially overlap, the return type -of the former overload should be assignable to the return type of the -latter overload. If this condition doesn't hold, it is indicative of a -programming error and should be reported by type checkers. The purpose of -this check is to prevent unsoundness of this form:: - - @overload - def is_one(x: Literal[1]) -> Literal[True]: ... - @overload - def is_one(x: int) -> Literal[False]: ... - - reveal_type(is_one(int(1))) # Reveals Literal[False], but True at runtime - -Type checkers may exempt certain magic methods from the above check -for conditions that are mandated by their usage in the runtime. For example, -the ``__get__`` method of a descriptor is often defined using overloads -that would partially overlap if the above rule is enforced. - -Type checkers may ignore the possibility of multiple inheritance or -intersections involving structural types for purposes of computing overlap. -In the following example, classes ``A`` and ``B`` could theoretically overlap -because there could be a common type ``C`` that derives from both ``A`` and -``B``, but type checkers may choose not to flag this as an overlapping -overload:: - - class A: ... - class B: ... - - @overload - def func(x: A) -> int: ... - @overload - def func(x: B) -> str: ... - -If all possible sets of arguments accepted by an overload are also always -accepted by an earlier overload, the two overloads are said to "fully overlap". -In this case, the latter overload will never be used. This condition -is indicative of a programming error and should be reported by type -checkers:: - - # These overloads fully overlap because the first overload - # accepts all arguments accepted by the second overload. - - @overload - def func[T](x: T) -> T: ... - @overload - def func(x: int) -> int: ... - - Overload call evaluation ^^^^^^^^^^^^^^^^^^^^^^^^
 
mypy 1.14.1
-
1.8sec
+
1.7sec
pyright 1.1.391
-
1.7sec
+
1.5sec
pyre 0.9.23
-
6.7sec
+
6.0sec
pytype 2024.10.11
-
31.1sec
+
30.8sec
@@ -692,9 +692,9 @@

Python Type System Conformance Test Results

     overloads_evaluation
Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand type[A | B] to type[A] and type[B].

Does not expand tuple arguments to possible combinations.

Does not prefer variadic match to indeterminate-length unpacked argument.

Does not treat multiple matches due to gradual types as ambiguous.

Partial

Does not pick a winning overload based on arity, prior to considering argument types.

Does not perform argument expansion (on any types) when matching overloads.

Does not treat multiple matches due to gradual types as ambiguous.

Partial

Does not perform argument expansion (on any types) when matching overloads.

Does not treat multiple matches due to gradual types as ambiguous.

Does not prefer variadic match to indeterminate-length unpacked argument.

     overloads_overlap Pass