details: https://code.tryton.org/tryton/commit/950ce335bfce
branch: default
user: Cédric Krier <[email protected]>
date: Mon Nov 03 23:08:31 2025 +0100
description:
Store only immutable structure or make a shallow copy in MemoryCache
Closes #14347
diffstat:
trytond/CHANGELOG | 1 +
trytond/doc/ref/cache.rst | 5 +++++
trytond/trytond/cache.py | 18 +++++++++++++++---
trytond/trytond/ir/action.py | 6 +++++-
trytond/trytond/ir/model.py | 2 ++
trytond/trytond/ir/ui/view.py | 2 +-
trytond/trytond/tests/test_cache.py | 2 +-
7 files changed, 30 insertions(+), 6 deletions(-)
diffs (132 lines):
diff -r 5395d22d4a07 -r 950ce335bfce trytond/CHANGELOG
--- a/trytond/CHANGELOG Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/CHANGELOG Mon Nov 03 23:08:31 2025 +0100
@@ -1,3 +1,4 @@
+* Store only immutable structure in MemoryCache
* Replace ``clean_days`` with ``log_size`` in the cron configuration
* Enforce access check in export_data (issue14366)
* Include the traceback only in RPC responses in development mode (issue14354)
diff -r 5395d22d4a07 -r 950ce335bfce trytond/doc/ref/cache.rst
--- a/trytond/doc/ref/cache.rst Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/doc/ref/cache.rst Mon Nov 03 23:08:31 2025 +0100
@@ -90,3 +90,8 @@
By default Tryton uses a MemoryCache, but this behaviour can be overridden
by setting a fully qualified name of an alternative class defined in the
``class`` of the :ref:`config-cache` section.
+
+.. note::
+
+ The MemoryCache convert the stored values into immutable structure and make
+ a copy for other structures.
diff -r 5395d22d4a07 -r 950ce335bfce trytond/trytond/cache.py
--- a/trytond/trytond/cache.py Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/trytond/cache.py Mon Nov 03 23:08:31 2025 +0100
@@ -1,12 +1,13 @@
# 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 copy
import datetime as dt
import json
import logging
import selectors
import threading
from collections import OrderedDict, defaultdict
-from copy import deepcopy
+from types import MappingProxyType
from uuid import uuid4
from weakref import WeakKeyDictionary
@@ -54,6 +55,17 @@
return o
+def immutable(o):
+ if isinstance(o, dict):
+ return MappingProxyType({k: immutable(v) for k, v in o.items()})
+ elif isinstance(o, list):
+ return tuple(immutable(v) for v in o)
+ elif isinstance(o, set):
+ return frozenset(immutable(v) for v in o)
+ else:
+ return copy.copy(o)
+
+
def _get_modules(cursor):
ir_module = Table('ir_module')
cursor.execute(*ir_module.select(
@@ -199,7 +211,7 @@
return default
cache.move_to_end(key)
self.hit += 1
- return deepcopy(result)
+ return result
except (KeyError, TypeError):
self.miss += 1
return default
@@ -212,7 +224,7 @@
else:
expire = None
try:
- cache[key] = (expire, deepcopy(value))
+ cache[key] = (expire, immutable(value))
except TypeError:
pass
return value
diff -r 5395d22d4a07 -r 950ce335bfce trytond/trytond/ir/action.py
--- a/trytond/trytond/ir/action.py Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/trytond/ir/action.py Mon Nov 03 23:08:31 2025 +0100
@@ -1,5 +1,6 @@
# 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 copy
import os
from collections import defaultdict
from functools import partial
@@ -731,7 +732,10 @@
cls._template_cache.clear()
def get_template_cached(self):
- return self._template_cache.get(self.id)
+ template = self._template_cache.get(self.id)
+ if template is not None:
+ template = copy.copy(template)
+ return template
def set_template_cached(self, template):
self._template_cache.set(self.id, template)
diff -r 5395d22d4a07 -r 950ce335bfce trytond/trytond/ir/model.py
--- a/trytond/trytond/ir/model.py Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/trytond/ir/model.py Mon Nov 03 23:08:31 2025 +0100
@@ -186,6 +186,8 @@
if issubclass(pool_get(m), classes))
items = list(items)
cls._get_names_cache.set(key, items)
+ else:
+ items = list(items)
return items
@classmethod
diff -r 5395d22d4a07 -r 950ce335bfce trytond/trytond/ir/ui/view.py
--- a/trytond/trytond/ir/ui/view.py Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/trytond/ir/ui/view.py Mon Nov 03 23:08:31 2025 +0100
@@ -294,7 +294,7 @@
key = (self.id, model)
result = self._view_get_cache.get(key)
if result:
- return result
+ return result.copy()
if self.inherit:
if self.inherit.model == model:
return self.inherit.view_get(model=model)
diff -r 5395d22d4a07 -r 950ce335bfce trytond/trytond/tests/test_cache.py
--- a/trytond/trytond/tests/test_cache.py Tue Oct 21 11:00:36 2025 +0200
+++ b/trytond/trytond/tests/test_cache.py Mon Nov 03 23:08:31 2025 +0100
@@ -133,7 +133,7 @@
cache.set('foo', value)
value.remove('bar')
- self.assertEqual(cache.get('foo'), ['bar'])
+ self.assertEqual(cache.get('foo'), ('bar',))
@with_transaction()
def test_memory_cache_drop(self):