From a24b5e67b40181b509b3cdbef893975400838ed5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Nov 2023 19:53:49 +0000 Subject: [PATCH 1/4] Exclude private attributes from override checks --- mypy/checker.py | 7 +++++-- test-data/unit/check-dataclasses.test | 13 +++++++++++++ test-data/unit/check-final.test | 13 +++++++++++++ test-data/unit/check-functions.test | 11 +++++++++++ test-data/unit/fixtures/dataclasses.pyi | 7 ++++++- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e4eb58d40715..b9a9d3affb90 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1879,6 +1879,7 @@ def check_explicit_override_decorator( found_method_base_classes and not defn.is_explicit_override and defn.name not in ("__init__", "__new__") + and not is_private(defn.name) ): self.msg.explicit_override_decorator_missing( defn.name, found_method_base_classes[0].fullname, context or defn @@ -1921,7 +1922,7 @@ def check_method_or_accessor_override_for_base( base_attr = base.names.get(name) if base_attr: # First, check if we override a final (always an error, even with Any types). - if is_final_node(base_attr.node): + if is_final_node(base_attr.node) and not is_private(name): self.msg.cant_override_final(name, base.name, defn) # Second, final can't override anything writeable independently of types. if defn.is_final: @@ -2680,7 +2681,7 @@ class C(B, A[int]): ... # this is unsafe because... ok = True # Final attributes can never be overridden, but can override # non-final read-only attributes. - if is_final_node(second.node): + if is_final_node(second.node) and not is_private(name): self.msg.cant_override_final(name, base2.name, ctx) if is_final_node(first.node): self.check_if_final_var_override_writable(name, second.node, ctx) @@ -3308,6 +3309,8 @@ def check_compatibility_final_super( """ if not isinstance(base_node, (Var, FuncBase, Decorator)): return True + if is_private(node.name): + return True if base_node.is_final and (node.is_final or not isinstance(base_node, Var)): # Give this error only for explicit override attempt with `Final`, or # if we are overriding a final method with variable. diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index d37ae569cc5e..107298875761 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2531,3 +2531,16 @@ class Foo: c: int # E: Name "c" already defined on line 5 [builtins fixtures/dataclasses.pyi] + +[case testDataclassInheritanceWorksWithExplicitOverrides] +# flags: --enable-error-code explicit-override +from dataclasses import dataclass + +@dataclass +class Base: + x: int + +@dataclass +class Child(Base): + y: int +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index da034caced76..a2fd64386707 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1117,3 +1117,16 @@ from typing import Final class MyClass: a: None a: Final[int] = 1 # E: Cannot redefine an existing name as final # E: Name "a" already defined on line 5 + +[case testFinalOverrideAllowedForPrivate] +from typing import Final, final + +class Parent: + __foo: Final[int] = 0 + @final + def __bar(self) -> None: ... + +class Child(Parent): + __foo: Final[int] = 1 + @final + def __bar(self) -> None: ... diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index cd098a84d4d3..9b884228457f 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3159,6 +3159,17 @@ class D(A, B): def f(self, z: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" [typing fixtures/typing-override.pyi] +[case testExplicitOverrideAllowedForPrivate] +# flags: --enable-error-code explicit-override --python-version 3.12 +from typing import override + +class B: + def __f(self, y: int) -> str: pass + +class C(B): + def __f(self, y: int) -> str: pass # OK +[typing fixtures/typing-override.pyi] + [case testCallableProperty] from typing import Callable diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi index 059c853a621f..164a74a5fe64 100644 --- a/test-data/unit/fixtures/dataclasses.pyi +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -1,8 +1,9 @@ import _typeshed from typing import ( Generic, Iterator, Iterable, Mapping, Optional, Sequence, Tuple, - TypeVar, Union, overload, + TypeVar, Union, overload ) +from typing_extensions import override _T = TypeVar('_T') _U = TypeVar('_U') @@ -29,8 +30,10 @@ class dict(Mapping[KT, VT]): def __init__(self, **kwargs: VT) -> None: pass @overload def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass + @override def __getitem__(self, key: KT) -> VT: pass def __setitem__(self, k: KT, v: VT) -> None: pass + @override def __iter__(self) -> Iterator[KT]: pass def __contains__(self, item: object) -> int: pass def update(self, a: Mapping[KT, VT]) -> None: pass @@ -42,7 +45,9 @@ class dict(Mapping[KT, VT]): class list(Generic[_T], Sequence[_T]): def __contains__(self, item: object) -> int: pass + @override def __getitem__(self, key: int) -> _T: pass + @override def __iter__(self) -> Iterator[_T]: pass class function: pass From daacfb656339c21e7651fc027695425e1b3a6632 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Nov 2023 19:58:21 +0000 Subject: [PATCH 2/4] Undo accidental change --- test-data/unit/fixtures/dataclasses.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi index 164a74a5fe64..29f87ae97e62 100644 --- a/test-data/unit/fixtures/dataclasses.pyi +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -1,7 +1,7 @@ import _typeshed from typing import ( Generic, Iterator, Iterable, Mapping, Optional, Sequence, Tuple, - TypeVar, Union, overload + TypeVar, Union, overload, ) from typing_extensions import override From 03222d8a2b6d668de80c1122c1cab6dadea40bea Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Nov 2023 20:24:36 +0000 Subject: [PATCH 3/4] Update test --- test-data/unit/fine-grained-dataclass-transform.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/fine-grained-dataclass-transform.test b/test-data/unit/fine-grained-dataclass-transform.test index cc297bc344aa..89628256fda5 100644 --- a/test-data/unit/fine-grained-dataclass-transform.test +++ b/test-data/unit/fine-grained-dataclass-transform.test @@ -86,9 +86,9 @@ class A(Dataclass): [out] main:7: error: Unexpected keyword argument "x" for "B" -builtins.pyi:13: note: "B" defined here +builtins.pyi:14: note: "B" defined here main:7: error: Unexpected keyword argument "y" for "B" -builtins.pyi:13: note: "B" defined here +builtins.pyi:14: note: "B" defined here == [case frozenInheritanceViaDefault] From 02fec388a0b799b153b42eb0499077c95b1ac001 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Nov 2023 00:57:35 +0000 Subject: [PATCH 4/4] Update test-data/unit/check-functions.test Co-authored-by: Alex Waygood --- test-data/unit/check-functions.test | 1 - 1 file changed, 1 deletion(-) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 9b884228457f..b3df5fddafba 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3161,7 +3161,6 @@ class D(A, B): [case testExplicitOverrideAllowedForPrivate] # flags: --enable-error-code explicit-override --python-version 3.12 -from typing import override class B: def __f(self, y: int) -> str: pass