Skip to content

Commit fd68e7b

Browse files
committed
add function maybe_mangled mainly for fixing the handling of "private" enum members
1 parent a04ca0a commit fd68e7b

File tree

8 files changed

+17
-13
lines changed

8 files changed

+17
-13
lines changed

mypy/checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@
210210
from mypy.types_utils import is_overlapping_none, remove_optional, store_argument_type, strip_type
211211
from mypy.typetraverser import TypeTraverserVisitor
212212
from mypy.typevars import fill_typevars, fill_typevars_with_any, has_no_typevars
213-
from mypy.util import is_dunder, is_sunder
213+
from mypy.util import maybe_mangled, is_dunder, is_sunder
214214
from mypy.visitor import NodeVisitor
215215

216216
T = TypeVar("T")
@@ -2711,7 +2711,7 @@ def is_final_enum_value(self, sym: SymbolTableNode, base: TypeInfo) -> bool:
27112711
# 4. If it is a method / descriptor like in `method = classmethod(func)`
27122712
name = sym.node.name
27132713
if (
2714-
(name.startswith(f"_{base.name}__") and not name.endswith("__"))
2714+
maybe_mangled(name, base.name)
27152715
or is_dunder(name)
27162716
or is_sunder(name)
27172717
# TODO: make sure that `x = @class/staticmethod(func)`

mypy/checkmember.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
get_proper_type,
7474
)
7575
from mypy.typetraverser import TypeTraverserVisitor
76+
from mypy.util import maybe_mangled, is_dunder
7677

7778
if TYPE_CHECKING: # import for forward declaration only
7879
import mypy.checker
@@ -1197,7 +1198,7 @@ def analyze_enum_class_attribute_access(
11971198
if name in EXCLUDED_ENUM_ATTRIBUTES:
11981199
return report_missing_attribute(mx.original_type, itype, name, mx)
11991200
# Dunders and private names are not Enum members
1200-
if name.startswith("__") and name.replace("_", "") != "":
1201+
if is_dunder(name) or maybe_mangled(name, itype.type.name):
12011202
return None
12021203

12031204
node = itype.type.get(name)

mypy/nodes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import mypy.strconv
1616
from mypy.options import Options
17-
from mypy.util import is_typeshed_file, short_type
17+
from mypy.util import maybe_mangled, is_dunder, is_typeshed_file, short_type
1818
from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor
1919

2020
if TYPE_CHECKING:
@@ -3237,7 +3237,7 @@ def enum_members(self) -> list[str]:
32373237
if (
32383238
isinstance(sym.node, Var)
32393239
and name not in EXCLUDED_ENUM_ATTRIBUTES
3240-
and not name.startswith("__")
3240+
and not (is_dunder(name) or maybe_mangled(name, self.name))
32413241
and sym.node.has_explicit_value
32423242
)
32433243
]

mypy/semanal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@
303303
)
304304
from mypy.types_utils import is_invalid_recursive_alias, store_argument_type
305305
from mypy.typevars import fill_typevars
306-
from mypy.util import correct_relative_import, is_dunder, module_prefix, unmangle, unnamed_function
306+
from mypy.util import correct_relative_import, maybe_mangled, is_dunder, module_prefix, unmangle, unnamed_function
307307
from mypy.visitor import NodeVisitor
308308

309309
T = TypeVar("T")
@@ -4292,7 +4292,7 @@ def analyze_name_lvalue(
42924292
kind == MDEF
42934293
and isinstance(self.type, TypeInfo)
42944294
and self.type.is_enum
4295-
and not name.startswith("__")
4295+
and not (is_dunder(name) or maybe_mangled(name, self.type.name))
42964296
):
42974297
# Special case: we need to be sure that `Enum` keys are unique.
42984298
if existing is not None and not isinstance(existing.node, PlaceholderNode):

mypy/typeanal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
)
115115
from mypy.types_utils import get_bad_type_type_item
116116
from mypy.typevars import fill_typevars
117+
from mypy.util import maybe_mangled
117118

118119
T = TypeVar("T")
119120

@@ -980,7 +981,7 @@ def analyze_unbound_type_without_type_info(
980981
isinstance(sym.node, Var)
981982
and sym.node.info
982983
and sym.node.info.is_enum
983-
and not sym.node.name.startswith("__")
984+
and not maybe_mangled(sym.node.name, sym.node.info.name)
984985
):
985986
value = sym.node.name
986987
base_enum_short_name = sym.node.info.name

mypy/util.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,17 @@ def is_dunder(name: str, exclude_special: bool = False) -> bool:
6262
"""
6363
if exclude_special and name in SPECIAL_DUNDERS:
6464
return False
65-
return name.startswith("__") and name.endswith("__")
65+
return name.startswith("__") and name.endswith("__") and name.replace("_", "")
6666

6767

6868
def is_sunder(name: str) -> bool:
6969
return not is_dunder(name) and name.startswith("_") and name.endswith("_")
7070

7171

72+
def maybe_mangled(member_name: str, type_name: str) -> bool:
73+
return member_name.startswith(f"_{type_name}__") and not member_name.endswith("__")
74+
75+
7276
def split_module_names(mod_name: str) -> list[str]:
7377
"""Return the module and all parent module names.
7478

test-data/unit/check-enum.test

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,8 +2260,7 @@ class MyEnum(Enum):
22602260
B = 2
22612261
__my_dict = {A: "ham", B: "spam"}
22622262

2263-
# TODO: change the next line to use MyEnum._MyEnum__my_dict when mypy implements name mangling
2264-
x: MyEnum = MyEnum.__my_dict # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum")
2263+
x: MyEnum = MyEnum._MyEnum__my_dict # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum")
22652264
[builtins fixtures/enum.pyi]
22662265

22672266
[case testEnumWithPrivateAttributeReachability]

test-data/unit/check-literal.test

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,8 +2506,7 @@ b: Literal[Color.BLUE]
25062506
bad1: Literal[Color] # E: Parameter 1 of Literal[...] is invalid
25072507
bad2: Literal[Color.func] # E: Parameter 1 of Literal[...] is invalid
25082508
bad3: Literal[Color.func()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
2509-
# TODO: change the next line to use Color._Color__ROUGE when mypy implements name mangling
2510-
bad4: Literal[Color.__ROUGE] # E: Parameter 1 of Literal[...] is invalid
2509+
bad4: Literal[Color._Color__ROUGE] # E: Parameter 1 of Literal[...] is invalid
25112510

25122511
def expects_color(x: Color) -> None: pass
25132512
def expects_red(x: Literal[Color.RED]) -> None: pass

0 commit comments

Comments
 (0)