mirror of https://github.com/python/cpython
bpo-46342: make @typing.final introspectable (GH-30530)
Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
This commit is contained in:
parent
e34c9367f8
commit
0bbf30e2b9
|
@ -1985,6 +1985,15 @@ Functions and decorators
|
|||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
The decorator will now set the ``__final__`` 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.
|
||||
|
||||
|
||||
.. decorator:: no_type_check
|
||||
|
||||
Decorator to indicate that annotations are not type hints.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import contextlib
|
||||
import collections
|
||||
from functools import lru_cache
|
||||
import inspect
|
||||
import pickle
|
||||
import re
|
||||
import sys
|
||||
|
@ -2536,10 +2538,80 @@ class FinalTests(BaseTestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
issubclass(int, Final)
|
||||
|
||||
|
||||
class FinalDecoratorTests(BaseTestCase):
|
||||
def test_final_unmodified(self):
|
||||
def func(x): ...
|
||||
self.assertIs(func, final(func))
|
||||
|
||||
def test_dunder_final(self):
|
||||
@final
|
||||
def func(): ...
|
||||
@final
|
||||
class Cls: ...
|
||||
self.assertIs(True, func.__final__)
|
||||
self.assertIs(True, Cls.__final__)
|
||||
|
||||
class Wrapper:
|
||||
__slots__ = ("func",)
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
# Check that no error is thrown if the attribute
|
||||
# is not writable.
|
||||
@final
|
||||
@Wrapper
|
||||
def wrapped(): ...
|
||||
self.assertIsInstance(wrapped, Wrapper)
|
||||
self.assertIs(False, hasattr(wrapped, "__final__"))
|
||||
|
||||
class Meta(type):
|
||||
@property
|
||||
def __final__(self): return "can't set me"
|
||||
@final
|
||||
class WithMeta(metaclass=Meta): ...
|
||||
self.assertEqual(WithMeta.__final__, "can't set me")
|
||||
|
||||
# Builtin classes throw TypeError if you try to set an
|
||||
# attribute.
|
||||
final(int)
|
||||
self.assertIs(False, hasattr(int, "__final__"))
|
||||
|
||||
# Make sure it works with common builtin decorators
|
||||
class Methods:
|
||||
@final
|
||||
@classmethod
|
||||
def clsmethod(cls): ...
|
||||
|
||||
@final
|
||||
@staticmethod
|
||||
def stmethod(): ...
|
||||
|
||||
# The other order doesn't work because property objects
|
||||
# don't allow attribute assignment.
|
||||
@property
|
||||
@final
|
||||
def prop(self): ...
|
||||
|
||||
@final
|
||||
@lru_cache()
|
||||
def cached(self): ...
|
||||
|
||||
# Use getattr_static because the descriptor returns the
|
||||
# underlying function, which doesn't have __final__.
|
||||
self.assertIs(
|
||||
True,
|
||||
inspect.getattr_static(Methods, "clsmethod").__final__
|
||||
)
|
||||
self.assertIs(
|
||||
True,
|
||||
inspect.getattr_static(Methods, "stmethod").__final__
|
||||
)
|
||||
self.assertIs(True, Methods.prop.fget.__final__)
|
||||
self.assertIs(True, Methods.cached.__final__)
|
||||
|
||||
|
||||
class CastTests(BaseTestCase):
|
||||
|
||||
|
|
|
@ -2042,8 +2042,17 @@ def final(f):
|
|||
class Other(Leaf): # Error reported by type checker
|
||||
...
|
||||
|
||||
There is no runtime checking of these properties.
|
||||
There is no runtime checking of these properties. The decorator
|
||||
sets the ``__final__`` attribute to ``True`` on the decorated object
|
||||
to allow runtime introspection.
|
||||
"""
|
||||
try:
|
||||
f.__final__ = 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 f
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
The ``@typing.final`` decorator now sets the ``__final__`` attribute on the
|
||||
decorated object to allow runtime introspection. Patch by Jelle Zijlstra.
|
Loading…
Reference in New Issue