changeset 647:df58983f28a2

Allow to disable the internal caching in configmix. Also allow to re-enable internal caching.
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 11 Mar 2022 01:53:08 +0100
parents f8cb74b447de
children e8f3e970e411
files configmix/config.py tests/test.py
diffstat 2 files changed, 117 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/configmix/config.py	Mon Mar 07 09:11:22 2022 +0100
+++ b/configmix/config.py	Fri Mar 11 01:53:08 2022 +0100
@@ -451,14 +451,23 @@
         #
         # PY2.7 compat: must be set before calling the superclass' __init__
         #
-        self.__lookup_cache = {}
-        self.__interpolation_cache = {}
+        self.enable_cache()
         super(Configuration, self).__init__(*args, **kwds)
 
     def clear_cache(self):
         """Clear the internal lookup cache and the interpolation cache"""
-        self.__lookup_cache.clear()
-        self.__interpolation_cache.clear()
+        if self.__lookup_cache is not None:
+            self.__lookup_cache.clear()
+        if self.__interpolation_cache is not None:
+            self.__interpolation_cache.clear()
+
+    def disable_cache(self):
+        self.__lookup_cache = None
+        self.__interpolation_cache = None
+
+    def enable_cache(self):
+        self.__lookup_cache = {}
+        self.__interpolation_cache = {}
 
     def __getitem__(self, key):
         """Mapping and list interface that forwards to :meth:`~.getvarl_s`
@@ -834,28 +843,33 @@
         """
         if not path:
             return self
-        v = self.__lookup_cache.get(path, _MARKER)
-        if v is not _MARKER:
-            if v is _MISSING:
-                raise KeyError(
-                    "Configuration variable %r not found"
-                    " (negative internal cache value)" % (path,))
-            else:
-                return v
+        use_cache = self.__lookup_cache is not None
+        if use_cache:
+            v = self.__lookup_cache.get(path, _MARKER)
+            if v is not _MARKER:
+                if v is _MISSING:
+                    raise KeyError(
+                        "Configuration variable %r not found"
+                        " (negative internal cache value)" % (path,))
+                else:
+                    return v
         eiref = self.expand_if_reference
         try:
             v = eiref(super(Configuration, self).__getitem__(path[0]))
             for p in path[1:]:
                 v = eiref(v[p])
         except TypeError:
-            self.__lookup_cache[path] = _MISSING
+            if use_cache:
+                self.__lookup_cache[path] = _MISSING
             raise KeyError(
                 "Configuration variable %r not found"
                 "(missing intermediate keys?)" % (path,))
         except KeyError:
-            self.__lookup_cache[path] = _MISSING
+            if use_cache:
+                self.__lookup_cache[path] = _MISSING
             raise
-        self.__lookup_cache[path] = v
+        if use_cache:
+            self.__lookup_cache[path] = v
         return v
 
     def _lookupref(self, key):
@@ -936,21 +950,23 @@
         start = s.find(_STARTTOK, 0)
         if start < 0:
             return s
-        res = self.__interpolation_cache.get(s, _MARKER)
-        if res is not _MARKER:
-            if res is _MISSING:
-                warnings.warn("Cannot interpolate variables in string "
-                              "%r (cached)" % (s, ),
-                              UserWarning,
-                              stacklevel=1)
-                raise KeyError("Cannot interpolate variables in string "
-                               "%r (cached)" % (s, ))
-            else:
-                return res
+        use_cache = self.__interpolation_cache is not None
+        if use_cache:
+            res = self.__interpolation_cache.get(s, _MARKER)
+            if res is not _MARKER:
+                if res is _MISSING:
+                    warnings.warn("Cannot interpolate variables in string "
+                                  "%r (cached)" % (s, ),
+                                  UserWarning,
+                                  stacklevel=1)
+                    raise KeyError("Cannot interpolate variables in string "
+                                   "%r (cached)" % (s, ))
+                else:
+                    return res
         res = []
         res_append = res.append
         rest = 0
-        use_cache = True
+        cacheable = True
         while start != -1:
             res_append(s[rest:start])
             end = s.find(_ENDTOK, start)
@@ -968,14 +984,15 @@
                 elif EMPTY_FILTER in filters:
                     varvalue = _EMPTY_STR
                 else:
-                    self.__interpolation_cache[s] = _MISSING
+                    if use_cache and cacheable:
+                        self.__interpolation_cache[s] = _MISSING
                     warnings.warn("Cannot interpolate variable %r in string "
                                   "%r" % (varname, s, ),
                                   UserWarning,
                                   stacklevel=1)
                     raise
             if not cacheable:
