changeset c0f1b12134d8 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset&node=c0f1b12134d8
description:
        Use immutable dictionary for Transaction context

        issue10691
        review358741002
diffstat:

 CHANGELOG                       |   1 +
 doc/ref/tools/immutabledict.rst |  10 ++++++
 doc/ref/tools/index.rst         |   1 +
 trytond/model/fields/dict.py    |  20 +------------
 trytond/tests/test_protocols.py |   2 +-
 trytond/tests/test_tools.py     |  62 +++++++++++++++++++++++++++++++++++++++++
 trytond/tools/immutabledict.py  |  20 +++++++++++++
 trytond/transaction.py          |  19 +++++++-----
 8 files changed, 107 insertions(+), 28 deletions(-)

diffs (247 lines):

diff -r e5f04f5be526 -r c0f1b12134d8 CHANGELOG
--- a/CHANGELOG Sun Sep 19 00:08:37 2021 +0200
+++ b/CHANGELOG Sun Sep 19 00:30:35 2021 +0200
@@ -1,3 +1,4 @@
+* Use ImmutableDict for Transaction context
 * Use UNION for 'Or'-ed domain with subqueries
 * Add remove_forbidden_chars in tools
 * Manage errors during non-interactive operations
diff -r e5f04f5be526 -r c0f1b12134d8 doc/ref/tools/immutabledict.rst
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ref/tools/immutabledict.rst   Sun Sep 19 00:30:35 2021 +0200
@@ -0,0 +1,10 @@
+.. _ref-immutabledict:
+.. module:: trytond.tools.immutabledict
+
+=============
+ImmutableDict
+=============
+
+.. class:: ImmutableDict
+
+A class that implements an immutable dictionary.
diff -r e5f04f5be526 -r c0f1b12134d8 doc/ref/tools/index.rst
--- a/doc/ref/tools/index.rst   Sun Sep 19 00:08:37 2021 +0200
+++ b/doc/ref/tools/index.rst   Sun Sep 19 00:30:35 2021 +0200
@@ -11,3 +11,4 @@
 
     misc
     singleton
+    immutabledict
diff -r e5f04f5be526 -r c0f1b12134d8 trytond/model/fields/dict.py
--- a/trytond/model/fields/dict.py      Sun Sep 19 00:08:37 2021 +0200
+++ b/trytond/model/fields/dict.py      Sun Sep 19 00:30:35 2021 +0200
@@ -9,6 +9,7 @@
 from trytond.pool import Pool
 from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder
 from trytond.tools import grouped_slice
+from trytond.tools.immutabledict import ImmutableDict
 from trytond.transaction import Transaction
 from .field import Field, SQL_OPERATORS
 
@@ -17,25 +18,6 @@
     json.dumps, cls=JSONEncoder, separators=(',', ':'), sort_keys=True)
 
 
-class ImmutableDict(dict):
-
-    __slots__ = ()
-
-    def _not_allowed(cls, *args, **kwargs):
-        raise TypeError("Operation not allowed on ImmutableDict")
-
-    __setitem__ = _not_allowed
-    __delitem__ = _not_allowed
-    __ior__ = _not_allowed
-    clear = _not_allowed
-    pop = _not_allowed
-    popitem = _not_allowed
-    setdefault = _not_allowed
-    update = _not_allowed
-
-    del _not_allowed
-
-
 class Dict(Field):
     'Define dict field.'
     _type = 'dict'
diff -r e5f04f5be526 -r c0f1b12134d8 trytond/tests/test_protocols.py
--- a/trytond/tests/test_protocols.py   Sun Sep 19 00:08:37 2021 +0200
+++ b/trytond/tests/test_protocols.py   Sun Sep 19 00:30:35 2021 +0200
@@ -6,7 +6,7 @@
 import datetime
 from decimal import Decimal
 
-from trytond.model.fields.dict import ImmutableDict
+from trytond.tools.immutabledict import ImmutableDict
 from trytond.protocols.jsonrpc import JSONEncoder, JSONDecoder, JSONRequest
 from trytond.protocols.xmlrpc import client, XMLRequest
 
diff -r e5f04f5be526 -r c0f1b12134d8 trytond/tests/test_tools.py
--- a/trytond/tests/test_tools.py       Sun Sep 19 00:08:37 2021 +0200
+++ b/trytond/tests/test_tools.py       Sun Sep 19 00:30:35 2021 +0200
@@ -20,6 +20,7 @@
     domain_inversion, parse, simplify, merge, concat, unique_value,
     eval_domain, localize_domain,
     prepare_reference_domain, extract_reference_models)
+from trytond.tools.immutabledict import ImmutableDict
 
 
 class ToolsTestCase(unittest.TestCase):
@@ -311,6 +312,66 @@
         self.assertEqual(s, 'barfoo')
 
 
