From 96c9e12f7ff9607a35b75bee56ee87edabc8dbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Wed, 12 Oct 2016 17:09:48 +0200 Subject: [PATCH] doc_diff() optionally no longer reports differences between falsey values. If falsey_is_equal=True, all Falsey values compare as equal, i.e. this function won't report differences between DoesNotExist, False, '', and 0. --- pillar/api/utils/__init__.py | 15 ++++++++++++++- tests/test_api/test_utils.py | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pillar/api/utils/__init__.py b/pillar/api/utils/__init__.py index 00f64696..c096cc08 100644 --- a/pillar/api/utils/__init__.py +++ b/pillar/api/utils/__init__.py @@ -115,11 +115,19 @@ def gravatar(email, size=64): "?" + urllib.urlencode(parameters) + +class MetaFalsey(type): + def __nonzero__(cls): + return False + __bool__ = __nonzero__ # for Python 3 + + class DoesNotExist(object): """Returned as value by doc_diff if a value does not exist.""" + __metaclass__ = MetaFalsey -def doc_diff(doc1, doc2): +def doc_diff(doc1, doc2, falsey_is_equal=True): """Generator, yields differences between documents. Yields changes as (key, value in doc1, value in doc2) tuples, where @@ -128,6 +136,9 @@ def doc_diff(doc1, doc2): Sub-documents (i.e. dicts) are recursed, and dot notation is used for the keys if changes are found. + + If falsey_is_equal=True, all Falsey values compare as equal, i.e. this + function won't report differences between DoesNotExist, False, '', and 0. """ for key in set(doc1.keys()).union(set(doc2.keys())): @@ -145,5 +156,7 @@ def doc_diff(doc1, doc2): if val1 == val2: continue + if falsey_is_equal and bool(val1) == bool(val2) == False: + continue yield key, val1, val2 diff --git a/tests/test_api/test_utils.py b/tests/test_api/test_utils.py index f4dc4f65..3b8d56d0 100644 --- a/tests/test_api/test_utils.py +++ b/tests/test_api/test_utils.py @@ -54,6 +54,27 @@ class DocDiffTest(unittest.TestCase): self.assertEqual([(3, 42, 513)], list(diff)) + def test_diff_values_falsey(self): + from pillar.api.utils import doc_diff, DoesNotExist + + # DoesNotExist vs. empty string + diff = doc_diff({'a': 'b', 3: ''}, + {'a': 'b'}) + self.assertEqual([], list(diff)) + + diff = doc_diff({'a': 'b', 3: ''}, + {'a': 'b'}, falsey_is_equal=False) + self.assertEqual([(3, '', DoesNotExist)], list(diff)) + + # Empty string vs. None + diff = doc_diff({'a': 'b', 3: ''}, + {'a': 'b', 3: None}) + self.assertEqual([], list(diff)) + + diff = doc_diff({'a': 'b', 3: ''}, + {'a': 'b', 3: None}, falsey_is_equal=False) + self.assertEqual([(3, '', None)], list(diff)) + def test_diff_keys_simple(self): from pillar.api.utils import doc_diff, DoesNotExist diff = doc_diff({'a': 'b', 3: 42},