Page MenuHomeSoftware Heritage

D1017.id3235.diff
No OneTemporary

D1017.id3235.diff

diff --git a/swh/core/config.py b/swh/core/config.py
--- a/swh/core/config.py
+++ b/swh/core/config.py
@@ -7,6 +7,8 @@
import logging
import os
import yaml
+from itertools import chain
+from copy import deepcopy
logger = logging.getLogger(__name__)
@@ -178,6 +180,77 @@
return full_config
+def merge_configs(base, other):
+ """Merge two config dictionaries
+
+ This does merge config dicts recursively, with the rules, for every value
+ of the dicts (with 'val' not being a dict):
+
+ - None + type -> type
+ - type + None -> None
+ - dict + dict -> dict (merged)
+ - val + dict -> TypeError
+ - dict + val -> TypeError
+ - val + val -> val (other)
+
+ so merging
+
+ {
+ 'key1': {
+ 'skey1': value1,
+ 'skey2': {'sskey1': value2},
+ },
+ 'key2': value3,
+ }
+
+ with
+
+ {
+ 'key1': {
+ 'skey1': value4,
+ 'skey2': {'sskey2': value5},
+ },
+ 'key3': value6,
+ }
+
+ will give:
+
+ {
+ 'key1': {
+ 'skey1': value4, # <-- note this
+ 'skey2': {
+ 'sskey1': value2,
+ 'sskey2': value5,
+ },
+ },
+ 'key2': value3,
+ 'key3': value6,
+ }
+
+ Note that no type checking is done for anything but dicts.
+ """
+ if not isinstance(base, dict) or not isinstance(other, dict):
+ raise TypeError(
+ 'Cannot merge a %s with a %s' % (type(base), type(other)))
+
+ output = {}
+ allkeys = set(chain(base.keys(), other.keys()))
+ for k in allkeys:
+ vb = base.get(k)
+ vo = other.get(k)
+
+ if isinstance(vo, dict):
+ output[k] = merge_configs(vb is not None and vb or {}, vo)
+ elif isinstance(vb, dict) and k in other and other[k] is not None:
+ output[k] = merge_configs(vb, vo is not None and vo or {})
+ elif k in other:
+ output[k] = deepcopy(vo)
+ else:
+ output[k] = deepcopy(vb)
+
+ return output
+
+
def swh_config_paths(base_filename):
"""Return the Software Heritage specific configuration paths for the given
filename."""
diff --git a/swh/core/tests/test_config.py b/swh/core/tests/test_config.py
--- a/swh/core/tests/test_config.py
+++ b/swh/core/tests/test_config.py
@@ -223,3 +223,90 @@
assert os.path.exists(conf['path1']), "path1 should still exist!"
assert os.path.exists(conf['path2']), "path2 should now exist."
+
+
+def test_merge_config():
+ cfg_a = {
+ 'a': 42,
+ 'b': [1, 2, 3],
+ 'c': None,
+ 'd': {'gheez': 27},
+ 'e': {
+ 'ea': 'Mr. Bungle',
+ 'eb': None,
+ 'ec': [11, 12, 13],
+ 'ed': {'eda': 'Secret Chief 3',
+ 'edb': 'Faith No More'},
+ 'ee': 451,
+ },
+ 'f': 'Janis',
+ }
+ cfg_b = {
+ 'a': 43,
+ 'b': [41, 42, 43],
+ 'c': 'Tom Waits',
+ 'd': None,
+ 'e': {
+ 'ea': 'Igorrr',
+ 'ec': [51, 52],
+ 'ed': {'edb': 'Sleepytime Gorilla Museum',
+ 'edc': 'Nils Peter Molvaer'},
+ },
+ 'g': 'Hüsker Dü',
+ }
+
+ # merge A, B
+ cfg_m = config.merge_configs(cfg_a, cfg_b)
+ assert cfg_m == {
+ 'a': 43, # b takes precedence
+ 'b': [41, 42, 43], # b takes precedence
+ 'c': 'Tom Waits', # b takes precedence
+ 'd': None, # b['d'] takes precedence (explicit None)
+ 'e': {
+ 'ea': 'Igorrr', # a takes precedence
+ 'eb': None, # only in a
+ 'ec': [51, 52], # b takes precedence
+ 'ed': {
+ 'eda': 'Secret Chief 3', # only in a
+ 'edb': 'Sleepytime Gorilla Museum', # b takes precedence
+ 'edc': 'Nils Peter Molvaer'}, # only defined in b
+ 'ee': 451,
+ },
+ 'f': 'Janis', # only defined in a
+ 'g': 'Hüsker Dü', # only defined in b
+ }
+
+ # merge B, A
+ cfg_m = config.merge_configs(cfg_b, cfg_a)
+ assert cfg_m == {
+ 'a': 42, # a takes precedence
+ 'b': [1, 2, 3], # a takes precedence
+ 'c': None, # a takes precedence
+ 'd': {'gheez': 27}, # a takes precedence
+ 'e': {
+ 'ea': 'Mr. Bungle', # a takes precedence
+ 'eb': None, # only defined in a
+ 'ec': [11, 12, 13], # a takes precedence
+ 'ed': {
+ 'eda': 'Secret Chief 3', # only in a
+ 'edb': 'Faith No More', # a takes precedence
+ 'edc': 'Nils Peter Molvaer'}, # only in b
+ 'ee': 451,
+ },
+ 'f': 'Janis', # only in a
+ 'g': 'Hüsker Dü', # only in b
+ }
+
+
+def test_merge_config_type_error():
+ for v in (1, 'str', None):
+ with pytest.raises(TypeError):
+ config.merge_configs(v, {})
+ with pytest.raises(TypeError):
+ config.merge_configs({}, v)
+
+ for v in (1, 'str'):
+ with pytest.raises(TypeError):
+ config.merge_configs({'a': v}, {'a': {}})
+ with pytest.raises(TypeError):
+ config.merge_configs({'a': {}}, {'a': v})

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 30, 3:51 PM (1 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3223413

Event Timeline