-                use_cache = False
+                cacheable = False
             varvalue = self._apply_filters(filters, varvalue)
             rest = end + 2
             #
@@ -983,7 +1000,7 @@
             # the whole `s` is just one expansion
             #
             if (start == 0) and (rest == len_s):
-                if use_cache:
+                if use_cache and cacheable:
                     self.__interpolation_cache[s] = varvalue
                 return varvalue
             if varvalue is None:
@@ -994,7 +1011,7 @@
             start = s.find(_STARTTOK, rest)
         res_append(s[rest:])
         res = _EMPTY_STR.join(res)
-        if use_cache:
+        if use_cache and cacheable:
             self.__interpolation_cache[s] = res
         return res
 
--- a/tests/test.py	Mon Mar 07 09:11:22 2022 +0100
+++ b/tests/test.py	Fri Mar 11 01:53:08 2022 +0100
@@ -439,10 +439,31 @@
                          os.path.join(TESTDATADIR, "conf24.toml"))
         x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
         self.assertEqual(10, x)
+        x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
+        self.assertEqual(10, x)
+
+    def test12a_Empty_filter_pass_through_without_cache(self):
+        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
+                         os.path.join(TESTDATADIR, "conf21.yml"),
+                         os.path.join(TESTDATADIR, "conf22.ini"),
+                         os.path.join(TESTDATADIR, "conf23.json"),
+                         os.path.join(TESTDATADIR, "conf24.toml"))
+        cfg.disable_cache()
+        x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
+        self.assertEqual(10, x)
+        x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
+        self.assertEqual(10, x)
 
     def test13_keyerror(self):
         cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
         self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")
+        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")        
+
+    def test13a_keyerror_without_cache(self):
+        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
+        cfg.disable_cache()
+        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")
+        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")
 
     def test14_getvar_with_default(self):
         cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
@@ -458,6 +479,16 @@
         cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
         self.assertEqual("999", cfg.getvar_s("non.existing.key",
                                              default="999"))
+        self.assertEqual("999", cfg.getvar_s("non.existing.key",
+                                             default="999"))
+
+    def test15a_getvar_s_with_default_without_cache(self):
+        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
+        cfg.disable_cache()
+        self.assertEqual("999", cfg.getvar_s("non.existing.key",
+                                             default="999"))
+        self.assertEqual("999", cfg.getvar_s("non.existing.key",
+                                             default="999"))
 
     def test15_getvar_s_with_original_default(self):
         # The default must be the original and not a copy
@@ -534,6 +565,20 @@
                                                "intl.fallback",
                                                default=None))
 
+    def test20a_getfirstvar_existing_without_cache(self):
+        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
+        cfg.disable_cache()
+        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain"))
+        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain",
+                                                           "intl.fallback"))
+        self.assertEqual("de", cfg.getfirstvar("intl.fallback",
+                                               "intl.domain",
+                                               default=None))
+
+        self.assertEqual("de", cfg.getfirstvar("intl.non.existing",
+                                               "intl.fallback",
+                                               default=None))
+
     def test21_getfirstvar_s_existing(self):
         cfg = self._load(
             os.path.join(TESTDATADIR, "conf20.yml"),
@@ -558,6 +603,20 @@
             "intl.non.existing",
             "intl.non.existing2")
 
+    def test22a_getfirstvar_s_non_existing_without_cache(self):
+        cfg = self._load(
+            os.path.join(TESTDATADIR, "conf20.yml"),
+            os.path.join(TESTDATADIR, "conf21.yml"))
+        cfg.disable_cache()
+        self.assertIsNone(
+            cfg.getfirstvar_s("intl.non.existing", "intl.non.existing2",
+                              default=None))
+        self.assertRaises(
+            KeyError,
+            cfg.getfirstvar_s,
+            "intl.non.existing",
+            "intl.non.existing2")
+
     def test23_getfirstintvar_s_nonexisting(self):
         cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
         self.assertIsNone(cfg.getfirstintvar_s("db.non.existing.key",
@@ -868,6 +927,16 @@
         cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
         cfg.clear_cache()
 
+    def test46_reenable_cache(self):
+        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
+        self.assertTrue(cfg.getvarl() is cfg)        
+        cfg.disable_cache()
+        self.assertTrue(cfg.getvarl() is cfg)        
+        cfg.clear_cache()
+        self.assertTrue(cfg.getvarl() is cfg)        
+        cfg.enable_cache()
+        self.assertTrue(cfg.getvarl() is cfg)
+
 
 class T02LoadAndMerge(_T02MixinLoadAndMerge, unittest.TestCase):