def are_equal(*sequences, easy_types=(sequence_tools.CuteRange,)): ''' Are the given sequences equal? This tries to make a cheap comparison between the sequences if possible, but if not, it goes over the sequences in parallel item-by-item and checks whether the items are all equal. A cheap comparison is attempted only if the sequences are all of the same type, and that type is in `easy_types`. (It's important to restrict `easy_types` only to types where equality between the sequences is the same as equality between every item in the sequences.) ''' from python_toolbox import logic_tools sequence_types = set(map(type, sequences)) # Trying cheap comparison: if len(sequence_types) == 1 and issubclass( get_single_if_any(sequence_types), easy_types): return logic_tools.all_equivalent(sequences) # If cheap comparison didn't work, trying item-by-item comparison: zipped = itertools.zip_longest(*sequences, fillvalue=_EMPTY_SENTINEL) for values in zipped: # No need to explicitly check for `_EMPTY_SENTINEL`, it would just make # the following condition `False`, because it's impossible for all # values to be the sentinel. if not logic_tools.all_equivalent(values): return False else: return True
def are_equal(*sequences, **kwargs): ''' Are the given sequences equal? This tries to make a cheap comparison between the sequences if possible, but if not, it goes over the sequences in parallel item-by-item and checks whether the items are all equal. A cheap comparison is attempted only if the sequences are all of the same type, and that type is in `easy_types`. (It's important to restrict `easy_types` only to types where equality between the sequences is the same as equality between every item in the sequences.) ''' from python_toolbox import logic_tools sequence_types = set(map(type, sequences)) easy_types = kwargs.get('easy_types', (sequence_tools.CuteRange, )) # Trying cheap comparison: if len(sequence_types) == 1 and issubclass( get_single_if_any(sequence_types), easy_types): return logic_tools.all_equivalent(sequences) # If cheap comparison didn't work, trying item-by-item comparison: zipped = itertools.izip_longest(*sequences, fillvalue=_EMPTY_SENTINEL) for values in zipped: # No need to explicitly check for `_EMPTY_SENTINEL`, it would just make # the following condition `False`, because it's impossible for all # values to be the sentinel. if not logic_tools.all_equivalent(values): return False else: return True
def test_assume_transitive_false(): ''' Test `all_equivalent` in cases where `assume_transitive=False` is relevant. ''' class FunkyFloat(float): def __eq__(self, other): return (abs(self - other) <= 2) funky_floats = [FunkyFloat(1), FunkyFloat(2), FunkyFloat(3), FunkyFloat(4)] assert all_equivalent(funky_floats) assert not all_equivalent(funky_floats, assume_transitive=False)
def cute_equal(*items): # For testing purposes, we need `nan == nan`, so we use `cute_equal`. if all(isinstance(item, numbers.Number) for item in items): if all(map(math.isnan, items)): return True else: return logic_tools.all_equivalent(items) else: assert all(isinstance(item, tuple) for item in items) return all(cute_equal(*sub_items) for sub_items in zip(*items))
def test_assume_transitive_false(): ''' Test `all_equivalent` in cases where `assume_transitive=False` is relevant. ''' class FunkyFloat(float): def __eq__(self, other): return (abs(self - other) <= 2) funky_floats = [ FunkyFloat(1), FunkyFloat(2), FunkyFloat(3), FunkyFloat(4) ] assert all_equivalent(funky_floats) assert not all_equivalent(funky_floats, assume_transitive=False)
def get_pairs_for_options(**kwargs): assert EquivalenceChecker.pairs_checked == [] # Testing with an iterator instead of the tuple to ensure it works and that # the function doesn't try to exhaust it twice. assert all_equivalent(iter(things), EquivalenceChecker.is_equivalent, **kwargs) is True try: return tuple((a.tag, b.tag) for (a, b) in EquivalenceChecker.pairs_checked) finally: EquivalenceChecker.pairs_checked = []
def get_pairs_for_options(**kwargs): assert EquivalenceChecker.pairs_checked == [] # Testing with an iterator instead of the tuple to ensure it works and that # the function doesn't try to exhaust it twice. assert all_equivalent(iter(things), EquivalenceChecker.is_equivalent, **kwargs) is True try: return tuple( (a.tag, b.tag) for (a, b) in EquivalenceChecker.pairs_checked) finally: EquivalenceChecker.pairs_checked = []
def zip_non_equal(iterables, lazy_tuple=False): ''' Zip the iterables, but only yield the tuples where the items aren't equal. ''' from python_toolbox import logic_tools iterator = (items for items in zip(*iterables) if not logic_tools.all_equivalent(items)) if lazy_tuple: from python_toolbox import nifty_collections return nifty_collections.LazyTuple(iterator) else: return iterator
def test_illegal_cases(): illegal_cases = ((4, 0), (infinity, 0), (-infinity, 0)) for illegal_case in illegal_cases: with cute_testing.RaiseAssertor() as raise_assertor_0: cute_divmod(*illegal_case) with cute_testing.RaiseAssertor() as raise_assertor_1: divmod(*illegal_case) with cute_testing.RaiseAssertor() as raise_assertor_2: cute_floor_div(*illegal_case) assert logic_tools.all_equivalent(( type(raise_assertor_0.exception), type(raise_assertor_1.exception), type(raise_assertor_2.exception), ))
def test_illegal_cases(): illegal_cases = ( (4, 0), (infinity, 0), (-infinity, 0) ) for illegal_case in illegal_cases: with cute_testing.RaiseAssertor() as raise_assertor_0: cute_divmod(*illegal_case) with cute_testing.RaiseAssertor() as raise_assertor_1: divmod(*illegal_case) with cute_testing.RaiseAssertor() as raise_assertor_2: cute_floor_div(*illegal_case) assert logic_tools.all_equivalent(( type(raise_assertor_0.exception), type(raise_assertor_1.exception), type(raise_assertor_2.exception), ))
def call_and_check_if_profiled(f): '''Call the function `f` and return whether it profiled itself.''' with OutputCapturer() as output_capturer: f() output = output_capturer.output segments_found = [(segment in output) for segment in segments] if not logic_tools.all_equivalent(segments_found): raise Exception("Some segments were found, but some weren't; can't " "know if this was a profiled call or not. Possibly " "some of our segments are wrong.") return segments_found[0]
def test_custom_relations(): assert all_equivalent(range(4), relation=operator.ne) is True assert all_equivalent(range(4), relation=operator.ge) is False assert all_equivalent(range(4), relation=operator.le) is True assert all_equivalent( range(4), relation=operator.le, assume_transitive=True) is True # (Always comparing small to big, even on `assume_transitive=False`.) assert all_equivalent(range(4), relation=lambda x, y: (x // 10 == y // 10)) is True assert all_equivalent(range(4), relation=lambda x, y: (x // 10 == y // 10), assume_transitive=True) is True assert all_equivalent(range(8, 12), relation=lambda x, y: (x // 10 == y // 10)) is False assert all_equivalent(range(8, 12), relation=lambda x, y: (x // 10 == y // 10), assume_transitive=True) is False
def test_operations_on_different_types(): x1 = OrderedSet(range(0, 4)) | FrozenOrderedSet(range(2, 6)) x2 = OrderedSet(range(0, 4)) & FrozenOrderedSet(range(2, 6)) x3 = FrozenOrderedSet(range(0, 4)) | OrderedSet(range(2, 6)) x4 = FrozenOrderedSet(range(0, 4)) & OrderedSet(range(2, 6)) assert type(x1) == OrderedSet assert type(x2) == OrderedSet assert type(x3) == FrozenOrderedSet assert type(x4) == FrozenOrderedSet assert x1 == OrderedSet(range(0, 6)) assert x2 == OrderedSet(range(2, 4)) assert x3 == FrozenOrderedSet(range(0, 6)) assert x4 == FrozenOrderedSet(range(2, 4)) assert logic_tools.all_equivalent((x1, x2, x3, x4), relation=operator.ne)
def test_custom_relations(): assert all_equivalent(range(4), relation=operator.ne) is True assert all_equivalent(range(4), relation=operator.ge) is False assert all_equivalent(range(4), relation=operator.le) is True assert all_equivalent(range(4), relation=operator.le, assume_transitive=True) is True # (Always comparing small to big, even on `assume_transitive=False`.) assert all_equivalent(range(4), relation=lambda x, y: (x // 10 == y // 10)) is True assert all_equivalent(range(4), relation=lambda x, y: (x // 10 == y // 10), assume_transitive=True) is True assert all_equivalent(range(8, 12), relation=lambda x, y: (x // 10 == y // 10)) is False assert all_equivalent(range(8, 12), relation=lambda x, y: (x // 10 == y // 10), assume_transitive=True) is False
def _check(assume_transitive): assert all_equivalent([1, 1, 1, 1], assume_transitive=assume_transitive) assert all_equivalent([1, 1, 1.0, 1], assume_transitive=assume_transitive) assert all_equivalent(((1 + 0j), 1, 1.0, 1), assume_transitive=assume_transitive) assert all_equivalent([], assume_transitive=assume_transitive) assert all_equivalent(iter([1, 1, 1.0, 1]), assume_transitive=assume_transitive) assert all_equivalent(set(('meow',)), assume_transitive=assume_transitive) assert all_equivalent(['frr', 'frr', 'frr', 'frr'], assume_transitive=assume_transitive) assert not all_equivalent([1, 1, 2, 1], assume_transitive=assume_transitive) assert not all_equivalent([1, 1, 1.001, 1], assume_transitive=assume_transitive) assert not all_equivalent(((1 + 0j), 3, 1.0, 1), assume_transitive=assume_transitive) assert not all_equivalent(range(7), assume_transitive=assume_transitive) assert not all_equivalent(iter([1, 17, 1.0, 1]), assume_transitive=assume_transitive) assert not all_equivalent(set(('meow', 'grr')), assume_transitive=assume_transitive) assert not all_equivalent(['frr', 'frr', {}, 'frr', 'frr'], assume_transitive=assume_transitive) assert not all_equivalent(itertools.count())
def assert_same_signature(*callables): '''Assert that all the `callables` have the same function signature.''' arg_specs = [cute_inspect.getargspec(callable_) for callable_ in callables] if not logic_tools.all_equivalent(arg_specs, assume_transitive=False): raise Failure('Not all the callables have the same signature.')
def _check(assume_transitive): assert all_equivalent([1, 1, 1, 1], assume_transitive=assume_transitive) assert all_equivalent([1, 1, 1.0, 1], assume_transitive=assume_transitive) assert all_equivalent(((1 + 0j), 1, 1.0, 1), assume_transitive=assume_transitive) assert all_equivalent([], assume_transitive=assume_transitive) assert all_equivalent(iter([1, 1, 1.0, 1]), assume_transitive=assume_transitive) assert all_equivalent(set(('meow', )), assume_transitive=assume_transitive) assert all_equivalent(['frr', 'frr', 'frr', 'frr'], assume_transitive=assume_transitive) assert not all_equivalent([1, 1, 2, 1], assume_transitive=assume_transitive) assert not all_equivalent([1, 1, 1.001, 1], assume_transitive=assume_transitive) assert not all_equivalent( ((1 + 0j), 3, 1.0, 1), assume_transitive=assume_transitive) assert not all_equivalent(range(7), assume_transitive=assume_transitive) assert not all_equivalent(iter([1, 17, 1.0, 1]), assume_transitive=assume_transitive) assert not all_equivalent(set(('meow', 'grr')), assume_transitive=assume_transitive) assert not all_equivalent(['frr', 'frr', {}, 'frr', 'frr'], assume_transitive=assume_transitive) assert not all_equivalent(itertools.count())