Exemple #1
0
    def test_duplicates(self):
        # Three of the exact-same differences.
        differences = [Extra('xxx'), Extra('xxx'), Extra('xxx')]

        # Only allow one of them.
        with self.assertRaises(DataError) as cm:
            allowed = [Extra('xxx')]
            with allow_only(allowed):
                raise DataError('example error', differences)

        expected = [Extra('xxx'), Extra('xxx')]  # Expect two remaining.
        actual = list(cm.exception.differences)
        self.assertEqual(expected, actual)

        # Only allow two of them.
        with self.assertRaises(DataError) as cm:
            allowed = [Extra('xxx'), Extra('xxx')]
            with allow_only(allowed):
                raise DataError('example error', differences)

        expected = [Extra('xxx')]  # Expect one remaining.
        actual = list(cm.exception.differences)
        self.assertEqual(expected, actual)

        # Allow all three.
        allowed = [Extra('xxx'), Extra('xxx'), Extra('xxx')]
        with allow_only(allowed):
            raise DataError('example error', differences)
Exemple #2
0
    def test_keys_mapping(self):
        # Function accepts single argument.
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}
        function = lambda x: x == 'AAA'

        with self.assertRaises(DataError) as cm:
            with allow_any(keys=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {'BBB': Missing('bar')})

        # Function accepts multiple arguments.
        in_diffs = {
            ('AAA', 'XXX'): Missing('foo'),
            ('BBB', 'YYY'): Missing('bar')
        }

        def function(first, second):  # <- Multiple args.
            return second == 'XXX'

        with self.assertRaises(DataError) as cm:
            with allow_any(keys=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {('BBB', 'YYY'): Missing('bar')})
Exemple #3
0
    def test_returns_bad_sequence(self):
        """In place of mapping objects, *function* may instead return an
        iterable of two-item sequences but if the sequence contains more
        or less items, a ValueError should be raised.
        """
        in_diffs = {
            ('AAA', 'xxx'): Missing('foo'),
            ('BBB', 'yyy'): Missing('bar')
        }

        # mapping / iterable of 1-item sequences.
        return_val = [
            [Missing('foo')],  # <- One item.
            [Missing('bar')]
        ]  # <- One item.

        regex = ('has length 1.*2 is required')
        with self.assertRaisesRegex(ValueError, regex):  # <- ValueError!
            with allow_iter(lambda x: return_val):
                raise DataError('example error', in_diffs)

        # mapping / iterable of 3-item sequences.
        return_val = [
            [('AAA', 'xxx'), Missing('foo'), None],  # <- Three items.
            [('BBB', 'yyy'), Missing('bar'), None]
        ]  # <- Three items.

        regex = 'has length 3.*2 is required'
        with self.assertRaisesRegex(ValueError, regex):  # <- ValueError!
            with allow_iter(lambda x: return_val):
                raise DataError('example error', in_diffs)
Exemple #4
0
    def test_allow_all(self):
        differences = [Missing('xxx'), Extra('yyy')]
        with allow_only(differences):
            raise DataError('example error', [Missing('xxx'), Extra('yyy')])

        # Order of differences should not matter!
        differences = [Extra('yyy'), Missing('xxx')]
        with allow_only(differences):
            raise DataError('example error', reversed(differences))
Exemple #5
0
    def test_returns_sequence(self):
        """If given a mapping, *function* may return an iterable of
        sequences (instead of a mapping).  Each sequence must contain
        exactly two objects---the first object will be use as the key
        and the second object will be used as the value (same as
        Python's 'dict' behavior).
        """
        # Simple key.
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}
        return_val = [
            ['AAA', Missing('foo')],  # <- Two items.
            ['BBB', Missing('bar')]
        ]  # <- Two items.

        with self.assertRaises(DataError) as cm:
            with allow_iter(lambda x: return_val):
                raise DataError('example error', in_diffs)

        out_diffs = cm.exception.differences
        self.assertEqual(out_diffs, in_diffs)

        # Compound key.
        in_diffs = {
            ('AAA', 'xxx'): Missing('foo'),
            ('BBB', 'yyy'): Missing('bar')
        }
        return_val = [
            [('AAA', 'xxx'), Missing('foo')],  # <- Two items.
            [('BBB', 'yyy'), Missing('bar')]
        ]  # <- Two items.

        with self.assertRaises(DataError) as cm:
            with allow_iter(lambda x: return_val):
                raise DataError('example error', in_diffs)

        out_diffs = cm.exception.differences
        self.assertEqual(out_diffs, in_diffs)

        # Duplicate keys--last defined value (bar) appears in mapping.
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}
        return_val = [
            ['AAA', Missing('foo')],
            ['BBB', Missing('xxx')],  # <- Duplicate key.
            ['BBB', Missing('yyy')],  # <- Duplicate key.
            ['BBB', Missing('zzz')],  # <- Duplicate key.
            ['BBB', Missing('bar')]
        ]  # <- Duplicate key.

        with self.assertRaises(DataError) as cm:
            with allow_iter(lambda x: return_val):
                raise DataError('example error', in_diffs)

        out_diffs = cm.exception.differences
        self.assertEqual(out_diffs, in_diffs)
