Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error for lambda containing Any even when type can be inferred #5879

Open
keis opened this issue Nov 9, 2018 · 4 comments · May be fixed by #12729
Open

Error for lambda containing Any even when type can be inferred #5879

keis opened this issue Nov 9, 2018 · 4 comments · May be fixed by #12729
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-0-high topic-disallow-any The disallow-any-* family of flags topic-usability

Comments

@keis
Copy link

keis commented Nov 9, 2018

Using mypy 0.641 with the --disallow-any-expr flag and this sample using @overload

from typing import Callable, Tuple, TypeVar, overload

T = TypeVar('T')
A0 = TypeVar('A0')
A1 = TypeVar('A1')


@overload
def foo(
    vars: Tuple[A0],
    op: Callable[[A0], T]
) -> T: ...


@overload
def foo(
    vars: Tuple[A0, A1],
    op: Callable[[A0, A1], T]
) -> T: ...


def foo(vars, op):
    pass


def foo_not_overloaded(
    vars: Tuple[A0, A1],
    op: Callable[[A0, A1], T]
) -> T:
    pass


a = 5
b = 3

foo((a, b), lambda a, b: a - b)

produces

typist2.py:36: error: Expression type contains "Any" (has type "Callable[[Any, Any], Any]")
typist2.py:36: error: Expression has type "Any"

However mypy seems to be able to infer the type of the lambda because if I modify the last line to read

foo((a, b), lambda a, b: a - b.bad)

mypy correctly identifies that "int" has no attribute "bad", sprinkling some prints on the mypy source also reveals it figures out it's a (int, int) -> int at some point.

foo_not_overloaded checks without trouble and so does using a plain def instead of the lambda.

@ilevkivskyi
Copy link
Member

Unfortunately we received several complains about false positives with this flag recently. My guess is this bug is tightly related to #5843, not exactly a duplicate, but the underlying reason is most likely the same.

@ilevkivskyi ilevkivskyi added bug mypy got something wrong priority-1-normal topic-usability false-positive mypy gave an error on correct code labels Nov 18, 2018
@justinpawela
Copy link

This seems to be a regression between 0.630 and 0.641. I have a few scripts that make liberal use of lambda statements, and it seems to be a problem with all of them now.

Here is my best attempt to isolate an example from my codebase:

import datetime as dt
import typing as t


class DeploymentResponse(t.NamedTuple):
    uid: str
    createdDate: dt.datetime


class DeploymentListResponse(t.NamedTuple):
    items: t.List[DeploymentResponse]


def get_deployments() -> DeploymentListResponse: ...

def prune_deployments(IDs: t.Iterable[str]) -> None: ...


def prune_api_deployments(keep_latest: bool, keep_IDs: t.Iterable[str]) -> None:
    response: DeploymentListResponse = get_deployments()

    obsolete_IDs = { d.uid for d in response.items }.difference(keep_IDs)

    # Works fine with mypy 0.630 but produces errors with 0.641 (see below).
    if keep_latest:
        obsolete_IDs.discard( max(response.items, key=lambda i: i.createdDate).uid )

    # Works fine in both 0.630 and 0.641 because the lambda does not need to be
    # inferred. This code is redundant, just for this example.
    if keep_latest:
        selector: t.Callable[[DeploymentResponse], dt.datetime] = lambda i: i.createdDate
        obsolete_IDs.discard( max(response.items, key=selector).uid )

    prune_deployments(IDs=obsolete_IDs)
    return

This code was tested on Python 3.6.1 using both mypy 0.630 and 0.641 with the --disallow-any-expr flag. There are no errors using 0.630, but 0.641 produces the following output:

test.py:26: error: Expression type contains "Any" (has type "Callable[[Any], Any]")
test.py:26: error: Expression has type "Any"

Of course, the workaround is obvious — don't make mypy infer the type of the lambda, but that gets very verbose and tiresome quickly, especially since lambda is usually quick and concise.

@DetachHead
Copy link
Contributor

what's interesting is that when using reveal_type within the lambda, it activates twice with two different results:

from typing import Callable, NoReturn, overload


@overload
def foo(value: str, predicate: Callable[[str], object]) -> None:
    ...


@overload
def foo(value: int, predicate: Callable[[int], object]) -> None:
    ...


def foo(value: int | str, predicate: Callable[[NoReturn], object]) -> None:
    ...


# error: Expression type contains "Any" (has type "Callable[[Any], Any]")  [misc]
# note: Revealed type is "Any"
# note: Revealed type is "builtins.int"
foo(1, lambda value: reveal_type(value))

https://mypy-play.net/?mypy=master&python=3.10&flags=show-error-codes%2Callow-redefinition%2Cstrict%2Ccheck-untyped-defs%2Cdisallow-any-decorated%2Cdisallow-any-expr%2Cdisallow-any-explicit%2Cdisallow-any-generics%2Cdisallow-any-unimported%2Cdisallow-incomplete-defs%2Cdisallow-subclassing-any%2Cdisallow-untyped-calls%2Cdisallow-untyped-decorators%2Cdisallow-untyped-defs%2Cno-implicit-optional%2Cno-implicit-reexport%2Cstrict-equality%2Cwarn-incomplete-stub%2Cwarn-redundant-casts%2Cwarn-return-any%2Cwarn-unreachable%2Cwarn-unused-configs%2Cwarn-unused-ignores&gist=2f59b7264cb0e77c39418bad595ba2e8

@KotlinIsland
Copy link
Contributor

This is resolved in basedmypy
https://mypy-play.net/?mypy=basedmypy-2.2.1&python=3.11&gist=66a86e78f129539cd320cd9d1a535a82

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-0-high topic-disallow-any The disallow-any-* family of flags topic-usability
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants