mirror of
https://github.com/python/cpython.git
synced 2025-10-08 08:01:55 +00:00
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
4 changed files with 93 additions and 1 deletions
|
@ -1985,6 +1985,15 @@ Functions and decorators
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. 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:: no_type_check
|
||||||
|
|
||||||
Decorator to indicate that annotations are not type hints.
|
Decorator to indicate that annotations are not type hints.
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import collections
|
import collections
|
||||||
|
from functools import lru_cache
|
||||||
|
import inspect
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -2536,10 +2538,80 @@ class FinalTests(BaseTestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
issubclass(int, Final)
|
issubclass(int, Final)
|
||||||
|
|
||||||
|
|
||||||
|
class FinalDecoratorTests(BaseTestCase):
|
||||||
def test_final_unmodified(self):
|
def test_final_unmodified(self):
|
||||||
def func(x): ...
|
def func(x): ...
|
||||||
self.assertIs(func, final(func))
|
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):
|
class CastTests(BaseTestCase):
|
||||||
|
|
||||||
|
|
|
@ -2042,8 +2042,17 @@ def final(f):
|
||||||
class Other(Leaf): # Error reported by type checker
|
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
|
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…
Add table
Add a link
Reference in a new issue