Exemple #6
0
    def test_kwds_all_ok(self):
        function = lambda iterable: list()  # <- Accepts everything.
        in_diffs = [
            Missing('foo', aaa='x', bbb='y'),
            Missing('bar', aaa='x', bbb='z'),
        ]
        # Using keyword aaa='x' should accept all in_diffs.
        with allow_iter(function, 'example allowance', aaa='x'):
            raise DataError('example error', in_diffs)

        # Using keyword bbb=['y', 'z'] should also accept all in_diffs.
        with allow_iter(function, 'example allowance', bbb=['y', 'z']):
            raise DataError('example error', in_diffs)
Exemple #7
0
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is None:  # <- Values are None when no exeption was raised.
            if self.msg:
                msg = self.msg
            else:
                msg = getattr(self.function, '__name__', str(self.function))
            exc = AssertionError('No differences found: ' + str(msg))
            exc.__cause__ = None
            raise exc

        if not issubclass(exc_type, DataError):
            raise exc_value  # If not DataError, re-raise without changes.

        diffs = exc_value.differences
        rejected_kwds, accepted_kwds = self._partition_kwds(diffs, **self.kwds)
        rejected_func = self.function(accepted_kwds)  # <- Apply function!
        not_allowed = itertools.chain(rejected_kwds, rejected_func)

        not_allowed = list(not_allowed)
        if not_allowed:
            msg = [self.msg, getattr(exc_value, 'msg')]
            msg = ': '.join(x for x in msg if x)
            exc = DataError(msg, not_allowed)
            exc.__cause__ = None  # <- Suppress context using verbose
            raise exc  # alternative to support older Python
            # versions--see PEP 415 (same as
            # effect as "raise ... from None").

        return True  # <- Suppress original exception.
Exemple #8
0
    def test_allow_one_but_find_duplicate(self):
        with self.assertRaises(DataError) as cm:
            with allow_only(Extra('xxx')):
                raise DataError('example error', [Extra('xxx'), Extra('xxx')])

        result_string = str(cm.exception)
        self.assertEqual("example error:\n Extra('xxx')", result_string)
Exemple #9
0
    def test_allow_duplicate_but_find_only_one(self):
        with self.assertRaises(DataError) as cm:
            with allow_only([Extra('xxx'), Extra('xxx')]):
                raise DataError('example error', [Extra('xxx')])

        result_string = str(cm.exception)
        self.assertEqual("Allowed difference not found:\n Extra('xxx')", result_string)
Exemple #10
0
    def test_dict_of_diffs_exceeds(self):
        differences = {'foo': Extra('xxx'), 'bar': Missing('yyy')}

        with self.assertRaises(DataError) as cm:
            with allow_limit(1):  # <- Allows only 1 but there are 2!
                raise DataError('example error', differences)

        rejected = cm.exception.differences
        self.assertEqual(differences, rejected)
Exemple #11
0
 def function(iterable):
     allowed = self._walk_diff(
         differences)  # <- Closes over *differences*.
     allowed = collections.Counter(allowed)
     not_allowed = []
     for x in iterable:
         if allowed[x]:
             allowed[x] -= 1
         else:
             not_allowed.append(x)
     if not_allowed:
         return not_allowed  # <- EXIT!
     not_found = list(allowed.elements())
     if not_found:
         exc = DataError('Allowed difference not found', not_found)
         exc.__cause__ = None
         raise exc
     return iter([])
Exemple #12
0
    def test_allow_some(self):
        differences = [Extra('xxx'), Missing('yyy')]

        with self.assertRaises(DataError) as cm:
            with allow_limit(1):  # <- Allows only 1 but there are 2!
                raise DataError('example error', differences)

        rejected = list(cm.exception.differences)
        self.assertEqual(differences, rejected)
Exemple #13
0
    def test_allow_some(self):
        differences = [Extra('xxx'), Missing('yyy')]

        with self.assertRaises(DataError) as cm:
            with allow_missing():
                raise DataError('example error', differences)

        rejected = list(cm.exception.differences)
        self.assertEqual(rejected, [Extra('xxx')])
