Skip to content

Narrowing inconsistency between == and "in" #17864

Closed
@JukkaL

Description

@JukkaL

Type narrowing behaves inconsistently:

def f(x: str | int) -> None:
    if x == "x":
        reveal_type(x)  # str | int
    if x in ["x"]:
        reveal_type(x)  # str

Both of these should infer the same type. This is a regression that was introduced in #17344 (cc @Jordandev678).

Also, unlike with ==, narrowing using "in" doesn't consider the possibility of a custom __eq__ method, so it can infer the wrong type:

class C:
    def __init__(self, x: int) -> None:
        self.x = x

    def __eq__(self, other: object) -> bool:
        if isinstance(other, C) and other.x == self.x:
            return True
        return NotImplemented

class D(C):
    pass

def f(x: C) -> None:
    if x in [D(5)]:
        print(type(x))  # C
        reveal_type(x)  # D

f(C(5))

I'm going to revert to #17344 since fixing the logic may be non-trivial, and there is also another related bug (#17841).

The original PR can be submitted again with a fix that uses the same logic we use for == narrowing. If there is desire to support more narrowing use cases, this should be addressed in a separate issue/PR, and both == and "in" narrowing should remain consistent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions