mirror of
https://github.com/python/cpython
synced 2024-10-14 19:55:22 +00:00
gh-101561: Add typing.override decorator (#101564)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
4624987b29
commit
0f89acf6cc
|
@ -91,6 +91,8 @@ annotations. These include:
|
|||
*Introducing* :data:`LiteralString`
|
||||
* :pep:`681`: Data Class Transforms
|
||||
*Introducing* the :func:`@dataclass_transform<dataclass_transform>` decorator
|
||||
* :pep:`698`: Adding an override decorator to typing
|
||||
*Introducing* the :func:`@override<override>` decorator
|
||||
|
||||
.. _type-aliases:
|
||||
|
||||
|
@ -2722,6 +2724,42 @@ 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.
|
||||
|
||||
For example::
|
||||
|
||||
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, "__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.
|
||||
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
---
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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(method: 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:
|
||||
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 method
|
||||
|
|
|
@ -1848,6 +1848,7 @@ Tom Tromey
|
|||
John Tromp
|
||||
Diane Trout
|
||||
Jason Trowbridge
|
||||
Steven Troxler
|
||||
Brent Tubbs
|
||||
Anthony Tuininga
|
||||
Erno Tukia
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Add a new decorator :func:`typing.override`. See :pep:`698` for details. Patch by Steven Troxler.
|
Loading…
Reference in a new issue