mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Wrong revealed type of float/int multiplied/divided by sum(Iterable[complex]).

Open AndrewUshakov opened this issue 7 years ago • 3 comments

For the code below (Python 3.7.1) mypy 0.650 improperly reveals types of expressions: (float/int multiplied/divided by the sum(arg)), where arg is Iterable[complex] and generates false positive errors for the type of the first argument of the sum (they are in comments). Attempt to explicitly define 'start' parameter does not help. There are no problems with locally defined function f2 with similar signature.

Additional question is: what does mean '' in "Revealed type is 'builtins.complex'" in the line 13?

from typing import Iterable, TYPE_CHECKING, TypeVar

_TC = TypeVar('_TC', int, float, complex)


def f1(arg: Iterable[complex]) -> complex:
    if TYPE_CHECKING:

        reveal_type(f2(arg))                # Revealed type is 'builtins.complex' Ok.
        reveal_type(1/f2(arg))              # Revealed type is 'builtins.complex' Ok.
        reveal_type(1*f2(arg))              # Revealed type is 'builtins.complex' Ok.

        reveal_type(sum(arg))               # Revealed type is 'builtins.complex*' <- What does mean '*' at the end?

        reveal_type(1/sum(arg))             # Revealed type is 'builtins.float' ???
                                            # Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[int]" ???

        reveal_type(1*sum(arg))             # Revealed type is 'builtins.int' ???
                                            # Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[int]" ???

        reveal_type(1.0/sum(arg))           # Revealed type is 'builtins.float' ???
                                            # Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[float]" ???

        reveal_type(1.0*sum(arg))           # Revealed type is 'builtins.float' ???
                                            # Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[float]" ???

        reveal_type(1/sum(arg, start=complex(0.0)))  # Revealed type is 'builtins.float' ???
                                            # Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[int]" ???

        reveal_type(complex(1)/sum(arg))    # Revealed type is 'builtins.complex' <- workaround
        reveal_type(1/complex(sum(arg)))    # Revealed type is 'builtins.complex' <- workaround

    return 1/sum(arg)                       # error: Argument 1 to "sum" has incompatible type "Iterable[complex]"; expected "Iterable[int]"


def f2(arg: Iterable[_TC]) -> _TC:
    return 1/next(iter(arg))


if __name__ == '__main__':

    a = [1.0, 2.0+3.0j]
    print(f1(a))

AndrewUshakov avatar Dec 09 '18 13:12 AndrewUshakov

The * means it's an inferred type, not an explicitly specified type. It doesn't really matter unless you're interested in mypy internals.

JelleZijlstra avatar Dec 09 '18 16:12 JelleZijlstra

This is another example of the situation where mypy is too eager about return/outer type context. Mypy uses the external context first, in particular, in this example int.__mul__ annotated as (int) -> int, sets the int context for sum(...), so the type variable _T is inferred as int. Therefor you see a weird error like "Iterable[int]" expected. Since this is a well known problem, mypy special-cases functions that return a plain type variable (like f2() in your example). But the crux is that sum() is annotated in typeshed as returning Union[_T, int], so that special-casing doesn't apply to it, and you see the bug.

Possible short term solution is to annotate sum() as just returning _T, but the only long term solution is to use "single bin inference".

See also https://github.com/python/mypy/issues/5971

ilevkivskyi avatar Dec 09 '18 17:12 ilevkivskyi

Possible short term solution is to annotate sum() as just returning _T, but the only long term solution is to use "single bin inference".

How would I go about annotating sum()? Cheers

arthurazs avatar Jul 31 '22 07:07 arthurazs

This issue can be closed.

Everything about sum(...) works in the example above with the mypy 0.960 while a version 0.950 still was with same errors.

The only reported error remains in the function f2() but it is an incorrectly typed function, because 1 / 1 is a float not an int. If the the "int" is removed from the second line: _TC = TypeVar('_TC', float, complex) then everything works also with int, float and complex:

reveal_type(f2([1]))  # Revealed type is "builtins.float"

hynekcer avatar Aug 22 '22 14:08 hynekcer

@hauntsaninja This issue can be closed similarly to #8814 that you closed yesterday.

hynekcer avatar Aug 25 '22 12:08 hynekcer