Description
Bug Report
Elements/members of an Enum
which have the same value (aliases or duplicate values) are treated as never-equal in mypy.
To Reproduce
mypy-play.net gist
# run with: --strict-equality --warn-unreachable
from __future__ import annotations
from enum import *
class TrafficLight(Enum):
RED = auto()
AMBER = auto() # (term often used in law)
GREEN = auto()
YELLOW = AMBER # alias (used in everyday language)
def demo1(inst: TrafficLight) -> None:
if inst is TrafficLight.AMBER:
reveal_type(inst) # mypy: ≈ Literal[AMBER] (ok)
else:
reveal_type(inst) # mypy: ≈ Literal[RED, GREEN, YELLOW]
# expected ≈ Literal[RED, GREEN]
def demo2() -> None:
if TrafficLight.AMBER is TrafficLight.YELLOW:
# this is true at runtime, but...
# mypy: Non-overlapping identity check (
# left operand type: "Literal[TrafficLight.AMBER]",
# right operand type: "Literal[TrafficLight.YELLOW]"
# )
# [comparison-overlap]
# expected: (no error)
_ = True # mypy: [unreachable]
# expected: (no error)
else:
_ = True # mypy: (no error)
# expected: [unreachable]
Expected Behavior
The tool should ideally understand that TrafficLight.AMBER
is indistinguishable from TrafficLight.YELLOW
, and that the latter is an alias of the former.
The tool should not issue a false positive under the incorrect assumption that Enum
members always have distinct values.
Actual Behavior
The tool assumes (incorrectly) that YELLOW
and AMBER
are never equal or the same.
Your Environment
- Mypy version used: 1.8.0
- Mypy command-line flags:
--strict-equality --warn-unreachable
- Mypy configuration options from
mypy.ini
(and other config files): none - Python version used: 3.8, 3.12
Existing bugs
None were found, open or closed, using the search terms (enum alias) and (enum duplicate).
Specifications and runtime behaviour
https://docs.python.org/3/howto/enum.html#duplicating-enum-members-and-values
[A]n enum member can have other names associated with it. Given two entries A and B with the same value (and A defined first), B is an alias for the member A. By-value lookup of the value of A will return the member A. By-name lookup of A will return the member A. By-name lookup of B will also return the member A[.]
At runtime, two Enum
elements with the same value, retrieved using dot-notation (attribute lookup), are exactly the same object.
Motivation
In my use case, the Enum
describes various scaled units (second, millisecond, microsecond, etc.); one of these is the "fundamental unit" in which quantities are stored (as an unqualified numeric value), while the others are converted to and from that unit by the read()
and write()
functions. The Enum
defines the fundamental unit twice, first as NANOSECOND
with FUNDAMENTAL
as an alias. Since the fundamental unit could change, the alias is useful.