Skip to content

Commit e37398b

Browse files
committed
remove method is_private by mangling "__mypy-replace" and "__mypy-post_init".
1 parent 01d5c5a commit e37398b

File tree

4 files changed

+38
-36
lines changed

4 files changed

+38
-36
lines changed

mypy/checker.py

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ def check_func_def(
12491249
if (
12501250
arg_type.variance == COVARIANT
12511251
and defn.name not in ("__init__", "__new__", "__post_init__")
1252-
and not is_private(defn.name) # private methods are not inherited
1252+
and "mypy-" not in defn.name # skip internally added methods
12531253
):
12541254
ctx: Context = arg_type
12551255
if ctx.line < 0:
@@ -1881,7 +1881,6 @@ def check_explicit_override_decorator(
18811881
found_method_base_classes
18821882
and not defn.is_explicit_override
18831883
and defn.name not in ("__init__", "__new__")
1884-
and not is_private(defn.name)
18851884
):
18861885
self.msg.explicit_override_decorator_missing(
18871886
defn.name, found_method_base_classes[0].fullname, context or defn
@@ -1924,7 +1923,7 @@ def check_method_or_accessor_override_for_base(
19241923
base_attr = base.names.get(name)
19251924
if base_attr:
19261925
# First, check if we override a final (always an error, even with Any types).
1927-
if is_final_node(base_attr.node) and not is_private(name):
1926+
if is_final_node(base_attr.node):
19281927
self.msg.cant_override_final(name, base.name, defn)
19291928
# Second, final can't override anything writeable independently of types.
19301929
if defn.is_final:
@@ -2175,9 +2174,6 @@ def check_override(
21752174
if original.type_guard is not None and override.type_guard is None:
21762175
fail = True
21772176

2178-
if is_private(name):
2179-
fail = False
2180-
21812177
if fail:
21822178
emitted_msg = False
21832179

@@ -2575,8 +2571,6 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
25752571
# Normal checks for attribute compatibility should catch any problems elsewhere.
25762572
non_overridden_attrs = base.names.keys() - typ.names.keys()
25772573
for name in non_overridden_attrs:
2578-
if is_private(name):
2579-
continue
25802574
for base2 in mro[i + 1 :]:
25812575
# We only need to check compatibility of attributes from classes not
25822576
# in a subclass relationship. For subclasses, normal (single inheritance)
@@ -2684,7 +2678,7 @@ class C(B, A[int]): ... # this is unsafe because...
26842678
ok = True
26852679
# Final attributes can never be overridden, but can override
26862680
# non-final read-only attributes.
2687-
if is_final_node(second.node) and not is_private(name):
2681+
if is_final_node(second.node):
26882682
self.msg.cant_override_final(name, base2.name, ctx)
26892683
if is_final_node(first.node):
26902684
self.check_if_final_var_override_writable(name, second.node, ctx)
@@ -3140,9 +3134,6 @@ def check_compatibility_all_supers(
31403134
):
31413135
continue
31423136

3143-
if is_private(lvalue_node.name):
3144-
continue
3145-
31463137
base_type, base_node = self.lvalue_type_from_base(lvalue_node, base)
31473138
if isinstance(base_type, PartialType):
31483139
base_type = None
@@ -3312,8 +3303,6 @@ def check_compatibility_final_super(
33123303
"""
33133304
if not isinstance(base_node, (Var, FuncBase, Decorator)):
33143305
return True
3315-
if is_private(node.name):
3316-
return True
33173306
if base_node.is_final and (node.is_final or not isinstance(base_node, Var)):
33183307
# Give this error only for explicit override attempt with `Final`, or
33193308
# if we are overriding a final method with variable.
@@ -8259,15 +8248,6 @@ def is_overlapping_types_no_promote_no_uninhabited_no_none(left: Type, right: Ty
82598248
)
82608249

82618250

8262-
def is_private(node_name: str) -> bool:
8263-
"""Check if node is private to class definition.
8264-
8265-
Since Mypy supports name mangling, `is_private` is likely only required for
8266-
internally introduced names like `__mypy-replace` and `__mypy-post_init`.
8267-
"""
8268-
return node_name.startswith("__") and not node_name.endswith("__")
8269-
8270-
82718251
def is_string_literal(typ: Type) -> bool:
82728252
strs = try_getting_str_literals_from_type(typ)
82738253
return strs is not None and len(strs) == 1

mypy/plugins/dataclasses.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,20 +392,22 @@ def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) ->
392392
Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass
393393
to be used later whenever 'dataclasses.replace' is called for this dataclass.
394394
"""
395+
mangled_name = f"_{self._cls.name.lstrip('_')}{_INTERNAL_REPLACE_SYM_NAME}"
395396
add_method_to_class(
396397
self._api,
397398
self._cls,
398-
_INTERNAL_REPLACE_SYM_NAME,
399+
mangled_name,
399400
args=[attr.to_argument(self._cls.info, of="replace") for attr in attributes],
400401
return_type=NoneType(),
401402
is_staticmethod=True,
402403
)
403404

404405
def _add_internal_post_init_method(self, attributes: list[DataclassAttribute]) -> None:
406+
mangled_name = f"_{self._cls.name.lstrip('_')}{_INTERNAL_POST_INIT_SYM_NAME}"
405407
add_method_to_class(
406408
self._api,
407409
self._cls,
408-
_INTERNAL_POST_INIT_SYM_NAME,
410+
mangled_name,
409411
args=[
410412
attr.to_argument(self._cls.info, of="__post_init__")
411413
for attr in attributes
@@ -998,7 +1000,8 @@ def _get_expanded_dataclasses_fields(
9981000
ctx, get_proper_type(typ.upper_bound), display_typ, parent_typ
9991001
)
10001002
elif isinstance(typ, Instance):
1001-
replace_sym = typ.type.get_method(_INTERNAL_REPLACE_SYM_NAME)
1003+
mangled_name = f"_{typ.type.name.lstrip('_')}{_INTERNAL_REPLACE_SYM_NAME}"
1004+
replace_sym = typ.type.get_method(mangled_name)
10021005
if replace_sym is None:
10031006
return None
10041007
replace_sig = replace_sym.type
@@ -1082,7 +1085,8 @@ def check_post_init(api: TypeChecker, defn: FuncItem, info: TypeInfo) -> None:
10821085
return
10831086
assert isinstance(defn.type, FunctionLike)
10841087

1085-
ideal_sig_method = info.get_method(_INTERNAL_POST_INIT_SYM_NAME)
1088+
mangled_name = f"_{info.name.lstrip('_')}{_INTERNAL_POST_INIT_SYM_NAME}"
1089+
ideal_sig_method = info.get_method(mangled_name)
10861090
assert ideal_sig_method is not None and ideal_sig_method.type is not None
10871091
ideal_sig = ideal_sig_method.type
10881092
assert isinstance(ideal_sig, ProperType) # we set it ourselves

test-data/unit/deps.test

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,15 +1385,16 @@ class B(A):
13851385

13861386
[out]
13871387
<m.A.(abstract)> -> <m.B.__init__>, m
1388+
<m.A._A__mypy-replace> -> <m.B._A__mypy-replace>, m
1389+
<m.A._B__mypy-replace> -> m.B._B__mypy-replace
13881390
<m.A.__dataclass_fields__> -> <m.B.__dataclass_fields__>
13891391
<m.A.__init__> -> <m.B.__init__>, m.B.__init__
1390-
<m.A.__mypy-replace> -> <m.B.__mypy-replace>, m, m.B.__mypy-replace
13911392
<m.A.__new__> -> <m.B.__new__>
13921393
<m.A.x> -> <m.B.x>
13931394
<m.A.y> -> <m.B.y>
13941395
<m.A> -> m, m.A, m.B
13951396
<m.A[wildcard]> -> m
1396-
<m.B.__mypy-replace> -> m
1397+
<m.B._B__mypy-replace> -> m
13971398
<m.B.y> -> m
13981399
<m.B> -> m.B
13991400
<m.Z> -> m
@@ -1417,10 +1418,11 @@ class B(A):
14171418

14181419
[out]
14191420
<m.A.(abstract)> -> <m.B.__init__>, m
1421+
<m.A._A__mypy-replace> -> <m.B._A__mypy-replace>, m
1422+
<m.A._B__mypy-replace> -> m.B._B__mypy-replace
14201423
<m.A.__dataclass_fields__> -> <m.B.__dataclass_fields__>
14211424
<m.A.__init__> -> <m.B.__init__>, m.B.__init__
14221425
<m.A.__match_args__> -> <m.B.__match_args__>
1423-
<m.A.__mypy-replace> -> <m.B.__mypy-replace>, m, m.B.__mypy-replace
14241426
<m.A.__new__> -> <m.B.__new__>
14251427
<m.A.x> -> <m.B.x>
14261428
<m.A.y> -> <m.B.y>

test-data/unit/pythoneval.test

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,17 +2037,33 @@ from dataclasses import dataclass, replace
20372037
class A:
20382038
x: int
20392039

2040+
@dataclass
2041+
class B(A):
2042+
y: str
2043+
20402044
a = A(x=42)
20412045
a2 = replace(a, x=42)
20422046
reveal_type(a2)
20432047
a2 = replace()
2044-
a2 = replace(a, x='spam')
2048+
a2 = replace(a, x="spam")
20452049
a2 = replace(a, x=42, q=42)
2046-
[out]
2047-
_testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A"
2048-
_testDataclassReplace.py:10: error: Too few arguments for "replace"
2049-
_testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"
2050-
_testDataclassReplace.py:12: error: Unexpected keyword argument "q" for "replace" of "A"
2050+
2051+
b = B(x=42, y="egg")
2052+
b2 = replace(b, x=42, y="egg")
2053+
reveal_type(b2)
2054+
replace()
2055+
replace(b, x="egg", y=42)
2056+
replace(b, x=42, y="egg", q=42)
2057+
[out]
2058+
_testDataclassReplace.py:13: note: Revealed type is "_testDataclassReplace.A"
2059+
_testDataclassReplace.py:14: error: Too few arguments for "replace"
2060+
_testDataclassReplace.py:15: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"
2061+
_testDataclassReplace.py:16: error: Unexpected keyword argument "q" for "replace" of "A"
2062+
_testDataclassReplace.py:20: note: Revealed type is "_testDataclassReplace.B"
2063+
_testDataclassReplace.py:21: error: Too few arguments for "replace"
2064+
_testDataclassReplace.py:22: error: Argument "x" to "replace" of "B" has incompatible type "str"; expected "int"
2065+
_testDataclassReplace.py:22: error: Argument "y" to "replace" of "B" has incompatible type "int"; expected "str"
2066+
_testDataclassReplace.py:23: error: Unexpected keyword argument "q" for "replace" of "B"
20512067

20522068
[case testGenericInferenceWithTuple]
20532069
# flags: --new-type-inference

0 commit comments

Comments
 (0)