Support cached_property on object with __slots__

Closes #12894
This commit is contained in:
Cédric Krier committed 2024-01-05 01:36:50 +01:00
1 parent 8eea0020b7
commit be1aac4fac
5 files changed
+91 -9

No files matched your search

+1 -1
View File
@@ -53,7 +53,7 @@ def remove_comment(stream):
class Invoice(Model):
"EDocument UN/CEFACT Invoice"
__name__ = 'edocument.uncefact.invoice'
__no_slots__ = True # to work with cached_property
__slots__ = ('invoice',)
@classmethod
def __setup__(cls):
+1
View File
@@ -1,3 +1,4 @@
* Support cached_property on object with __slots__
* Allow creating indexes concurrently
* Allow skipping index creation
* Add assert methods to test tools
+3 -1
View File
@@ -32,7 +32,9 @@ class Model(URLMixin, PoolBase, metaclass=ModelMeta):
"""
Define a model in Tryton.
"""
__slots__ = ('_id', '_values', '_init_values', '_removed', '_deleted')
__slots__ = (
'_id', '_values', '_init_values', '_removed', '_deleted',
'__weakref__')
_rec_name = 'name'
id = fields.Integer(lazy_gettext('ir.msg_ID'), readonly=True)
+54 -2
View File
@@ -12,8 +12,8 @@ import sql
import sql.operators
from trytond.tools import (
decimal_, escape_wildcard, file_open, firstline, grouped_slice,
is_full_text, is_instance_method, likify, lstrip_wildcard,
cached_property, decimal_, escape_wildcard, file_open, firstline,
grouped_slice, is_full_text, is_instance_method, likify, lstrip_wildcard,
pairwise_longest, reduce_domain, reduce_ids, remove_forbidden_chars,
rstrip_wildcard, slugify, sortable_values, strip_wildcard, timezone,
unescape_wildcard)
@@ -1214,6 +1214,58 @@ class QRCodeTestCase(unittest.TestCase):
self.assertIsNotNone(image.getvalue())
class CachedProperty:
_count = 0
def get_count(self):
self._count += 1
return self._count
cached_count = cached_property(get_count)
class CachedPropertySlots:
__slots__ = ('_count', '__weakref__')
def __init__(self):
self._count = 0
def get_count(self):
self._count += 1
return self._count
cached_count = cached_property(get_count)
class CachedPropertyTestCase(unittest.TestCase):
"Test cached_property"
def test_cached_property_clear(self):
"Test clear cached property"
item = CachedProperty()
self.assertEqual(item.cached_count, 1)
del item.cached_count
self.assertEqual(item.cached_count, 2)
def test_cached_property_with_slots(self):
"Test cached property with slots"
item = CachedPropertySlots()
self.assertEqual(item.cached_count, 1)
self.assertEqual(item.cached_count, 1)
self.assertEqual(item.get_count(), 2)
self.assertEqual(item.cached_count, 1)
def test_cached_property_with_slots_clear(self):
"Test clear cached property with slots"
item = CachedPropertySlots()
self.assertEqual(item.cached_count, 1)
del item.cached_count
self.assertEqual(item.cached_count, 2)
def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite(decimal_))
return tests
+32 -5
View File
@@ -1,11 +1,8 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import posixpath
try:
from functools import cached_property
except ImportError:
from werkzeug.utils import cached_property
from functools import cached_property
from weakref import WeakKeyDictionary
try:
from werkzeug.security import safe_join
@@ -21,6 +18,36 @@ from .misc import (
rstrip_wildcard, slugify, sortable_values, sql_pairing, strip_wildcard,
unescape_wildcard)
_NOT_FOUND = object()
class cached_property(cached_property):
_cache = None
def __get__(self, instance, owner=None):
if instance is None:
return self
cache = getattr(instance, '__dict__', None)
if cache is None: # __slots__
if self._cache is None:
self._cache = WeakKeyDictionary()
val = self._cache.get(instance, _NOT_FOUND)
if val is _NOT_FOUND:
self._cache[instance] = val = self.func(instance)
return val
else:
return super().__get__(instance, owner)
def __delete__(self, instance):
cache = getattr(instance, '__dict__', None)
try:
if cache:
del cache[self.attrname]
elif self._cache:
del self._cache[instance]
except KeyError:
pass
class ClassProperty(property):
def __get__(self, cls, owner):