From e2349bb8fffbd7d6b9043c026ed92dcc0b06a762 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Fri, 3 Feb 2023 10:29:23 -0800 Subject: [PATCH 01/10] PEP 698: Add typing.override decorator Testing: First set up the repo, following instructions at https://devguide.python.org/ by running: ``` ./configure --with-pydebug && make -j ``` Then run the typing tests: ``` ./python -m test test_typing -v ``` I ran the full test suite with ``` ./python -m test -j3 ``` and it came back clean except for a `test_grp` failure which I seem to get on trunk as well - likely something in my build is misconfigured but I'm pretty sure it is unrelated to the changes here. --- Doc/library/typing.rst | 36 ++++++++++++++++++++++++++++++++++++ Lib/test/test_typing.py | 38 ++++++++++++++++++++++++++++++++++++++ Lib/typing.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index bbbf6920ddec88..45113fbdbd553f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -91,6 +91,8 @@ annotations. These include: *Introducing* :data:`LiteralString` * :pep:`681`: Data Class Transforms *Introducing* the :func:`@dataclass_transform` decorator +* :pep:`698`: Adding an override decorator to typing + *Introducing* the :func:`@override` decorator .. _type-aliases: @@ -2722,6 +2724,40 @@ Functions and decorators This wraps the decorator with something that wraps the decorated function in :func:`no_type_check`. + +.. decorator:: override + + A decorator for methods that indicates to type checkers that this method + should override a method or attribute 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. + + class Base: + def log_status(self) + + class Sub(Base): + @override + def log_status(self) -> None: # Okay: overrides Base.log_status + ... + + @override + def done(self) -> None: # Error reported by type checker + ... + + There is no runtime checking of this property. + + The decorator will set the ``__override__`` attribute to ``True`` on + the decorated object. Thus, a check like + ``if getattr(obj, "__final__", False)`` can be used at runtime to determine + whether an object ``obj`` has been marked as final. If the decorated object + does not support setting attributes, the decorator returns the object unchanged + without raising an exception. + + See :pep:`698` for more details. + + .. versionadded:: 3.12 + + .. decorator:: type_check_only Decorator to mark a class or function to be unavailable at runtime. diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7a460d94469fe7..d61dc6e2fbd70b 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -23,6 +23,7 @@ from typing import assert_type, cast, runtime_checkable from typing import get_type_hints from typing import get_origin, get_args +from typing import override from typing import is_typeddict from typing import reveal_type from typing import dataclass_transform @@ -4166,6 +4167,43 @@ def cached(self): ... self.assertIs(True, Methods.cached.__final__) +class OverrideDecoratorTests(BaseTestCase): + def test_override(self): + class Base: + def normal_method(self): ... + @staticmethod + def static_method_good_order(): ... + @staticmethod + def static_method_bad_order(): ... + @staticmethod + def decorator_with_slots(): ... + + class Derived(Base): + @override + def normal_method(self): + return 42 + + @staticmethod + @override + def static_method_good_order(): + return 42 + + @override + @staticmethod + def static_method_bad_order(): + return 42 + + + self.assertIsSubclass(Derived, Base) + instance = Derived() + self.assertEqual(instance.normal_method(), 42) + self.assertIs(True, instance.normal_method.__override__) + self.assertEqual(Derived.static_method_good_order(), 42) + self.assertIs(True, Derived.static_method_good_order.__override__) + self.assertEqual(Derived.static_method_bad_order(), 42) + self.assertIs(False, hasattr(Derived.static_method_bad_order, "__override__")) + + class CastTests(BaseTestCase): def test_basics(self): diff --git a/Lib/typing.py b/Lib/typing.py index bdf51bb5f41595..20599ed51fa196 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -138,6 +138,7 @@ def _idfunc(_, x): 'NoReturn', 'NotRequired', 'overload', + 'override', 'ParamSpecArgs', 'ParamSpecKwargs', 'Required', @@ -2657,6 +2658,7 @@ class Other(Leaf): # Error reported by type checker # Internal type variable used for Type[]. CT_co = TypeVar('CT_co', covariant=True, bound=type) + # A useful type variable with constraints. This represents string types. # (This one *is* for export!) AnyStr = TypeVar('AnyStr', bytes, str) @@ -2748,6 +2750,8 @@ def new_user(user_class: Type[U]) -> U: At this point the type checker knows that joe has type BasicUser. """ +# Internal type variable for callables. Not for export. +F = TypeVar("F", bound=Callable[..., Any]) @runtime_checkable class SupportsInt(Protocol): @@ -3448,3 +3452,40 @@ def decorator(cls_or_fn): } return cls_or_fn return decorator + + + +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 or attribute 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. + + There is no runtime checking of this property. The decorator sets the + ``__override__`` attribute to ``True`` on the decorated object to allow + runtime introspection. + + See PEP 698 for details. + + """ + try: + __arg.__override__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return __arg From bd3b6acff2096fcd6ce2111c6e155300f07438fa Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 08:30:12 -0800 Subject: [PATCH 02/10] Add missing `For example::` in the rst doc --- Doc/library/typing.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 45113fbdbd553f..dabd247738d665 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2732,6 +2732,8 @@ Functions and decorators This helps prevent bugs that may occur when a base class is changed without an equivalent change to a child class. + For example:: + class Base: def log_status(self) From c3b9b7f4ffbc2c228953a1c6af6d9a32b6bd3aa9 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 4 Feb 2023 16:35:48 +0000 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst diff --git a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst new file mode 100644 index 00000000000000..3863c9347bc3a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst @@ -0,0 +1,5 @@ +Add a new decorator ``typing.override``. + +This decorator indicates to static type checkers that they should verify the method overrides an attribute or method of the same name in a base class. This is useful for catching bugs when base class methods are renamed but some children are not updated accordingly. + +See :pep:698 for details. From c7cecf8200fafbfad58b748f16c4184e96bdb23a Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 08:43:00 -0800 Subject: [PATCH 04/10] Update Doc/library/typing.rst Correct `s/final/override` (from reusing a similar block of documentation on the `@final` decorator) Co-authored-by: Jelle Zijlstra --- Doc/library/typing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index dabd247738d665..3395e4bfb95c44 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2750,8 +2750,8 @@ Functions and decorators The decorator will set the ``__override__`` attribute to ``True`` on the decorated object. Thus, a check like - ``if getattr(obj, "__final__", False)`` can be used at runtime to determine - whether an object ``obj`` has been marked as final. If the decorated object + ``if getattr(obj, "__override__", False)`` can be used at runtime to determine + whether an object ``obj`` has been marked as an override. If the decorated object does not support setting attributes, the decorator returns the object unchanged without raising an exception. From 2acf32fd373322903a3be20ce50d11a9881f42b2 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 08:40:43 -0800 Subject: [PATCH 05/10] Add whatsnew entry, as per Alex Waygood's suggestion Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- Doc/whatsnew/3.12.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index e551c5b4fd06a9..6f0514b03c9fc4 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -350,6 +350,14 @@ tempfile The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter *delete_on_close* (Contributed by Evgeny Zorin in :gh:`58451`.) +typing +----- + +* Add :func:`typing.override`, an override decorator telling to static type + checkers to verify that a method overrides some method or attribute of the + same name on a base class, as per :pep:698 (Contributed by Steven Troxler in + :gh:`101564`). + sys --- From 6bd3356398628050b0caf7be605cb0f75a133b57 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 08:47:32 -0800 Subject: [PATCH 06/10] Add ACKS entry, fix rst errors Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- Doc/whatsnew/3.12.rst | 2 +- Misc/ACKS | 1 + .../next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 6f0514b03c9fc4..aa9444a7807bbf 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -355,7 +355,7 @@ typing * Add :func:`typing.override`, an override decorator telling to static type checkers to verify that a method overrides some method or attribute of the - same name on a base class, as per :pep:698 (Contributed by Steven Troxler in + same name on a base class, as per :pep:`698` (Contributed by Steven Troxler in :gh:`101564`). sys diff --git a/Misc/ACKS b/Misc/ACKS index 3403aee4cc78ff..2da3d0ab29b81d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1848,6 +1848,7 @@ Tom Tromey John Tromp Diane Trout Jason Trowbridge +Steven Troxler Brent Tubbs Anthony Tuininga Erno Tukia diff --git a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst index 3863c9347bc3a3..6581f73fdd6f02 100644 --- a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst +++ b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst @@ -2,4 +2,4 @@ Add a new decorator ``typing.override``. This decorator indicates to static type checkers that they should verify the method overrides an attribute or method of the same name in a base class. This is useful for catching bugs when base class methods are renamed but some children are not updated accordingly. -See :pep:698 for details. +See :pep:`698` for details. From 42277cb9561014d7a7d188f86d35f2625b388616 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 08:52:51 -0800 Subject: [PATCH 07/10] Fix title underline length in whatisnew doc. Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index aa9444a7807bbf..0dd6bf86350e50 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -351,7 +351,7 @@ The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter *delete_on_close* (Contributed by Evgeny Zorin in :gh:`58451`.) typing ------ +------ * Add :func:`typing.override`, an override decorator telling to static type checkers to verify that a method overrides some method or attribute of the From afb74de6274ef8fbb3927d7688129c15b87f2c7c Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 09:19:33 -0800 Subject: [PATCH 08/10] Update Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst Co-authored-by: Alex Waygood --- .../next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst index 6581f73fdd6f02..936764433e4bbb 100644 --- a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst +++ b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst @@ -2,4 +2,4 @@ Add a new decorator ``typing.override``. This decorator indicates to static type checkers that they should verify the method overrides an attribute or method of the same name in a base class. This is useful for catching bugs when base class methods are renamed but some children are not updated accordingly. -See :pep:`698` for details. +See :pep:`698` for details. Patch by Steven Troxler. From 090d9d294a889b833372342cd65ed129e1984bd9 Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Sat, 4 Feb 2023 14:22:02 -0800 Subject: [PATCH 09/10] Use a positional-only argument named `method` Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- Lib/typing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 20599ed51fa196..8d40e923bb1d08 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -3455,7 +3455,7 @@ def decorator(cls_or_fn): -def override(__arg: F) -> F: +def override(method: F, /) -> F: """Indicate that a method is intended to override a method in a base class. Usage: @@ -3482,10 +3482,10 @@ def method(self) -> None: """ try: - __arg.__override__ = True + method.__override__ = True except (AttributeError, TypeError): # Skip the attribute silently if it is not writable. # AttributeError happens if the object has __slots__ or a # read-only property, TypeError if it's a builtin class. pass - return __arg + return method From f6c00e54c3eea74c65dba0c87737cb78920fd01d Mon Sep 17 00:00:00 2001 From: Steven Troxler Date: Mon, 27 Feb 2023 12:46:15 -0800 Subject: [PATCH 10/10] Apply suggestions from code review Thanks, looks good Co-authored-by: Jelle Zijlstra --- Doc/whatsnew/3.12.rst | 4 ++-- .../Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 0dd6bf86350e50..1a25ec6b70613b 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -355,8 +355,8 @@ typing * Add :func:`typing.override`, an override decorator telling to static type checkers to verify that a method overrides some method or attribute of the - same name on a base class, as per :pep:`698` (Contributed by Steven Troxler in - :gh:`101564`). + same name on a base class, as per :pep:`698`. (Contributed by Steven Troxler in + :gh:`101564`.) sys --- diff --git a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst index 936764433e4bbb..2f6a4153062e5a 100644 --- a/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst +++ b/Misc/NEWS.d/next/Library/2023-02-04-16-35-46.gh-issue-101561.Xo6pIZ.rst @@ -1,5 +1 @@ -Add a new decorator ``typing.override``. - -This decorator indicates to static type checkers that they should verify the method overrides an attribute or method of the same name in a base class. This is useful for catching bugs when base class methods are renamed but some children are not updated accordingly. - -See :pep:`698` for details. Patch by Steven Troxler. +Add a new decorator :func:`typing.override`. See :pep:`698` for details. Patch by Steven Troxler.