diff configmix/__init__.py @ 111:d51a18e5b0e3

Reimplement configmix.safe_merge() do to a deepcopy of all source configurations when merging. configmix.safe_merge() does now a deepcopy of all source configurations when merging. Changes in configuration instances after will not be reflected in the merged configuration any more.
author Franz Glasner <hg@dom66.de>
date Sat, 24 Mar 2018 18:47:54 +0100
parents 29cf359ddf4d
children c50ad93eb5dc
line wrap: on
line diff
--- a/configmix/__init__.py	Sat Mar 24 16:06:08 2018 +0100
+++ b/configmix/__init__.py	Sat Mar 24 18:47:54 2018 +0100
@@ -124,17 +124,34 @@
 
 
 def safe_merge(user, default):
-    """A more safe version of :func:`merge` that makes shallow copies of
+    """A more safe version of :func:`merge` that makes deep copies of
     the returned container objects.
 
+    No given argument is ever changed inplace. Every object from `default`
+    is decoupled from the result -- so changing the `default` configuration
+    lates does not yield into a merged configuration later.
+
     """
     if user is None:
-        return copy.copy(default)
-    user = copy.copy(user)
+        return copy.deepcopy(default)
+    user = copy.deepcopy(user)
     if isinstance(user, dict) and isinstance(default, dict):
         for k, v in default.items():
             if k not in user:
-                user[k] = copy.copy(v)
+                user[k] = copy.deepcopy(v)
             else:
-                user[k] = _merge(user[k], v)
+                user[k] = _safe_merge(user[k], v)
     return user
+
+
+def _safe_merge(user, default):
+    """Recursion helper for :meth:`safe_merge`
+
+    """
+    if isinstance(user, dict) and isinstance(default, dict):
+        for k, v in default.items():
+            if k not in user:
+                user[k] = copy.deepcopy(v)
+            else:
+                user[k] = _safe_merge(user[k], v)
+    return user