python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.46k stars 2.83k forks source link

Fails to properly narrow types on enums that have an __init__ #11461

Open phidra opened 3 years ago

phidra commented 3 years ago

Bug Report

mypy behaves weirdly when narrowing type on an enum that has an __init__ (as described in the enum doc), whereas it succeeds with "regular" enums.

To Reproduce

  1. create an enum with __init__ (this is the example of the enum doc) :
class Planet(Enum):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS = (4.869e+24, 6.0518e6)

    def __init__(self, mass, radius):
        self.mass = mass
        self.radius = radius

    @property
    def surface_gravity(self) -> float:
        G = 6.67300E-11
        return G * self.mass / (self.radius * self.radius)
  1. narrow such an enum :
def get_gravity(x: Planet) -> float:
    reveal_type(x)  # Revealed type is 'main.Planet'
    if x is Planet.MERCURY:
        reveal_type(x)  # Revealed type is 'Literal[main.Planet.MERCURY]'
        return 0.0

   # the alleged bug is in the line below, as I expected :Revealed type is 'Literal[main.Planet.VENUS]'
    reveal_type(x)  # Revealed type is 'Union[Literal[main.Planet.VENUS], Literal[main.Planet.mass], Literal[main.Planet.radius]]'
    return x.surface_gravity

Expected Behavior

I'd have expected the same behaviour as with a "regular" enum :

class Planet(Enum):
    MERCURY = auto()
    VENUS = auto()

def get_gravity(x: Planet) -> float:
    reveal_type(x)  # Revealed type is 'main.Planet'
    if x is Planet.MERCURY:
        reveal_type(x)  # Revealed type is 'Literal[main.Planet.MERCURY]'
        return 0.0

    # here, the revealed type is as expected :
    reveal_type(x)  # Revealed type is 'Literal[main.Planet.VENUS]'
    return 1.0

Actual Behavior

Revealed type after the narrowing weirdly includes the attributes.

Note : as the problem seems to be related to how self is handled, maybe it is related to this issue, even if the situation is quite different ?

Your Environment

phidra commented 3 years ago

As a side note, even if the enum has an __init__, it is still impossible to add a new Planet :

a_brand_new_planet = Planet(5.5, 6.6)
# TypeError: Cannot extend enumerations
AlexWaygood commented 2 years ago

Similar to https://github.com/python/mypy/issues/10573