| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1985,6 +1985,15 @@ Functions and decorators | |||
| 1985 | 1985 | ||
| 1986 | 1986 | .. versionadded:: 3.8 | |
| 1987 | 1987 | ||
| 1988 | + .. versionchanged:: 3.11 | ||
| 1989 | + The decorator will now set the ``__final__`` attribute to ``True`` | ||
| 1990 | + on the decorated object. Thus, a check like | ||
| 1991 | + ``if getattr(obj, "__final__", False)`` can be used at runtime | ||
| 1992 | + to determine whether an object ``obj`` has been marked as final. | ||
| 1993 | + If the decorated object does not support setting attributes, | ||
| 1994 | + the decorator returns the object unchanged without raising an exception. | ||
| 1995 | + | ||
| 1996 | + | ||
| 1988 | 1997 | .. decorator:: no_type_check | |
| 1989 | 1998 | ||
| 1990 | 1999 | Decorator to indicate that annotations are not type hints. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,7 @@ | |||
| 1 | 1 | import contextlib | |
| 2 | 2 | import collections | |
| 3 | + from functools import lru_cache | ||
| 4 | + import inspect | ||
| 3 | 5 | import pickle | |
| 4 | 6 | import re | |
| 5 | 7 | import sys | |
@@ -2536,10 +2538,80 @@ def test_no_isinstance(self): | |||
| 2536 | 2538 | with self.assertRaises(TypeError): | |
| 2537 | 2539 | issubclass(int, Final) | |
| 2538 | 2540 | ||
| 2541 | + | ||
| 2542 | + class FinalDecoratorTests(BaseTestCase): | ||
| 2539 | 2543 | def test_final_unmodified(self): | |
| 2540 | 2544 | def func(x): ... | |
| 2541 | 2545 | self.assertIs(func, final(func)) | |
| 2542 | 2546 | ||
| 2547 | + def test_dunder_final(self): | ||
| 2548 | + @final | ||
| 2549 | + def func(): ... | ||
| 2550 | + @final | ||
| 2551 | + class Cls: ... | ||
| 2552 | + self.assertIs(True, func.__final__) | ||
| 2553 | + self.assertIs(True, Cls.__final__) | ||
| 2554 | + | ||
| 2555 | + class Wrapper: | ||
| 2556 | + __slots__ = ("func",) | ||
| 2557 | + def __init__(self, func): | ||
| 2558 | + self.func = func | ||
| 2559 | + def __call__(self, *args, **kwargs): | ||
| 2560 | + return self.func(*args, **kwargs) | ||
| 2561 | + | ||
| 2562 | + # Check that no error is thrown if the attribute | ||
| 2563 | + # is not writable. | ||
| 2564 | + @final | ||
| 2565 | + @Wrapper | ||
| 2566 | + def wrapped(): ... | ||
| 2567 | + self.assertIsInstance(wrapped, Wrapper) | ||
| 2568 | + self.assertIs(False, hasattr(wrapped, "__final__")) | ||
| 2569 | + | ||
| 2570 | + class Meta(type): | ||
| 2571 | + @property | ||
| 2572 | + def __final__(self): return "can't set me" | ||
| 2573 | + @final | ||
| 2574 | + class WithMeta(metaclass=Meta): ... | ||
| 2575 | + self.assertEqual(WithMeta.__final__, "can't set me") | ||
| 2576 | + | ||
| 2577 | + # Builtin classes throw TypeError if you try to set an | ||
| 2578 | + # attribute. | ||
| 2579 | + final(int) | ||
| 2580 | + self.assertIs(False, hasattr(int, "__final__")) | ||
| 2581 | + | ||
| 2582 | + # Make sure it works with common builtin decorators | ||
| 2583 | + class Methods: | ||
| 2584 | + @final | ||
| 2585 | + @classmethod | ||
| 2586 | + def clsmethod(cls): ... | ||
| 2587 | + | ||
| 2588 | + @final | ||
| 2589 | + @staticmethod | ||
| 2590 | + def stmethod(): ... | ||
| 2591 | + | ||
| 2592 | + # The other order doesn't work because property objects | ||
| 2593 | + # don't allow attribute assignment. | ||
| 2594 | + @property | ||
| 2595 | + @final | ||
| 2596 | + def prop(self): ... | ||
| 2597 | + | ||
| 2598 | + @final | ||
| 2599 | + @lru_cache() | ||
| 2600 | + def cached(self): ... | ||
| 2601 | + | ||
| 2602 | + # Use getattr_static because the descriptor returns the | ||
| 2603 | + # underlying function, which doesn't have __final__. | ||
| 2604 | + self.assertIs( | ||
| 2605 | + True, | ||
| 2606 | + inspect.getattr_static(Methods, "clsmethod").__final__ | ||
| 2607 | + ) | ||
| 2608 | + self.assertIs( | ||
| 2609 | + True, | ||
| 2610 | + inspect.getattr_static(Methods, "stmethod").__final__ | ||
| 2611 | + ) | ||
| 2612 | + self.assertIs(True, Methods.prop.fget.__final__) | ||
| 2613 | + self.assertIs(True, Methods.cached.__final__) | ||
| 2614 | + | ||
| 2543 | 2615 | ||
| 2544 | 2616 | class CastTests(BaseTestCase): | |
| 2545 | 2617 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2042,8 +2042,17 @@ class Leaf: | |||
| 2042 | 2042 | class Other(Leaf): # Error reported by type checker | |
| 2043 | 2043 | ... | |
| 2044 | 2044 | ||
| 2045 | - There is no runtime checking of these properties. | ||
| 2045 | + There is no runtime checking of these properties. The decorator | ||
| 2046 | + sets the ``__final__`` attribute to ``True`` on the decorated object | ||
| 2047 | + to allow runtime introspection. | ||
| 2046 | 2048 | """ | |
| 2049 | + try: | ||
| 2050 | + f.__final__ = True | ||
| 2051 | + except (AttributeError, TypeError): | ||
| 2052 | + # Skip the attribute silently if it is not writable. | ||
| 2053 | + # AttributeError happens if the object has __slots__ or a | ||
| 2054 | + # read-only property, TypeError if it's a builtin class. | ||
| 2055 | + pass | ||
| 2047 | 2056 | return f | |
| 2048 | 2057 | ||
| 2049 | 2058 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + The ``@typing.final`` decorator now sets the ``__final__`` attribute on the | ||
| 2 | + decorated object to allow runtime introspection. Patch by Jelle Zijlstra. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments