diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a6df4a1..884d8f1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ and Jelle Zijlstra). Patch by James Hilton-Balfe (@Gobot1234). - Add initial support for TypeVarLike `default` parameter, PEP 696. Patch by Marc Mueller (@cdce8p). +- Runtime support for PEP 698, adding `typing_extensions.override`. Patch by + Jelle Zijlstra. - Add the `infer_variance` parameter to `TypeVar`, as specified in PEP 695. Patch by Jelle Zijlstra. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index b01db1d3..15b2147b 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -29,6 +29,7 @@ from typing_extensions import assert_type, get_type_hints, get_origin, get_args from typing_extensions import clear_overloads, get_overloads, overload from typing_extensions import NamedTuple +from typing_extensions import override from _typed_dict_test_helper import FooGeneric # Flags used to mark tests that only apply after a specific @@ -160,6 +161,21 @@ def test_exception(self): assert_never(None) +class OverrideTests(BaseTestCase): + def test_override(self): + class Base: + def foo(self): ... + + class Derived(Base): + @override + def foo(self): + return 42 + + self.assertIsSubclass(Derived, Base) + self.assertEqual(Derived().foo(), 42) + self.assertEqual(dir(Base.foo), dir(Derived.foo)) + + class AnyTests(BaseTestCase): def test_can_subclass(self): class Mock(Any): pass diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 44396624..acb0a94f 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -62,6 +62,7 @@ 'Literal', 'NewType', 'overload', + 'override', 'Protocol', 'reveal_type', 'runtime', @@ -2078,6 +2079,36 @@ def decorator(cls_or_fn): return decorator +if hasattr(typing, "override"): + override = typing.override +else: + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(__arg: _F) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: ... + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + See PEP 698 for details. + + """ + return __arg + + # We have to do some monkey patching to deal with the dual nature of # Unpack/TypeVarTuple: # - We want Unpack to be a kind of TypeVar so it gets accepted in