Skip to content

Commit a1eb0dd

Browse files
committed
remove method is_private by mangling "__mypy-replace" and "__mypy-post_init".
1 parent cfaddb3 commit a1eb0dd

File tree

4 files changed

+39
-39
lines changed

4 files changed

+39
-39
lines changed

mypy/checker.py

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,7 @@ def check_func_def(
13361336
if (
13371337
arg_type.variance == COVARIANT
13381338
and defn.name not in ("__init__", "__new__", "__post_init__")
1339-
and not is_private(defn.name) # private methods are not inherited
1339+
and "mypy-" not in defn.name # skip internally added methods
13401340
):
13411341
ctx: Context = arg_type
13421342
if ctx.line < 0:
@@ -1979,7 +1979,6 @@ def check_explicit_override_decorator(
19791979
and found_method_base_classes
19801980
and not defn.is_explicit_override
19811981
and defn.name not in ("__init__", "__new__")
1982-
and not is_private(defn.name)
19831982
):
19841983
self.msg.explicit_override_decorator_missing(
19851984
defn.name, found_method_base_classes[0].fullname, context or defn
@@ -2036,7 +2035,7 @@ def check_method_or_accessor_override_for_base(
20362035
base_attr = base.names.get(name)
20372036
if base_attr:
20382037
# First, check if we override a final (always an error, even with Any types).
2039-
if is_final_node(base_attr.node) and not is_private(name):
2038+
if is_final_node(base_attr.node):
20402039
self.msg.cant_override_final(name, base.name, defn)
20412040
# Second, final can't override anything writeable independently of types.
20422041
if defn.is_final:
@@ -2393,9 +2392,6 @@ def check_override(
23932392
if original.type_is is not None and override.type_is is None:
23942393
fail = True
23952394

2396-
if is_private(name):
2397-
fail = False
2398-
23992395
if fail:
24002396
emitted_msg = False
24012397

@@ -2828,12 +2824,8 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
28282824
# Verify that inherited attributes are compatible.
28292825
mro = typ.mro[1:]
28302826
all_names = {name for base in mro for name in base.names}
2827+
# Sort for reproducible message order.
28312828
for name in sorted(all_names - typ.names.keys()):
2832-
# Sort for reproducible message order.
2833-
# Attributes defined in both the type and base are skipped.
2834-
# Normal checks for attribute compatibility should catch any problems elsewhere.
2835-
if is_private(name):
2836-
continue
28372829
# Compare the first base defining a name with the rest.
28382830
# Remaining bases may not be pairwise compatible as the first base provides
28392831
# the used definition.
@@ -2945,7 +2937,7 @@ class C(B, A[int]): ... # this is unsafe because...
29452937
ok = True
29462938
# Final attributes can never be overridden, but can override
29472939
# non-final read-only attributes.
2948-
if is_final_node(second.node) and not is_private(name):
2940+
if is_final_node(second.node):
29492941
self.msg.cant_override_final(name, base2.name, ctx)
29502942
if is_final_node(first.node):
29512943
self.check_if_final_var_override_writable(name, second.node, ctx)
@@ -3415,9 +3407,6 @@ def check_compatibility_all_supers(
34153407
):
34163408
continue
34173409

3418-
if is_private(lvalue_node.name):
3419-
continue
3420-
34213410
base_type, base_node = self.lvalue_type_from_base(lvalue_node, base)
34223411
custom_setter = is_custom_settable_property(base_node)
34233412
if isinstance(base_type, PartialType):
@@ -3621,8 +3610,6 @@ def check_compatibility_final_super(
36213610
"""
36223611
if not isinstance(base_node, (Var, FuncBase, Decorator)):
36233612
return True
3624-
if is_private(node.name):
3625-
return True
36263613
if base_node.is_final and (node.is_final or not isinstance(base_node, Var)):
36273614
# Give this error only for explicit override attempt with `Final`, or
36283615
# if we are overriding a final method with variable.
@@ -8908,15 +8895,6 @@ def is_overlapping_types_for_overload(left: Type, right: Type) -> bool:
89088895
)
89098896

89108897

8911-
def is_private(node_name: str) -> bool:
8912-
"""Check if node is private to class definition.
8913-
8914-
Since Mypy supports name mangling, `is_private` is likely only required for
8915-
internally introduced names like `__mypy-replace` and `__mypy-post_init`.
8916-
"""
8917-
return node_name.startswith("__") and not node_name.endswith("__")
8918-
8919-
89208898
def is_string_literal(typ: Type) -> bool:
89218899
strs = try_getting_str_literals_from_type(typ)
89228900
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
@@ -422,20 +422,22 @@ def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) ->
422422
Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass
423423
to be used later whenever 'dataclasses.replace' is called for this dataclass.
424424
"""
425+
mangled_name = f"_{self._cls.name.lstrip('_')}{_INTERNAL_REPLACE_SYM_NAME}"
425426
add_method_to_class(
426427
self._api,
427428
self._cls,
428-
_INTERNAL_REPLACE_SYM_NAME,
429+
mangled_name,
429430
args=[attr.to_argument(self._cls.info, of="replace") for attr in attributes],
430431
return_type=NoneType(),
431432
is_staticmethod=True,
432433
)
433434

434435
def _add_internal_post_init_method(self, attributes: list[DataclassAttribute]) -> None:
436+
mangled_name = f"_{self._cls.name.lstrip('_')}{_INTERNAL_POST_INIT_SYM_NAME}"
435437
add_method_to_class(
436438
self._api,
437439
self._cls,
438-
_INTERNAL_POST_INIT_SYM_NAME,
440+
mangled_name,
439441
args=[
440442
attr.to_argument(self._cls.info, of="__post_init__")
441443
for attr in attributes
@@ -1027,7 +1029,8 @@ def _get_expanded_dataclasses_fields(
10271029
ctx, get_proper_type(typ.upper_bound), display_typ, parent_typ
10281030
)
10291031
elif isinstance(typ, Instance):
1030-
replace_sym = typ.type.get_method(_INTERNAL_REPLACE_SYM_NAME)
1032+
mangled_name = f"_{typ.type.name.lstrip('_')}{_INTERNAL_REPLACE_SYM_NAME}"
1033+
replace_sym = typ.type.get_method(mangled_name)
10311034
if replace_sym is None:
10321035
return None
10331036
replace_sig = replace_sym.type
@@ -1111,7 +1114,8 @@ def check_post_init(api: TypeChecker, defn: FuncItem, info: TypeInfo) -> None:
11111114
return
11121115
assert isinstance(defn.type, FunctionLike)
11131116

1114-
ideal_sig_method = info.get_method(_INTERNAL_POST_INIT_SYM_NAME)
1117+
mangled_name = f"_{info.name.lstrip('_')}{_INTERNAL_POST_INIT_SYM_NAME}"
1118+
ideal_sig_method = info.get_method(mangled_name)
11151119
assert ideal_sig_method is not None and ideal_sig_method.type is not None
11161120
ideal_sig = ideal_sig_method.type
11171121
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
@@ -1389,15 +1389,16 @@ class B(A):
13891389

13901390
[out]
13911391
<m.A.(abstract)> -> <m.B.__init__>, m
1392+
<m.A._A__mypy-replace> -> <m.B._A__mypy-replace>, m
1393+
<m.A._B__mypy-replace> -> m.B._B__mypy-replace
13921394
<m.A.__dataclass_fields__> -> <m.B.__dataclass_fields__>
13931395
<m.A.__init__> -> <m.B.__init__>, m.B.__init__
1394-
<m.A.__mypy-replace> -> <m.B.__mypy-replace>, m, m.B.__mypy-replace
13951396
<m.A.__new__> -> <m.B.__new__>
13961397
<m.A.x> -> <m.B.x>
13971398
<m.A.y> -> <m.B.y>
13981399
<m.A> -> m, m.A, m.B
13991400
<m.A[wildcard]> -> m
1400-
<m.B.__mypy-replace> -> m
1401+
<m.B._B__mypy-replace> -> m
14011402
<m.B.y> -> m
14021403
<m.B> -> m.B
14031404
<m.Z> -> m
@@ -1421,10 +1422,11 @@ class B(A):
14211422

14221423
[out]
14231424
<m.A.(abstract)> -> <m.B.__init__>, m
1425+
<m.A._A__mypy-replace> -> <m.B._A__mypy-replace>, m
1426+
<m.A._B__mypy-replace> -> m.B._B__mypy-replace
14241427
<m.A.__dataclass_fields__> -> <m.B.__dataclass_fields__>
14251428
<m.A.__init__> -> <m.B.__init__>, m.B.__init__
14261429
<m.A.__match_args__> -> <m.B.__match_args__>
1427-
<m.A.__mypy-replace> -> <m.B.__mypy-replace>, m, m.B.__mypy-replace
14281430
<m.A.__new__> -> <m.B.__new__>
14291431
<m.A.x> -> <m.B.x>
14301432
<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
@@ -1963,17 +1963,33 @@ from dataclasses import dataclass, replace
19631963
class A:
19641964
x: int
19651965

1966+
@dataclass
1967+
class B(A):
1968+
y: str
1969+
19661970
a = A(x=42)
19671971
a2 = replace(a, x=42)
19681972
reveal_type(a2)
19691973
a2 = replace()
1970-
a2 = replace(a, x='spam')
1974+
a2 = replace(a, x="spam")
19711975
a2 = replace(a, x=42, q=42)
1972-
[out]
1973-
_testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A"
1974-
_testDataclassReplace.py:10: error: Too few arguments for "replace"
1975-
_testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"
1976-
_testDataclassReplace.py:12: error: Unexpected keyword argument "q" for "replace" of "A"
1976+
1977+
b = B(x=42, y="egg")
1978+
b2 = replace(b, x=42, y="egg")
1979+
reveal_type(b2)
1980+
replace()
1981+
replace(b, x="egg", y=42)
1982+
replace(b, x=42, y="egg", q=42)
1983+
[out]
1984+
_testDataclassReplace.py:13: note: Revealed type is "_testDataclassReplace.A"
1985+
_testDataclassReplace.py:14: error: Too few arguments for "replace"
1986+
_testDataclassReplace.py:15: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"
1987+
_testDataclassReplace.py:16: error: Unexpected keyword argument "q" for "replace" of "A"
1988+
_testDataclassReplace.py:20: note: Revealed type is "_testDataclassReplace.B"
1989+
_testDataclassReplace.py:21: error: Too few arguments for "replace"
1990+
_testDataclassReplace.py:22: error: Argument "x" to "replace" of "B" has incompatible type "str"; expected "int"
1991+
_testDataclassReplace.py:22: error: Argument "y" to "replace" of "B" has incompatible type "int"; expected "str"
1992+
_testDataclassReplace.py:23: error: Unexpected keyword argument "q" for "replace" of "B"
19771993

19781994
[case testGenericInferenceWithTuple]
19791995
# flags: --new-type-inference

0 commit comments

Comments
 (0)