diff --git a/mypy/semanal.py b/mypy/semanal.py index 219459c92e3c..123e5a0ca0fc 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -309,7 +309,7 @@ ) from mypy.types_utils import is_invalid_recursive_alias, store_argument_type from mypy.typevars import fill_typevars -from mypy.util import correct_relative_import, is_dunder, module_prefix, unmangle, unnamed_function +from mypy.util import correct_relative_import, module_prefix, unmangle, unnamed_function from mypy.visitor import NodeVisitor T = TypeVar("T") @@ -3841,9 +3841,13 @@ def store_final_status(self, s: AssignmentStmt) -> None: and isinstance(cur_node.node, Var) and not (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs) ): - # Double underscored members are writable on an `Enum`. + # Double underscored names are writable on an `Enum`: + # - Dunders (__x__) are not enum members + # - Private names (__x) are not enum members (since Python 3.11) # (Except read-only `__members__` but that is handled in type checker) - cur_node.node.is_final = s.is_final_def = not is_dunder(cur_node.node.name) + cur_node.node.is_final = s.is_final_def = ( + not cur_node.node.name.startswith("__") + ) # Special case: deferred initialization of a final attribute in __init__. # In this case we just pretend this is a valid final definition to suppress diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index c05dfdef2bf7..15a0b80c3341 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -1602,6 +1602,20 @@ Medal.silver = 2 # E: Cannot assign to final attribute "silver" [builtins fixtures/enum.pyi] +[case testEnumPrivateNameNotFinal] +# Private names (__x) are not enum members and should not be final +from enum import Enum + +class MyEnum(Enum): + __config = "some_value" + A = 1 + B = 2 + +# __config is not a member so reassignment should be allowed +MyEnum._MyEnum__config = "other_value" +[builtins fixtures/enum.pyi] + + [case testEnumFinalValuesCannotRedefineValueProp] from enum import Enum class Types(Enum):