diff configmix/__init__.py @ 276:af371f9c016d

Allow deletion of key-value pairs when merging is done. When encountering the "{{::DEL::}}" special value the corresponding key-value pair is deleted.
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 03 Oct 2020 17:11:41 +0200
parents e2fd8fea1a4c
children 57fca7448740
line wrap: on
line diff
--- a/configmix/__init__.py	Sat Oct 03 15:52:30 2020 +0200
+++ b/configmix/__init__.py	Sat Oct 03 17:11:41 2020 +0200
@@ -13,7 +13,7 @@
 from __future__ import division, print_function, absolute_import
 
 
-__version__ = "0.10.1.dev1"
+__version__ = "0.11.0.dev1"
 
 __revision__ = "|VCSRevision|"
 __date__ = "|VCSJustDate|"
@@ -50,6 +50,12 @@
 
 """
 
+DEL_VALUE = u("{{::DEL::}}")
+"""Value for configuration items to signal that the corresponding
+key-value is to be deleted when configurations are merged
+
+"""
+
 
 def load(*files, **kwargs):
     """Load the given configuration files, merge them in the given order
@@ -418,12 +424,17 @@
               The configuration in `default` will be changed **inplace**
               when filtering out comments (which is the default).
 
+    If a value in `user` is equal to :data:`.DEL_VALUE`
+    (``{{::DEL::}}``) the corresponding key will be deleted from the
+    merged output.
+
     From http://stackoverflow.com/questions/823196/yaml-merge-in-python
 
     """
     if user is None:
         if filter_comments:
             _filter_comments(default)
+        _filter_deletions(default)
         return default
     if filter_comments:
         _filter_comments(user)
@@ -431,10 +442,15 @@
         for k, v in default.items():
             if filter_comments and _is_comment(k):
                 continue
-            if k not in user:
+            if k in user:
+                if user[k] == DEL_VALUE:
+                    # do not copy
+                    del user[k]
+                else:
+                    user[k] = _merge(user[k], v, filter_comments)
+            else:
                 user[k] = v
-            else:
-                user[k] = _merge(user[k], v, filter_comments)
+    _filter_deletions(user)
     return user
 
 
@@ -446,10 +462,14 @@
         for k, v in default.items():
             if filter_comments and _is_comment(k):
                 continue
-            if k not in user:
+            if k in user:
+                if user[k] == DEL_VALUE:
+                    # do not copy
+                    del user[k]
+                else:
+                    user[k] = _merge(user[k], v, filter_comments)
+            else:
                 user[k] = v
-            else:
-                user[k] = _merge(user[k], v, filter_comments)
     return user
 
 
@@ -466,6 +486,7 @@
     if user is None:
         if filter_comments:
             _filter_comments(default)
+        _filter_deletions(default)
         return copy.deepcopy(default)
     user = copy.deepcopy(user)
     if filter_comments:
@@ -474,10 +495,15 @@
         for k, v in default.items():
             if filter_comments and _is_comment(k):
                 continue
-            if k not in user:
+            if k in user:
+                if user[k] == DEL_VALUE:
+                    # do not copy
+                    del user[k]
+                else:
+                    user[k] = _safe_merge(user[k], v, filter_comments)
+            else:
                 user[k] = copy.deepcopy(v)
-            else:
-                user[k] = _safe_merge(user[k], v, filter_comments)
+    _filter_deletions(user)
     return user
 
 
@@ -489,10 +515,14 @@
         for k, v in default.items():
             if filter_comments and _is_comment(k):
                 continue
-            if k not in user:
+            if k in user:
+                if user[k] == DEL_VALUE:
+                    # do not copy
+                    del user[k]
+                else:
+                    user[k] = _safe_merge(user[k], v, filter_comments)
+            else:
                 user[k] = copy.deepcopy(v)
-            else:
-                user[k] = _safe_merge(user[k], v, filter_comments)
     return user
 
 
@@ -525,6 +555,23 @@
     return False
 
 
+def _filter_deletions(d):
+    """Recursively filter deletions in the dict `d`.
+
+    Deletions have values that equal :data:`.DEL_VALUE`.
+
+    """
+    if not isinstance(d, dict):
+        return
+    # use a copy of the items because we change `d` while iterating
+    for k, v in list(d.items()):
+        if v == DEL_VALUE:
+            del d[k]
+        else:
+            if isinstance(d[k], dict):
+                _filter_deletions(d[k])
+
+
 #
 # Init loader defaults: mode->loader and extension->mode
 #