+class ImmutableDictTestCase(unittest.TestCase):
+    "Test ImmutableDict"
+
+    def test_setitem(self):
+        "__setitem__ not allowed"
+        d = ImmutableDict()
+
+        with self.assertRaises(TypeError):
+            d['foo'] = 'bar'
+
+    def test_delitem(self):
+        "__delitem__ not allowed"
+        d = ImmutableDict(foo='bar')
+
+        with self.assertRaises(TypeError):
+            del d['foo']
+
+    def test_ior(self):
+        "__ior__ not allowed"
+        d = ImmutableDict()
+
+        with self.assertRaises(TypeError):
+            d |= {'foo': 'bar'}
+
+    def test_clear(self):
+        "clear not allowed"
+        d = ImmutableDict(foo='bar')
+
+        with self.assertRaises(TypeError):
+            d.clear()
+
+    def test_pop(self):
+        "pop not allowed"
+        d = ImmutableDict(foo='bar')
+
+        with self.assertRaises(TypeError):
+            d.pop('foo')
+
+    def test_popitem(self):
+        "popitem not allowed"
+        d = ImmutableDict(foo='bar')
+
+        with self.assertRaises(TypeError):
+            d.popitem('foo')
+
+    def test_setdefault(self):
+        "setdefault not allowed"
+        d = ImmutableDict()
+
+        with self.assertRaises(TypeError):
+            d.setdefault('foo', 'bar')
+
+    def test_update(self):
+        "update not allowed"
+        d = ImmutableDict()
+
+        with self.assertRaises(TypeError):
+            d.update({'foo': 'bar'})
+
+
 class DomainInversionTestCase(unittest.TestCase):
     "Test domain_inversion"
 
@@ -866,6 +927,7 @@
     suite = unittest.TestSuite()
     for testcase in [ToolsTestCase,
             StringPartitionedTestCase,
+            ImmutableDictTestCase,
             DomainInversionTestCase]:
         suite.addTests(func(testcase))
     suite.addTest(doctest.DocTestSuite(decimal_))
diff -r e5f04f5be526 -r c0f1b12134d8 trytond/tools/immutabledict.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/tools/immutabledict.py    Sun Sep 19 00:30:35 2021 +0200
@@ -0,0 +1,20 @@
+# This file is part of Tryton.  The COPYRIGHT file at the toplevel of this
+# repository contains the full copyright notices and license terms.
+
+class ImmutableDict(dict):
+
+    __slots__ = ()
+
+    def _not_allowed(cls, *args, **kwargs):
+        raise TypeError("Operation not allowed on ImmutableDict")
+
+    __setitem__ = _not_allowed
+    __delitem__ = _not_allowed
+    __ior__ = _not_allowed
+    clear = _not_allowed
+    pop = _not_allowed
+    popitem = _not_allowed
+    setdefault = _not_allowed
+    update = _not_allowed
+
+    del _not_allowed
diff -r e5f04f5be526 -r c0f1b12134d8 trytond/transaction.py
--- a/trytond/transaction.py    Sun Sep 19 00:08:37 2021 +0200
+++ b/trytond/transaction.py    Sun Sep 19 00:30:35 2021 +0200
@@ -7,6 +7,7 @@
 from sql import Flavor
 
 from trytond.config import config
+from trytond.tools.immutabledict import ImmutableDict
 
 _cache_model = config.getint('cache', 'model')
 logger = logging.getLogger(__name__)
@@ -111,7 +112,7 @@
         self.database = database
         self.readonly = readonly
         self.close = close
-        self.context = context or {}
+        self.context = ImmutableDict(context or {})
         self.create_records = defaultdict(set)
         self.delete_records = defaultdict(set)
         self.trigger_records = defaultdict(set)
@@ -174,15 +175,16 @@
         if context is None:
             context = {}
         manager = _AttributeManager(context=self.context)
-        self.context = self.context.copy()
-        self.context.update(context)
+        ctx = self.context.copy()
+        ctx.update(context)
         if kwargs:
-            self.context.update(kwargs)
+            ctx.update(kwargs)
+        self.context = ImmutableDict(ctx)
         return manager
 
     def reset_context(self):
         manager = _AttributeManager(context=self.context)
-        self.context = {}
+        self.context = ImmutableDict()
         return manager
 
     def set_user(self, user, set_context=False):
@@ -190,12 +192,13 @@
             raise ValueError('set_context only allowed for root')
         manager = _AttributeManager(user=self.user,
                 context=self.context)
-        self.context = self.context.copy()
+        ctx = self.context.copy()
         if set_context:
             if user != self.user:
-                self.context['user'] = self.user
+                ctx['user'] = self.user
         else:
-            self.context.pop('user', None)
+            ctx.pop('user', None)
+        self.context = ImmutableDict(ctx)
         self.user = user
         return manager
 

Reply via email to