|
12 | 12 | import random |
13 | 13 | import re |
14 | 14 | import string |
| 15 | +import uuid |
15 | 16 | import warnings |
16 | 17 | from types import CodeType, FunctionType |
17 | 18 | from typing import ( |
@@ -1666,17 +1667,92 @@ def figure_out_type(value: Any) -> types.GenericType: |
1666 | 1667 | return type(value) |
1667 | 1668 |
|
1668 | 1669 |
|
1669 | | -class cached_property_no_lock(functools.cached_property): # noqa: N801 |
1670 | | - """A special version of functools.cached_property that does not use a lock.""" |
| 1670 | +GLOBAL_CACHE = {} |
| 1671 | + |
| 1672 | + |
| 1673 | +class cached_property: # noqa: N801 |
| 1674 | + """A cached property that caches the result of the function.""" |
1671 | 1675 |
|
1672 | 1676 | def __init__(self, func: Callable): |
1673 | | - """Initialize the cached_property_no_lock. |
| 1677 | + """Initialize the cached_property. |
1674 | 1678 |
|
1675 | 1679 | Args: |
1676 | 1680 | func: The function to cache. |
1677 | 1681 | """ |
1678 | | - super().__init__(func) |
1679 | | - self.lock = contextlib.nullcontext() |
| 1682 | + self._func = func |
| 1683 | + self._attrname = None |
| 1684 | + |
| 1685 | + def __set_name__(self, owner: Any, name: str): |
| 1686 | + """Set the name of the cached property. |
| 1687 | +
|
| 1688 | + Args: |
| 1689 | + owner: The owner of the cached property. |
| 1690 | + name: The name of the cached property. |
| 1691 | +
|
| 1692 | + Raises: |
| 1693 | + TypeError: If the cached property is assigned to two different names. |
| 1694 | + """ |
| 1695 | + if self._attrname is None: |
| 1696 | + self._attrname = name |
| 1697 | + |
| 1698 | + original_del = getattr(owner, "__del__", None) |
| 1699 | + |
| 1700 | + def delete_property(this: Any): |
| 1701 | + """Delete the cached property. |
| 1702 | +
|
| 1703 | + Args: |
| 1704 | + this: The object to delete the cached property from. |
| 1705 | + """ |
| 1706 | + cached_field_name = "_reflex_cache_" + name |
| 1707 | + try: |
| 1708 | + unique_id = object.__getattribute__(this, cached_field_name) |
| 1709 | + except AttributeError: |
| 1710 | + if original_del is not None: |
| 1711 | + original_del(this) |
| 1712 | + return |
| 1713 | + if unique_id in GLOBAL_CACHE: |
| 1714 | + del GLOBAL_CACHE[unique_id] |
| 1715 | + |
| 1716 | + if original_del is not None: |
| 1717 | + original_del(this) |
| 1718 | + |
| 1719 | + owner.__del__ = delete_property |
| 1720 | + |
| 1721 | + elif name != self._attrname: |
| 1722 | + raise TypeError( |
| 1723 | + "Cannot assign the same cached_property to two different names " |
| 1724 | + f"({self._attrname!r} and {name!r})." |
| 1725 | + ) |
| 1726 | + |
| 1727 | + def __get__(self, instance: Any, owner: Type | None = None): |
| 1728 | + """Get the cached property. |
| 1729 | +
|
| 1730 | + Args: |
| 1731 | + instance: The instance to get the cached property from. |
| 1732 | + owner: The owner of the cached property. |
| 1733 | +
|
| 1734 | + Returns: |
| 1735 | + The cached property. |
| 1736 | +
|
| 1737 | + Raises: |
| 1738 | + TypeError: If the class does not have __set_name__. |
| 1739 | + """ |
| 1740 | + if self._attrname is None: |
| 1741 | + raise TypeError( |
| 1742 | + "Cannot use cached_property on a class without __set_name__." |
| 1743 | + ) |
| 1744 | + cached_field_name = "_reflex_cache_" + self._attrname |
| 1745 | + try: |
| 1746 | + unique_id = object.__getattribute__(instance, cached_field_name) |
| 1747 | + except AttributeError: |
| 1748 | + unique_id = uuid.uuid4().int |
| 1749 | + object.__setattr__(instance, cached_field_name, unique_id) |
| 1750 | + if unique_id not in GLOBAL_CACHE: |
| 1751 | + GLOBAL_CACHE[unique_id] = self._func(instance) |
| 1752 | + return GLOBAL_CACHE[unique_id] |
| 1753 | + |
| 1754 | + |
| 1755 | +cached_property_no_lock = cached_property |
1680 | 1756 |
|
1681 | 1757 |
|
1682 | 1758 | class CachedVarOperation: |
|
0 commit comments