Skip to content

Commit

Permalink
[utils] Added utils.merge_list #56
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed Jan 16, 2017
1 parent aec2898 commit f91b7b5
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 6 deletions.
6 changes: 4 additions & 2 deletions docs/source/general/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,13 @@ refactor the previous code as follows:
}
router2 = OpenWrt(router2_config, templates=[dhcp_template])
The function used under the hood to merge dictionaries and lists
is ``netjsonconfig.utils.merge_config``:
The functions used under the hood to merge configurations and templates
are ``netjsonconfig.utils.merge_config`` and ``netjsonconfig.utils.merge_list``:

.. autofunction:: netjsonconfig.utils.merge_config

.. autofunction:: netjsonconfig.utils.merge_list

.. _multiple_templates:

Multiple template inheritance
Expand Down
37 changes: 34 additions & 3 deletions netjsonconfig/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def merge_config(template, config):
* simple values (eg: ``str``, ``int``, ``float``, ecc) in ``config`` will
overwrite the ones in ``template``
* values of type ``list`` in both ``config`` and ``template`` will be summed
in order to create a list which contains elements of both
* values of type ``list`` in both ``config`` and ``template`` will be
merged using to the ``merge_list`` function
* values of type ``dict`` will be merged recursively
:param template: template ``dict``
Expand All @@ -27,12 +27,43 @@ def merge_config(template, config):
node = result.get(key, {})
result[key] = merge_config(node, value)
elif isinstance(value, list) and isinstance(result.get(key), list):
result[key] = deepcopy(result[key]) + deepcopy(value)
result[key] = merge_list(result[key], value)
else:
result[key] = value
return result


def merge_list(list1, list2):
"""
Merges ``list2`` on top of ``list1``.
If both lists contain dictionaries which have keys like
``name`` or ``id`` of equal values, those dicts are merged
(dicts in ``list2`` will override dicts in ``list1``).
The remaining elements will be summed in order to create a list
which contains elements of both lists.
:param list1: list from template
:param list2: list from config
:returns: merged ``list``
"""
dict_map = {'list1': OrderedDict(), 'list2': OrderedDict()}
counter = 1
for list_ in [list1, list2]:
container = dict_map['list{0}'.format(counter)]
for el in list_:
if isinstance(el, dict) and 'name' in el:
key = el['name']
elif isinstance(el, dict) and 'id' in el:
key = el['id']
else:
key = id(el)
container[key] = deepcopy(el)
counter += 1
merged = merge_config(dict_map['list1'], dict_map['list2'])
return list(merged.values())


def sorted_dict(dictionary):
return OrderedDict(sorted(dictionary.items()))

Expand Down
20 changes: 19 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest

from netjsonconfig.utils import evaluate_vars, merge_config
from netjsonconfig.utils import evaluate_vars, merge_config, merge_list


class TestUtils(unittest.TestCase):
Expand Down Expand Up @@ -168,3 +168,21 @@ def test_evaluate_vars_immersed(self):

def test_evaluate_vars_one_char(self):
self.assertEqual(evaluate_vars('{{ a }}', {'a': 'letter-A'}), 'letter-A')

def test_merge_list_override(self):
template = [{"name": "test1", "tx": 1}]
config = [{"name": "test1", "tx": 2}]
result = merge_list(template, config)
self.assertEqual(result, config)

def test_merge_list_union_and_override(self):
template = [{"id": "test1", "a": "a"}]
config = [
{"id": "test1", "a": "0", "b": "b"},
{"id": "test2", "c": "c"}
]
result = merge_list(template, config)
self.assertEqual(result, [
{"id": "test1", "a": "0", "b": "b"},
{"id": "test2", "c": "c"}
])

0 comments on commit f91b7b5

Please sign in to comment.