mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Disable comparison overlap checks in assert statements

Open JukkaL opened this issue 1 year ago • 4 comments

Feature

In assert statements, allow comparisons that don't appear to overlap. These are common in test cases, and tend to generate false positives.

One way to implement this would be to filter out errors with the comparison-overlap error code in assert statements.

A potentially better way would be to not narrow down types in comparisons in assert statements, but this could be too complicated and ad hoc.

Example where we have a false positive:

# mypy: strict-equality
from enum import Enum

class MyEnum(Enum):
    X = 1
    Y = 2

class MyClass:
    attr: MyEnum = MyEnum.X

    def mutate(self) -> None:
        self.attr = MyEnum.Y

def test_foo() -> None:
    a = MyClass()
    assert a.attr == MyEnum.X
    a.mutate()
    assert a.attr == MyEnum.Y  # Error: Non-overlapping equality check

Pitch

These errors are often false positives, and they are somewhat frequent in test cases. The fix seems simple.

Hints

Here is an example of filtering errors adapted from mypy/plugins/default.py:

            with self.msg.filter_errors(
                filter_errors=lambda name, info: info.code != codes.TYPEDDICT_READONLY_MUTATED,
                save_filtered_errors=True,
            ):

JukkaL avatar Oct 08 '24 11:10 JukkaL

Could I be assigned this?

Kevan668 avatar Oct 10 '24 23:10 Kevan668

Feel free to send a PR if you're interested in working on this!

JelleZijlstra avatar Oct 10 '24 23:10 JelleZijlstra

Even in non-test code, a redundant assert can get optimized out anyway. And in non-optimized mode, it's almost certainly a quick sanity check. So I'm in favor of such a change for less false-positives.

Avasam avatar Oct 11 '24 02:10 Avasam

Wonder if there's anything we can do about the reachability implications, since mypy will currently just not check any subsequent code in the example given. And if you do happen to have --warn-unreachable on, you'll get a similar error (granted it doesn't get used that much since it's not currently part of --strict)

hauntsaninja avatar Oct 11 '24 03:10 hauntsaninja

I would like to add that in my opinion, there is a specific issue with the type being narrowed to a specific enum member. This does not happen with other types. As an analogy, I'll take the same code @JukkaL used with ints instead of enums (I'll even leave the Enum and make it an IntEnum:

# mypy: strict-equality
from enum import IntEnum

class MyEnum(IntEnum):
    X = 1
    Y = 2

class MyClass:
    attr: MyEnum = MyEnum.X
    num: int = 0

    def mutate(self) -> None:
        self.attr = MyEnum.Y
        self.num += 1

def test_foo() -> None:
    a = MyClass()
    assert a.attr == MyEnum.X
    assert a.num == 0
    a.mutate()
    assert a.attr == MyEnum.Y  # Error: Non-overlapping equality check
    assert a.num == 1  # No error here

I understand that from python's point of view each enum member is it's own type but when I annotate var: MyEnum What I mean is that var could be any one of the members of MyEnum.

YotamAlon avatar Dec 02 '24 12:12 YotamAlon