Exemple #14
0
    def test_empty_generator(self):
        """If all items are valid, returning an empty generator or other
        iterable non-container should work, too.
        """
        function = lambda iterable: (x for x in [])  # <- Empty generator.
        in_diffs = [Missing('foo'), Missing('bar')]

        with allow_iter(function):  # <- Passes without error.
            raise DataError('example error', in_diffs)
Exemple #15
0
    def test_keys_nonmapping(self):
        # Missing required keyword 'diffs'.
        in_diffs = [Missing('foo'), Missing('bar')]
        function = lambda first, second: first == 'AAA'

        regex = "accepts only 'diffs' keyword, found 'keys'"
        with self.assertRaisesRegex(ValueError, regex):
            with allow_any(keys=function):  # <- expects 'diffs='.
                raise DataError('example error', in_diffs)

        # Disallowed keywords ('keys').
        in_diffs = [Missing('foo'), Missing('bar')]
        function = lambda first, second: first == 'AAA'

        with self.assertRaisesRegex(ValueError, "found 'keys'"):
            with allow_any(diffs=function,
                           keys=function):  # <- 'keys=' not allowed.
                raise DataError('example error', in_diffs)
Exemple #16
0
    def test_diffs_nonmapping(self):
        in_diffs = [Missing('foo'), Missing('bar')]
        function = lambda x: x.value == 'foo'

        with self.assertRaises(DataError) as cm:
            with allow_any(diffs=function):  # <- Using diffs keyword!
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, [Missing('bar')])
Exemple #17
0
    def test_diffs_mapping(self):
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}
        function = lambda x: x.value == 'foo'

        with self.assertRaises(DataError) as cm:
            with allow_any(diffs=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {'BBB': Missing('bar')})
Exemple #18
0
    def test_not_found(self):
        with self.assertRaises(DataError) as cm:
            with allow_only([Extra('xxx'), Missing('yyy')]):
                raise DataError('example error', [Extra('xxx')])

        result_str = str(cm.exception)
        self.assertTrue(result_str.startswith('Allowed difference not found'))

        result_diffs = list(cm.exception.differences)
        self.assertEqual([Missing('yyy')], result_diffs)
Exemple #19
0
    def test_iterable_all_good(self):
        """Given a non-mapping iterable in which all items are valid,
        *function* should omit all items and simply return an empty
        iterable.
        """
        function = lambda iterable: list()  # <- Accepts everything.
        in_diffs = [Missing('foo'), Missing('bar')]

        with allow_iter(function):  # <- Passes without error.
            raise DataError('example error', in_diffs)
Exemple #20
0
    def test_items_mapping(self):
        # Function of one argument.
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}

        def function(item):
            key, diff = item  # Unpack item tuple.
            return key == 'AAA'

        with self.assertRaises(DataError) as cm:
            with allow_any(items=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {'BBB': Missing('bar')})

        # Function of two arguments.
        in_diffs = {'AAA': Missing('foo'), 'BBB': Missing('bar')}

        def function(key, diff):
            return key == 'AAA'

        with self.assertRaises(DataError) as cm:
            with allow_any(items=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {'BBB': Missing('bar')})

        # Function of three arguments.
        in_diffs = {
            ('AAA', 'XXX'): Missing('foo'),
            ('BBB', 'YYY'): Missing('bar')
        }

        def function(key1, key2, diff):
            return key2 == 'XXX'

        with self.assertRaises(DataError) as cm:
            with allow_any(items=function):
                raise DataError('example error', in_diffs)

        rejected = cm.exception.differences
        self.assertEqual(rejected, {('BBB', 'YYY'): Missing('bar')})
Exemple #21
0
    def test_allow_some(self):
        with self.assertRaises(DataError) as cm:
            with allow_only(Extra('xxx'), 'example allowance'):
                raise DataError('example error', [Extra('xxx'), Missing('yyy')])

        result_str = str(cm.exception)
        self.assertEqual("example allowance: example error:\n Missing('yyy')", result_str)

        result_diffs = list(cm.exception.differences)
        self.assertEqual([Missing('yyy')], result_diffs)
Exemple #22
0
    def test_dict_of_diffs_kwds_func_under_limit(self):
        differences = {'foo': Extra('xxx'), 'bar': Missing('yyy')}

        with self.assertRaises(DataError) as cm:
            is_extra = lambda x: isinstance(x, Extra)
            with allow_limit(2, diffs=is_extra):
                raise DataError('example error', differences)

        rejected = cm.exception.differences
        self.assertEqual({'bar': Missing('yyy')}, rejected)
