Skip to content

Commit 63c414a

Browse files
authored
Marks enums with values as implicitly final (#11247)
Closes #10857
1 parent f4a21a4 commit 63c414a

File tree

2 files changed

+270
-4
lines changed

2 files changed

+270
-4
lines changed

mypy/semanal.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
get_nongen_builtins, get_member_expr_fullname, REVEAL_TYPE,
7777
REVEAL_LOCALS, is_final_node, TypedDictExpr, type_aliases_source_versions,
7878
EnumCallExpr, RUNTIME_PROTOCOL_DECOS, FakeExpression, Statement, AssignmentExpr,
79-
ParamSpecExpr, EllipsisExpr
79+
ParamSpecExpr, EllipsisExpr,
80+
FuncBase, implicit_module_attrs,
8081
)
8182
from mypy.tvar_scope import TypeVarLikeScope
8283
from mypy.typevars import fill_typevars
@@ -95,7 +96,6 @@
9596
)
9697
from mypy.typeops import function_type
9798
from mypy.type_visitor import TypeQuery
98-
from mypy.nodes import implicit_module_attrs
9999
from mypy.typeanal import (
100100
TypeAnalyser, analyze_type_alias, no_subscript_builtin_alias,
101101
TypeVarLikeQuery, TypeVarLikeList, remove_dups, has_any_from_unimported_type,
@@ -1096,8 +1096,8 @@ def analyze_class(self, defn: ClassDef) -> None:
10961096
self.update_metaclass(defn)
10971097

10981098
bases = defn.base_type_exprs
1099-
bases, tvar_defs, is_protocol = self.clean_up_bases_and_infer_type_variables(defn, bases,
1100-
context=defn)
1099+
bases, tvar_defs, is_protocol = self.clean_up_bases_and_infer_type_variables(
1100+
defn, bases, context=defn)
11011101

11021102
for tvd in tvar_defs:
11031103
if any(has_placeholder(t) for t in [tvd.upper_bound] + tvd.values):
@@ -1521,6 +1521,19 @@ def configure_base_classes(self,
15211521
elif isinstance(base, Instance):
15221522
if base.type.is_newtype:
15231523
self.fail('Cannot subclass "NewType"', defn)
1524+
if (
1525+
base.type.is_enum
1526+
and base.type.fullname not in (
1527+
'enum.Enum', 'enum.IntEnum', 'enum.Flag', 'enum.IntFlag')
1528+
and base.type.names
1529+
and any(not isinstance(n.node, (FuncBase, Decorator))
1530+
for n in base.type.names.values())
1531+
):
1532+
# This means that are trying to subclass a non-default
1533+
# Enum class, with defined members. This is not possible.
1534+
# In runtime, it will raise. We need to mark this type as final.
1535+
# However, methods can be defined on a type: only values can't.
1536+
base.type.is_final = True
15241537
base_types.append(base)
15251538
elif isinstance(base, AnyType):
15261539
if self.options.disallow_subclassing_any:

test-data/unit/check-enum.test

+253
Original file line numberDiff line numberDiff line change
@@ -1391,3 +1391,256 @@ class Foo(Enum):
13911391
x = 3
13921392
x = 4
13931393
[builtins fixtures/bool.pyi]
1394+
1395+
[case testEnumImplicitlyFinalForSubclassing]
1396+
from enum import Enum, IntEnum, Flag, IntFlag
1397+
1398+
class NonEmptyEnum(Enum):
1399+
x = 1
1400+
class NonEmptyIntEnum(IntEnum):
1401+
x = 1
1402+
class NonEmptyFlag(Flag):
1403+
x = 1
1404+
class NonEmptyIntFlag(IntFlag):
1405+
x = 1
1406+
1407+
class ErrorEnumWithValue(NonEmptyEnum): # E: Cannot inherit from final class "NonEmptyEnum"
1408+
x = 1
1409+
class ErrorIntEnumWithValue(NonEmptyIntEnum): # E: Cannot inherit from final class "NonEmptyIntEnum"
1410+
x = 1
1411+
class ErrorFlagWithValue(NonEmptyFlag): # E: Cannot inherit from final class "NonEmptyFlag"
1412+
x = 1
1413+
class ErrorIntFlagWithValue(NonEmptyIntFlag): # E: Cannot inherit from final class "NonEmptyIntFlag"
1414+
x = 1
1415+
1416+
class ErrorEnumWithoutValue(NonEmptyEnum): # E: Cannot inherit from final class "NonEmptyEnum"
1417+
pass
1418+
class ErrorIntEnumWithoutValue(NonEmptyIntEnum): # E: Cannot inherit from final class "NonEmptyIntEnum"
1419+
pass
1420+
class ErrorFlagWithoutValue(NonEmptyFlag): # E: Cannot inherit from final class "NonEmptyFlag"
1421+
pass
1422+
class ErrorIntFlagWithoutValue(NonEmptyIntFlag): # E: Cannot inherit from final class "NonEmptyIntFlag"
1423+
pass
1424+
[builtins fixtures/bool.pyi]
1425+
1426+
[case testSubclassingNonFinalEnums]
1427+
from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta
1428+
1429+
def decorator(func):
1430+
return func
1431+
1432+
class EmptyEnum(Enum):
1433+
pass
1434+
class EmptyIntEnum(IntEnum):
1435+
pass
1436+
class EmptyFlag(Flag):
1437+
pass
1438+
class EmptyIntFlag(IntFlag):
1439+
pass
1440+
class EmptyEnumMeta(EnumMeta):
1441+
pass
1442+
1443+
class NonEmptyEnumSub(EmptyEnum):
1444+
x = 1
1445+
class NonEmptyIntEnumSub(EmptyIntEnum):
1446+
x = 1
1447+
class NonEmptyFlagSub(EmptyFlag):
1448+
x = 1
1449+
class NonEmptyIntFlagSub(EmptyIntFlag):
1450+
x = 1
1451+
class NonEmptyEnumMetaSub(EmptyEnumMeta):
1452+
x = 1
1453+
1454+
class EmptyEnumSub(EmptyEnum):
1455+
def method(self) -> None: pass
1456+
@decorator
1457+
def other(self) -> None: pass
1458+
class EmptyIntEnumSub(EmptyIntEnum):
1459+
def method(self) -> None: pass
1460+
class EmptyFlagSub(EmptyFlag):
1461+
def method(self) -> None: pass
1462+
class EmptyIntFlagSub(EmptyIntFlag):
1463+
def method(self) -> None: pass
1464+
class EmptyEnumMetaSub(EmptyEnumMeta):
1465+
def method(self) -> None: pass
1466+
1467+
class NestedEmptyEnumSub(EmptyEnumSub):
1468+
x = 1
1469+
class NestedEmptyIntEnumSub(EmptyIntEnumSub):
1470+
x = 1
1471+
class NestedEmptyFlagSub(EmptyFlagSub):
1472+
x = 1
1473+
class NestedEmptyIntFlagSub(EmptyIntFlagSub):
1474+
x = 1
1475+
class NestedEmptyEnumMetaSub(EmptyEnumMetaSub):
1476+
x = 1
1477+
[builtins fixtures/bool.pyi]
1478+
1479+
[case testEnumExplicitlyAndImplicitlyFinal]
1480+
from typing import final
1481+
from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta
1482+
1483+
@final
1484+
class EmptyEnum(Enum):
1485+
pass
1486+
@final
1487+
class EmptyIntEnum(IntEnum):
1488+
pass
1489+
@final
1490+
class EmptyFlag(Flag):
1491+
pass
1492+
@final
1493+
class EmptyIntFlag(IntFlag):
1494+
pass
1495+
@final
1496+
class EmptyEnumMeta(EnumMeta):
1497+
pass
1498+
1499+
class EmptyEnumSub(EmptyEnum): # E: Cannot inherit from final class "EmptyEnum"
1500+
pass
1501+
class EmptyIntEnumSub(EmptyIntEnum): # E: Cannot inherit from final class "EmptyIntEnum"
1502+
pass
1503+
class EmptyFlagSub(EmptyFlag): # E: Cannot inherit from final class "EmptyFlag"
1504+
pass
1505+
class EmptyIntFlagSub(EmptyIntFlag): # E: Cannot inherit from final class "EmptyIntFlag"
1506+
pass
1507+
class EmptyEnumMetaSub(EmptyEnumMeta): # E: Cannot inherit from final class "EmptyEnumMeta"
1508+
pass
1509+
1510+
@final
1511+
class NonEmptyEnum(Enum):
1512+
x = 1
1513+
@final
1514+
class NonEmptyIntEnum(IntEnum):
1515+
x = 1
1516+
@final
1517+
class NonEmptyFlag(Flag):
1518+
x = 1
1519+
@final
1520+
class NonEmptyIntFlag(IntFlag):
1521+
x = 1
1522+
@final
1523+
class NonEmptyEnumMeta(EnumMeta):
1524+
x = 1
1525+
1526+
class ErrorEnumWithoutValue(NonEmptyEnum): # E: Cannot inherit from final class "NonEmptyEnum"
1527+
pass
1528+
class ErrorIntEnumWithoutValue(NonEmptyIntEnum): # E: Cannot inherit from final class "NonEmptyIntEnum"
1529+
pass
1530+
class ErrorFlagWithoutValue(NonEmptyFlag): # E: Cannot inherit from final class "NonEmptyFlag"
1531+
pass
1532+
class ErrorIntFlagWithoutValue(NonEmptyIntFlag): # E: Cannot inherit from final class "NonEmptyIntFlag"
1533+
pass
1534+
class ErrorEnumMetaWithoutValue(NonEmptyEnumMeta): # E: Cannot inherit from final class "NonEmptyEnumMeta"
1535+
pass
1536+
[builtins fixtures/bool.pyi]
1537+
1538+
[case testEnumFinalSubtypingEnumMetaSpecialCase]
1539+
from enum import EnumMeta
1540+
# `EnumMeta` types are not `Enum`s
1541+
class SubMeta(EnumMeta):
1542+
x = 1
1543+
class SubSubMeta(SubMeta):
1544+
x = 2
1545+
[builtins fixtures/bool.pyi]
1546+
1547+
[case testEnumFinalSubtypingOverloadedSpecialCase]
1548+
from typing import overload
1549+
from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta
1550+
1551+
class EmptyEnum(Enum):
1552+
@overload
1553+
def method(self, arg: int) -> int:
1554+
pass
1555+
@overload
1556+
def method(self, arg: str) -> str:
1557+
pass
1558+
def method(self, arg):
1559+
pass
1560+
class EmptyIntEnum(IntEnum):
1561+
@overload
1562+
def method(self, arg: int) -> int:
1563+
pass
1564+
@overload
1565+
def method(self, arg: str) -> str:
1566+
pass
1567+
def method(self, arg):
1568+
pass
1569+
class EmptyFlag(Flag):
1570+
@overload
1571+
def method(self, arg: int) -> int:
1572+
pass
1573+
@overload
1574+
def method(self, arg: str) -> str:
1575+
pass
1576+
def method(self, arg):
1577+
pass
1578+
class EmptyIntFlag(IntFlag):
1579+
@overload
1580+
def method(self, arg: int) -> int:
1581+
pass
1582+
@overload
1583+
def method(self, arg: str) -> str:
1584+
pass
1585+
def method(self, arg):
1586+
pass
1587+
class EmptyEnumMeta(EnumMeta):
1588+
@overload
1589+
def method(self, arg: int) -> int:
1590+
pass
1591+
@overload
1592+
def method(self, arg: str) -> str:
1593+
pass
1594+
def method(self, arg):
1595+
pass
1596+
1597+
class NonEmptyEnumSub(EmptyEnum):
1598+
x = 1
1599+
class NonEmptyIntEnumSub(EmptyIntEnum):
1600+
x = 1
1601+
class NonEmptyFlagSub(EmptyFlag):
1602+
x = 1
1603+
class NonEmptyIntFlagSub(EmptyIntFlag):
1604+
x = 1
1605+
class NonEmptyEnumMetaSub(EmptyEnumMeta):
1606+
x = 1
1607+
[builtins fixtures/bool.pyi]
1608+
1609+
[case testEnumFinalSubtypingMethodAndValueSpecialCase]
1610+
from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta
1611+
1612+
def decorator(func):
1613+
return func
1614+
1615+
class NonEmptyEnum(Enum):
1616+
x = 1
1617+
def method(self) -> None: pass
1618+
@decorator
1619+
def other(self) -> None: pass
1620+
class NonEmptyIntEnum(IntEnum):
1621+
x = 1
1622+
def method(self) -> None: pass
1623+
class NonEmptyFlag(Flag):
1624+
x = 1
1625+
def method(self) -> None: pass
1626+
class NonEmptyIntFlag(IntFlag):
1627+
x = 1
1628+
def method(self) -> None: pass
1629+
1630+
class ErrorEnumWithoutValue(NonEmptyEnum): # E: Cannot inherit from final class "NonEmptyEnum"
1631+
pass
1632+
class ErrorIntEnumWithoutValue(NonEmptyIntEnum): # E: Cannot inherit from final class "NonEmptyIntEnum"
1633+
pass
1634+
class ErrorFlagWithoutValue(NonEmptyFlag): # E: Cannot inherit from final class "NonEmptyFlag"
1635+
pass
1636+
class ErrorIntFlagWithoutValue(NonEmptyIntFlag): # E: Cannot inherit from final class "NonEmptyIntFlag"
1637+
pass
1638+
[builtins fixtures/bool.pyi]
1639+
1640+
[case testFinalEnumWithClassDef]
1641+
from enum import Enum
1642+
1643+
class A(Enum):
1644+
class Inner: pass
1645+
class B(A): pass # E: Cannot inherit from final class "A"
1646+
[builtins fixtures/bool.pyi]

0 commit comments

Comments
 (0)