summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/typing.rst9
-rw-r--r--Lib/test/test_typing.py72
-rw-r--r--Lib/typing.py11
-rw-r--r--Misc/NEWS.d/next/Library/2022-01-11-04-28-09.bpo-46342.5QVEH1.rst2
4 files changed, 93 insertions, 1 deletions
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index de7aa08..cb14db9 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -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.
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index af5b1df..fd8237a 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -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):
diff --git a/Lib/typing.py b/Lib/typing.py
index d520f6b..972b8ba 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -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
diff --git a/Misc/NEWS.d/next/Library/2022-01-11-04-28-09.bpo-46342.5QVEH1.rst b/Misc/NEWS.d/next/Library/2022-01-11-04-28-09.bpo-46342.5QVEH1.rst
new file mode 100644
index 0000000..31d484f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-11-04-28-09.bpo-46342.5QVEH1.rst
@@ -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.