Skip to content

False-positive errors w.r.t. max() #11588

Closed
@AlexWaygood

Description

@AlexWaygood

Bug Report

Mypy raises false-positive errors if you pass an object that only has a __gt__ method to max().

To Reproduce

Consider the following class definitions:

class OnlyKnowsDunderLT:
    def __init__(self, n):
        self.n = n
    def __lt__(self, other):
        return self.n < other.n
    def __repr__(self):
        return f'OnlyKnowsDunderLT(n={self.n})'
 
class OnlyKnowsDunderGT:
    def __init__(self, n):
        self.n = n
    def __gt__(self, other):
        return self.n > other.n
    def __repr__(self):
        return f'OnlyKnowsDunderGT(n={self.n})'

Here's how they behave with max() at runtime in Python 3.10:

>>> max(OnlyKnowsDunderLT(5), OnlyKnowsDunderLT(6))
OnlyKnowsDunderLT(n=6)
>>> max(OnlyKnowsDunderGT(3), OnlyKnowsDunderGT(8))
OnlyKnowsDunderGT(n=8)
>>> max(OnlyKnowsDunderLT(3), OnlyKnowsDunderGT(9))
OnlyKnowsDunderGT(n=9)

Expected Behavior

Mypy should raise no errors for code such as this, as it works fine at runtime and is perfectly type-safe.

Actual Behavior

Mypy raises errors for the following snippet of code, a typed equivalent to the untyped code above:

from typing import Protocol

class HasN(Protocol):
    n: int

class OnlyKnowsDunderLT:
    def __init__(self, n: int) -> None:
        self.n = n
    def __lt__(self, other: HasN) -> bool:
        return self.n < other.n
    def __repr__(self) -> str:
        return f'OnlyKnowsDunderLT(n={self.n})'
 
class OnlyKnowsDunderGT:
    def __init__(self, n: int) -> None:
        self.n = n
    def __gt__(self, other: HasN) -> bool:
        return self.n > other.n
    def __repr__(self) -> str:
        return f'OnlyKnowsDunderGT(n={self.n})'

max(OnlyKnowsDunderLT(5), OnlyKnowsDunderLT(6))
max(OnlyKnowsDunderGT(3), OnlyKnowsDunderGT(8)) # error: No overload variant of "max" matches argument types "OnlyKnowsDunderGT", "OnlyKnowsDunderGT"  [call-overload]
max(OnlyKnowsDunderLT(3), OnlyKnowsDunderGT(9)) # error: No overload variant of "max" matches argument types "OnlyKnowsDunderLT", "OnlyKnowsDunderGT"  [call-overload]

Over at typeshed, the stub for max() has just been changed. max() is now annotated as requiring arguments to have a __gt__ method rather than a __lt__ method, as this more accurately reflects the C code for max(). However, the only effect of this change is to "flip" the errors that mypy reports: it will now error out on the class that only has a __lt__ method, instead of the class that only has a __gt__ method.

One potential solution to this problem could be to further revise the typeshed stub. However, I have been advised by @srittau that this would be a suboptimal solution, as __gt__/__lt__ probably need to be special-cased by mypy and other type-checkers to some extent.

Your Environment

  • Mypy version used: 0.910
  • Mypy command-line flags: --strict, --show-error-codes (the same errors occur without the --strict flag)
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.10

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