Exemple #23
0
    def test_mapping_none_allowed(self):
        differences = {'foo': Extra('xxx'), 'bar': Missing('yyy')}
        allowed = {}

        with self.assertRaises(DataError) as cm:
            with allow_only(allowed):
                raise DataError('example error', differences)

        actual = cm.exception.differences
        self.assertEqual(differences, actual)
Exemple #24
0
    def test_kwds_under_limit(self):
        differences = [Extra('xxx'), Missing('yyy'), Extra('zzz')]

        with self.assertRaises(DataError) as cm:
            is_extra = lambda x: isinstance(x, Extra)
            with allow_limit(4,
                             diffs=is_extra):  # <- Limit of 4 and is_extra().
                raise DataError('example error', differences)

        rejected = list(cm.exception.differences)
        self.assertEqual([Missing('yyy')], rejected)
Exemple #25
0
    def test_kwds(self):
        diff_set = set([
            Missing('xxx', aaa='foo'),
            Missing('yyy', aaa='bar'),
            Extra('zzz', aaa='foo'),
        ])

        with self.assertRaises(DataError) as cm:
            # Allows 2 with aaa='foo' and there are two (only aaa='bar' is rejected).
            with allow_limit(2, 'example allowance', aaa='foo'):
                raise DataError('example error', diff_set)
        rejected = set(cm.exception.differences)
        self.assertEqual(rejected, set([Missing('yyy', aaa='bar')]))

        with self.assertRaises(DataError) as cm:
            # Allows 1 with aaa='foo' but there are 2 (all are rejected)!
            with allow_limit(1, 'example allowance', aaa='foo'):
                raise DataError('example error', diff_set)
        rejected = set(cm.exception.differences)
        self.assertEqual(rejected, diff_set)
Exemple #26
0
    def test_some_allowed(self):
        differences = [Extra('xxx'), Missing('yyy')]
        allowed = [Extra('xxx')]

        with self.assertRaises(DataError) as cm:
            with allow_only(allowed):
                raise DataError('example error', differences)

        expected = [Missing('yyy')]
        actual = list(cm.exception.differences)
        self.assertEqual(expected, actual)
Exemple #27
0
    def test_single_diff_without_container(self):
        differences = [Extra('xxx'), Missing('yyy')]
        allowed = Extra('xxx')  # <- Single diff, not in list.

        with self.assertRaises(DataError) as cm:
            with allow_only(allowed):
                raise DataError('example error', differences)

        expected = [Missing('yyy')]
        actual = list(cm.exception.differences)
        self.assertEqual(expected, actual)
Exemple #28
0
    def test_mapping_mismatched_types(self):
        # Dict of diffs vs list of allowed.
        differences = {'foo': Extra('xxx'), 'bar': Missing('yyy')}
        allowed = [Extra('xxx'), Missing('yyy')]

        regex = ("expects non-mapping differences but found 'dict' of "
                 "differences")
        with self.assertRaisesRegex(ValueError, regex):
            with allow_only(allowed):
                raise DataError('example error', differences)

        # List of diffs vs dict of allowed.
        differences = [Extra('xxx'), Missing('yyy')]
        allowed = {'foo': Extra('xxx'), 'bar': Missing('yyy')}

        regex = ("expects mapping of differences but found 'list' of "
                 "differences")
        with self.assertRaisesRegex(ValueError, regex):
            with allow_only(allowed):
                raise DataError('example error', differences)
Exemple #29
0
    def test_kwds(self):
        in_diffs = [
            Missing('xxx', aaa='foo'),
            Missing('yyy', aaa='bar'),
            Extra('zzz', aaa='foo'),
        ]
        with self.assertRaises(DataError) as cm:
            with allow_missing('example allowance', aaa='foo'):
                raise DataError('example error', in_diffs)

        rejected = list(cm.exception.differences)
        self.assertEqual(rejected, [Missing('yyy', aaa='bar'), Extra('zzz', aaa='foo')])
Exemple #30
0
    def test_function_some_ok(self):
        function = lambda iterable: (x for x in iterable if x.value != 'bar')
        in_diffs = [
            Missing('foo'),
            Missing('bar'),
        ]
        with self.assertRaises(DataError) as cm:
            with allow_iter(function, 'example allowance'):
                raise DataError('example error', in_diffs)

        rejected = list(cm.exception.differences)
        self.assertEqual(rejected, [Missing('foo')])