Added util function to compute the difference between two dicts.
This commit is contained in:
@@ -113,3 +113,37 @@ def gravatar(email, size=64):
|
|||||||
return "https://www.gravatar.com/avatar/" + \
|
return "https://www.gravatar.com/avatar/" + \
|
||||||
hashlib.md5(str(email)).hexdigest() + \
|
hashlib.md5(str(email)).hexdigest() + \
|
||||||
"?" + urllib.urlencode(parameters)
|
"?" + urllib.urlencode(parameters)
|
||||||
|
|
||||||
|
|
||||||
|
class DoesNotExist(object):
|
||||||
|
"""Returned as value by doc_diff if a value does not exist."""
|
||||||
|
|
||||||
|
|
||||||
|
def doc_diff(doc1, doc2):
|
||||||
|
"""Generator, yields differences between documents.
|
||||||
|
|
||||||
|
Yields changes as (key, value in doc1, value in doc2) tuples, where
|
||||||
|
the value can also be the DoesNotExist class. Does not report changed
|
||||||
|
private keys (i.e. starting with underscores).
|
||||||
|
|
||||||
|
Sub-documents (i.e. dicts) are recursed, and dot notation is used
|
||||||
|
for the keys if changes are found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for key in set(doc1.keys()).union(set(doc2.keys())):
|
||||||
|
if isinstance(key, basestring) and key[0] == u'_':
|
||||||
|
continue
|
||||||
|
|
||||||
|
val1 = doc1.get(key, DoesNotExist)
|
||||||
|
val2 = doc2.get(key, DoesNotExist)
|
||||||
|
|
||||||
|
# Only recurse if both values are dicts
|
||||||
|
if isinstance(val1, dict) and isinstance(val2, dict):
|
||||||
|
for subkey, subval1, subval2 in doc_diff(val1, val2):
|
||||||
|
yield '%s.%s' % (key, subkey), subval1, subval2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if val1 == val2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield key, val1, val2
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import unittest
|
||||||
|
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
from pillar.tests import AbstractPillarTest
|
from pillar.tests import AbstractPillarTest
|
||||||
@@ -28,3 +30,57 @@ class Str2idTest(AbstractPillarTest):
|
|||||||
unhappy('')
|
unhappy('')
|
||||||
unhappy(u'')
|
unhappy(u'')
|
||||||
unhappy(None)
|
unhappy(None)
|
||||||
|
|
||||||
|
|
||||||
|
class DocDiffTest(unittest.TestCase):
|
||||||
|
def test_no_diff_simple(self):
|
||||||
|
from pillar.api.utils import doc_diff
|
||||||
|
diff = doc_diff({'a': 'b', 3: 42},
|
||||||
|
{'a': 'b', 3: 42})
|
||||||
|
|
||||||
|
self.assertEqual([], list(diff))
|
||||||
|
|
||||||
|
def test_no_diff_privates(self):
|
||||||
|
from pillar.api.utils import doc_diff
|
||||||
|
diff = doc_diff({'a': 'b', 3: 42, '_updated': 5133},
|
||||||
|
{'a': 'b', 3: 42, '_updated': 42})
|
||||||
|
|
||||||
|
self.assertEqual([], list(diff))
|
||||||
|
|
||||||
|
def test_diff_values_simple(self):
|
||||||
|
from pillar.api.utils import doc_diff
|
||||||
|
diff = doc_diff({'a': 'b', 3: 42},
|
||||||
|
{'a': 'b', 3: 513})
|
||||||
|
|
||||||
|
self.assertEqual([(3, 42, 513)], list(diff))
|
||||||
|
|
||||||
|
def test_diff_keys_simple(self):
|
||||||
|
from pillar.api.utils import doc_diff, DoesNotExist
|
||||||
|
diff = doc_diff({'a': 'b', 3: 42},
|
||||||
|
{'a': 'b', 2: 42})
|
||||||
|
|
||||||
|
self.assertEqual({(3, 42, DoesNotExist), (2, DoesNotExist, 42)}, set(diff))
|
||||||
|
|
||||||
|
def test_no_diff_nested(self):
|
||||||
|
from pillar.api.utils import doc_diff
|
||||||
|
diff = doc_diff({'a': 'b', 'props': {'status': u'todo', 'notes': u'jemoeder'}},
|
||||||
|
{'a': 'b', 'props': {'status': u'todo', 'notes': u'jemoeder'}})
|
||||||
|
|
||||||
|
self.assertEqual([], list(diff))
|
||||||
|
|
||||||
|
def test_diff_values_nested(self):
|
||||||
|
from pillar.api.utils import doc_diff
|
||||||
|
diff = doc_diff({'a': 'b', 'props': {'status': u'todo', 'notes': u'jemoeder'}},
|
||||||
|
{'a': 'c', 'props': {'status': u'done', 'notes': u'jemoeder'}})
|
||||||
|
|
||||||
|
self.assertEqual({('a', 'b', 'c'), ('props.status', u'todo', u'done')},
|
||||||
|
set(diff))
|
||||||
|
|
||||||
|
def test_diff_keys_nested(self):
|
||||||
|
from pillar.api.utils import doc_diff, DoesNotExist
|
||||||
|
diff = doc_diff({'a': 'b', 'props': {'status1': u'todo', 'notes': u'jemoeder'}},
|
||||||
|
{'a': 'b', 'props': {'status2': u'todo', 'notes': u'jemoeder'}})
|
||||||
|
|
||||||
|
self.assertEqual({('props.status1', u'todo', DoesNotExist),
|
||||||
|
('props.status2', DoesNotExist, u'todo')},
|
||||||
|
set(diff))
|
||||||
|
Reference in New Issue
Block a user