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

weird typeguard behavior in inspect.getmembers when disallow-any-expr is enabled #16025

Closed
DetachHead opened this issue Sep 3, 2023 · 5 comments · May be fixed by #16101
Closed

weird typeguard behavior in inspect.getmembers when disallow-any-expr is enabled #16025

DetachHead opened this issue Sep 3, 2023 · 5 comments · May be fixed by #16101
Labels
bug mypy got something wrong topic-disallow-any The disallow-any-* family of flags

Comments

@DetachHead
Copy link
Contributor

import inspect

class Foo:
    pass


def foo(value: bool) -> None:
    for _, method in inspect.getmembers(
        Foo, predicate=lambda method: method in vars(Foo) # type:ignore[misc,arg-type]
    ):
        reveal_type(method)  # bool???

playground

if i disable disallow-any-expr, the arg-type error goes away and the type of method changes from bool to Any

i don't think disallow-any-expr should change how types are inferred, or cause any new errors other than Expression type contains "Any"

@DetachHead DetachHead added the bug mypy got something wrong label Sep 3, 2023
@AlexWaygood AlexWaygood added the topic-disallow-any The disallow-any-* family of flags label Sep 3, 2023
@ikonst
Copy link
Contributor

ikonst commented Sep 4, 2023

Oh wow, this is quite tricky.

The first realization to understand is that lambda a: a is basically incompatible with --disallow-any-expr since a is an Any and there's no way to annotate it.

For example, mypy --disallow-any-expr will error on:

lst: list[int] = [1, 2, 3]
lst.sort(key=lambda a: a)

I think it's basically issue #5879.

Now, inspect.getmembers is an overloaded function, and the mechanism to determine which overload to use is by trying to check the call and seeing if there are no "new errors" (see code). Apparently "new errors" include not just the inspect.getmembers call expr, but also the nested lambda expr. If you change the lambda to a function, this no longer happens (mypy-play).

The weird bool is due to the first signature being arbitrarily picked in this fallback code. Basically it's the lambda's retval (bool) becomes the expression's.

There's many things to tackle here but maybe start with

  1. Fix how unrelated expr's errors end up rolling into the error watcher
  2. Maybe this kind of error try-catch should happen with some kind of "standard environment" rather than be dependent on options like --disallow-any-expr?

@KotlinIsland
Copy link
Contributor

I think #12729 would fix this

@KotlinIsland
Copy link
Contributor

KotlinIsland commented Sep 11, 2023

Minified - (🐞) Incorrect overload matching with lambda expression and disallow-any-expr

# mypy: disallow_any_expr
from typing import overload, Any, Callable

@overload
def f(fn: Callable[[object], bool]) -> str: ...
@overload
def f(fn: Callable[[Any], None]) -> int: ...
def f(fn: object = 1) -> object: ...

x = f(lambda _: None)
reveal_type(x)  # `str`, with `allow_any_expr` we get `int`

@KotlinIsland
Copy link
Contributor

@DetachHead DetachHead closed this as not planned Won't fix, can't repro, duplicate, stale Sep 27, 2023
@DetachHead
Copy link
Contributor Author

closing in favor of #16186

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-disallow-any The disallow-any-* family of flags
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants