Import of working code base
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/mergers/__init__.py b/mergers/__init__.py
new file mode 100644
index 0000000..ada8bd8
--- /dev/null
+++ b/mergers/__init__.py
@@ -0,0 +1,9 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+
diff --git a/mergers/base.py b/mergers/base.py
new file mode 100644
index 0000000..b9a23dc
--- /dev/null
+++ b/mergers/base.py
@@ -0,0 +1,12 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+class BaseMerger(object):
+
+ def merge(self, first, second):
+ raise NotImplementedError
diff --git a/mergers/dict.py b/mergers/dict.py
new file mode 100644
index 0000000..77f7239
--- /dev/null
+++ b/mergers/dict.py
@@ -0,0 +1,10 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from update import Update
+from recursive_extend import RecursiveExtend
diff --git a/mergers/dict/__init__.py b/mergers/dict/__init__.py
new file mode 100644
index 0000000..8346017
--- /dev/null
+++ b/mergers/dict/__init__.py
@@ -0,0 +1,11 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from update import DictUpdate
+from recursive_update import DictRecursiveUpdate
+from recursive_policy_update import DictRecursivePolicyUpdate
diff --git a/mergers/dict/base.py b/mergers/dict/base.py
new file mode 100644
index 0000000..92fdd1b
--- /dev/null
+++ b/mergers/dict/base.py
@@ -0,0 +1,12 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.base import BaseMerger
+
+class BaseDictMerger(BaseMerger):
+ pass
diff --git a/mergers/dict/recursive_policy_update.py b/mergers/dict/recursive_policy_update.py
new file mode 100644
index 0000000..f3bcb3e
--- /dev/null
+++ b/mergers/dict/recursive_policy_update.py
@@ -0,0 +1,32 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictRecursivePolicyUpdate(BaseDictMerger):
+
+ def __init__(self, policy=None):
+ super(DictRecursivePolicyUpdate, self).__init__()
+ if policy is None:
+ policy = {(dict,dict) : self.merge,
+ (list,list) : lambda x,y: x+y,
+ (dict,list) : lambda x,y: self.merge(x, dict(y)),
+ None : lambda x,y: y
+ }
+ self._policy = policy
+
+ def merge(self, first, second):
+ ret = first.copy()
+ for k,v in second.iteritems():
+ if k in ret:
+ pfn = self._policy.get((type(ret[k]), type(v)),
+ self._policy.get(None))
+ ret[k] = pfn(ret[k], v)
+ else:
+ ret[k] = v
+ return ret
diff --git a/mergers/dict/recursive_update.py b/mergers/dict/recursive_update.py
new file mode 100644
index 0000000..ba94c1f
--- /dev/null
+++ b/mergers/dict/recursive_update.py
@@ -0,0 +1,25 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictRecursiveUpdate(BaseDictMerger):
+
+ def merge(self, first, second):
+ ret = first.copy()
+ for k,v in second.iteritems():
+ if k in ret:
+ if isinstance(ret[k], dict):
+ if isinstance(v, (list, tuple)):
+ v = dict(v)
+ ret[k] = self.merge(ret[k], v)
+ else:
+ ret[k] = v
+ else:
+ ret[k] = v
+ return ret
diff --git a/mergers/dict/tests/test_recursive_policy_update.py b/mergers/dict/tests/test_recursive_policy_update.py
new file mode 100644
index 0000000..f44ada6
--- /dev/null
+++ b/mergers/dict/tests/test_recursive_policy_update.py
@@ -0,0 +1,26 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from test_recursive_update import TestDictRecursiveUpdate
+from mergers.dict import DictRecursivePolicyUpdate
+
+class TestDictRecursivePolicyUpdate(TestDictRecursiveUpdate):
+
+ def setUp(self):
+ self.merger = DictRecursivePolicyUpdate()
+
+ def test_nested_lists_extend(self):
+ first = {'one': [1,2],
+ 'two': {'one': [1,2]}}
+ second = {'one': [3,4],
+ 'two': {'one': [3,4]}}
+ ret = self.merger.merge(first, second)
+ assert len(ret['one']) == 4
+ assert ret['one'][2] == 3
+ assert len(ret['two']['one']) == 4
+ assert ret['two']['one'][3] == 4
diff --git a/mergers/dict/tests/test_recursive_update.py b/mergers/dict/tests/test_recursive_update.py
new file mode 100644
index 0000000..c1e8501
--- /dev/null
+++ b/mergers/dict/tests/test_recursive_update.py
@@ -0,0 +1,45 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from test_update import TestDictUpdate
+from mergers.dict import DictRecursiveUpdate
+
+class TestDictRecursiveUpdate(TestDictUpdate):
+
+ def setUp(self):
+ self.merger = DictRecursiveUpdate()
+
+ def test_simple_recursive_dict_update(self):
+ first = {'one':{1:1,2:3,3:2}}
+ second = {'one':{2:2,3:3,4:4}}
+ ret = self.merger.merge(first, second)
+ assert len(ret) == 1
+ for k,v in ret['one'].iteritems():
+ assert k == v
+
+ def test_complex_recursive_dict_update(self):
+ first = {'one': 1,
+ 'two': {'a':92,'b':94},
+ 'three': {'third':0.33,'two thirds':0.67},
+ 'four': {1:{1:1},2:{2:2},3:{3:4}}
+ }
+ second = {'five': 5,
+ 'one': 1,
+ 'two': {'b':93,'c':94},
+ 'four': {4:{4:4}, 3:{3:3}},
+ }
+ ret = self.merger.merge(first, second)
+ assert ret['one'] == 1
+ assert len(ret['two']) == 3
+ assert ret['two']['b'] == 93
+ assert len(ret['three']) == 2
+ assert len(ret['four']) == 4
+ for i in range(1,4):
+ assert len(ret['four'][i]) == 1
+ for k,v in ret['four'][i].iteritems():
+ assert k == v
diff --git a/mergers/dict/tests/test_update.py b/mergers/dict/tests/test_update.py
new file mode 100644
index 0000000..8e35c53
--- /dev/null
+++ b/mergers/dict/tests/test_update.py
@@ -0,0 +1,22 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.dict import DictUpdate
+
+class TestDictUpdate:
+
+ def setUp(self):
+ self.merger = DictUpdate()
+
+ def test_dict_update(self):
+ first = {1:1,2:3,3:2}
+ second = {2:2,3:3,4:4}
+ ret = self.merger.merge(first, second)
+ assert len(ret) == 4
+ for k,v in ret.iteritems():
+ assert k == v
diff --git a/mergers/dict/update.py b/mergers/dict/update.py
new file mode 100644
index 0000000..dd834f0
--- /dev/null
+++ b/mergers/dict/update.py
@@ -0,0 +1,16 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseDictMerger
+
+class DictUpdate(BaseDictMerger):
+
+ def merge(self, first, second):
+ ret = first.copy()
+ ret.update(second)
+ return ret
diff --git a/mergers/list.py b/mergers/list.py
new file mode 100644
index 0000000..5885147
--- /dev/null
+++ b/mergers/list.py
@@ -0,0 +1,9 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from list import Extend
diff --git a/mergers/list/__init__.py b/mergers/list/__init__.py
new file mode 100644
index 0000000..51ee818
--- /dev/null
+++ b/mergers/list/__init__.py
@@ -0,0 +1,10 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from extend import ListExtend
+from set import SetExtend
diff --git a/mergers/list/base.py b/mergers/list/base.py
new file mode 100644
index 0000000..a7bbc17
--- /dev/null
+++ b/mergers/list/base.py
@@ -0,0 +1,16 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.base import BaseMerger
+
+class BaseListMerger(BaseMerger):
+
+ def merge(self, first, second):
+ first = [first] if not isinstance(first, list) else first[:]
+ second = [second] if not isinstance(second, list) else second[:]
+ return self._combine(first, second)
diff --git a/mergers/list/extend.py b/mergers/list/extend.py
new file mode 100644
index 0000000..f4acc35
--- /dev/null
+++ b/mergers/list/extend.py
@@ -0,0 +1,18 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseListMerger
+
+class ListExtend(BaseListMerger):
+
+ def _combine(self, first, second):
+ if isinstance(second, list):
+ first.extend(second)
+ else:
+ first.append(second)
+ return first
diff --git a/mergers/list/set.py b/mergers/list/set.py
new file mode 100644
index 0000000..ddb0eb4
--- /dev/null
+++ b/mergers/list/set.py
@@ -0,0 +1,17 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from extend import BaseListMerger
+
+class SetExtend(BaseListMerger):
+
+ def _combine(self, first, second):
+ for i in second:
+ if i not in first:
+ first.append(i)
+ return first
diff --git a/mergers/list/tests/test_extend.py b/mergers/list/tests/test_extend.py
new file mode 100644
index 0000000..bdc40ca
--- /dev/null
+++ b/mergers/list/tests/test_extend.py
@@ -0,0 +1,66 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from mergers.list import ListExtend
+
+class TestListExtend:
+
+ def setUp(self):
+ self.merger = ListExtend()
+
+ def _test_merge(self, one, two, target):
+ res = self.merger.merge(one, two)
+ print res, target
+ return res == target
+
+ def test_merge_scalars(self):
+ assert self._test_merge(1, 2, [1,2])
+
+ def test_merge_tuples(self):
+ t1 = (1,2,3)
+ t2 = (6,5,4)
+ target = [t1, t2]
+ assert self._test_merge(t1, t2, target)
+
+ def test_merge_lists(self):
+ l1 = [1,2,3]
+ l2 = [6,5,4]
+ target = l1 + l2
+ assert self._test_merge(l1, l2, target)
+
+ def test_merge_scalar_tuple(self):
+ s = 'one'
+ t = (2,3)
+ target = [s, t]
+ assert self._test_merge(s, t, target)
+
+ def test_merge_scalar_list(self):
+ s = 'foo'
+ l = [1,2,3]
+ target = [s]
+ target.extend(l)
+ assert self._test_merge(s, l, target)
+
+ def test_merge_list_scalar(self):
+ l = [1,2,3]
+ s = 'bar'
+ target = l[:]
+ target.append(s)
+ assert self._test_merge(l, s, target)
+
+ def test_merge_duplicates_scalar(self):
+ s1 = 2
+ s2 = 2
+ target = [2,2]
+ assert self._test_merge(s1, s2, target)
+
+ def test_merge_duplicates_list(self):
+ l1 = [1,2,3]
+ l2 = [3,2,1]
+ target = l1 + l2
+ assert self._test_merge(l1, l2, target)
diff --git a/mergers/list/tests/test_set.py b/mergers/list/tests/test_set.py
new file mode 100644
index 0000000..d3e8183
--- /dev/null
+++ b/mergers/list/tests/test_set.py
@@ -0,0 +1,27 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from test_extend import TestListExtend
+from mergers.list import SetExtend
+
+class TestSetExtend(TestListExtend):
+
+ def setUp(self):
+ self.merger = SetExtend()
+
+ def test_merge_duplicates_scalar(self):
+ s1 = 2
+ s2 = 2
+ target = [2]
+ assert self._test_merge(s1, s2, target)
+
+ def test_merge_duplicates_list(self):
+ l1 = [1,2,3]
+ l2 = [3,2,1]
+ target = l1
+ assert self._test_merge(l1, l2, target)
diff --git a/mergers/recursive_extend.py b/mergers/recursive_extend.py
new file mode 100644
index 0000000..f8487af
--- /dev/null
+++ b/mergers/recursive_extend.py
@@ -0,0 +1,14 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+from base import BaseMerger
+
+class RecursiveExtend(BaseMerger):
+
+ def merge(self, first, second):
+ for