Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1 from playforge/patching

Patching
  • Loading branch information...
commit c6ca83cc2729c319af4f4e07c524196f98d2ecc4 2 parents 4c90013 + 478304a
Stephen Altamirano alts authored
Showing with 100 additions and 11 deletions.
  1. +68 −9 libpldiff/diff.py
  2. +1 −1  setup.py
  3. +31 −1 tests/test_diff.py
77 libpldiff/diff.py
View
@@ -3,6 +3,8 @@
Diffs structured data
"""
+import copy
+
def find_top_level_keys(structure):
"""
Finds all values that are valid indexes of the provided structure
@@ -58,15 +60,72 @@ def diff_structures(structure_1, structure_2):
else:
new_keys = set(new_keys)
old_keys = set(old_keys)
- for key in new_keys:
- if key not in old_keys:
- final['+'][make_path(path + [key])] = new[key]
- else:
- comparisons.append((old[key], new[key], path + [key]))
- old_keys.remove(key)
- # keys present only in old_keys
- for key in old_keys:
- final['-'].append(make_path(path + [key]))
+ # path is used because we cannot represent a type change at the root
+ # level of the structure
+ if new_keys.isdisjoint(old_keys) and path:
+ final['*'][make_path(path)] = new
+ else:
+ for key in new_keys:
+ if key not in old_keys:
+ final['+'][make_path(path + [key])] = new[key]
+ else:
+ comparisons.append((old[key], new[key], path + [key]))
+ old_keys.remove(key)
+
+ # keys present only in old_keys
+ for key in old_keys:
+ final['-'].append(make_path(path + [key]))
return final
+
+def patch(structure, diff):
+ """
+ Applies the changes represented in `diff` to `structure`. This function makes
+ no assumptions about the presence of +, *, - keys in `diff`.
+
+ `structure` is not modified by calling this function. A modified copy of
+ structure is returned.
+ """
+ # additions and modifications can be collapsed into one changeset
+ obj = copy.deepcopy(structure)
+ additions = diff.get('*', {}).items() + diff.get('+', {}).items()
+ for key, value in additions:
+ key_parts = key.split('.')
+ init, tail = key_parts[:-1], key_parts[-1]
+ struct = obj
+ for part in init:
+ struct = struct[part]
+
+ try:
+ tail = int(tail, 10)
+ # I will assume that this structure is list-like
+ if tail < 0:
+ raise Exception('List index must be >= 0')
+
+ while len(struct) < tail+1:
+ # I can't think of a better way, unfortunately
+ struct.append(None)
+ except ValueError:
+ pass
+
+ struct[tail] = value
+
+ for key in diff.get('-', []):
+ key_parts = key.split('.')
+ init, tail = key_parts[:-1], key_parts[-1]
+ struct = obj
+ for part in init:
+ struct = struct[part]
+
+ try:
+ tail = int(tail, 10)
+ if tail < 0:
+ raise Exception('List index must be >= 0')
+
+ while len(struct) >= tail+1:
+ del struct[tail]
+ except ValueError:
+ del struct[tail]
+
+ return obj
2  setup.py
View
@@ -4,7 +4,7 @@
distutils.core.setup(
name='pldiff',
- version='1.0.1',
+ version='1.1.0',
description='Structured Data Diffing',
author='Stephen Altamirano',
author_email='stephen@theplayforge.com',
32 tests/test_diff.py
View
@@ -86,4 +86,34 @@ def testModifications(self):
self.assertEquals(
{'+':{}, '*':{'a':'z'}, '-':[]},
diff.diff_structures(old_structure, new_structure)
- )
+ )
+
+ def testOverwrite(self):
+ old_structure = {'a': {'b': {}}}
+ new_structure = {'a': {'c': {}}}
+ self.assertEquals(
+ {'+':{}, '*': new_structure, '-': []},
+ diff.diff_structures(old_structure, new_structure)
+ )
+
+ def testPatchEmpty(self):
+ a = {'hello': 'there'}
+ self.assertEquals(a, diff.patch(a, {}))
+ self.assertEquals(a, diff.patch(a, {'+':{}, '*':{}, '-':[]}))
+
+ def testPatch(self):
+ a = {'hello': 'there', 'done': None}
+ b = {'hello': 'hola', 'tank': False, 'gas': 8}
+ patch = diff.diff_structures(a, b)
+ self.assertEquals(b, diff.patch(a, patch))
+
+ a = ['hello']
+ b = ['hola', False, None]
+ patch = diff.diff_structures(a, b)
+ self.assertEquals(b, diff.patch(a, patch))
+
+ def testPatchNested(self):
+ a = {'a': {'b': {'c': 7}}}
+ b = {'a': {'b': {'d': 7}}}
+ patch = diff.diff_structures(a, b)
+ self.assertEquals(b, diff.patch(a, patch))
Please sign in to comment.
Something went wrong with that request. Please try again.