def test_is_balanced(self): # Tests that balanced_reduce returns the object as a balanced tree class CombinedNode: def __init__(self, a, b): self.a = a self.b = b def __repr__(self): return '(%s %s)' % (self.a, self.b) self.assertEqual( repr( balanced_reduce(CombinedNode, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'])), '(((A B) (C D)) ((E F) (G H)))' # Note: functools.reduce will return '(((((((A B) C) D) E) F) G) H)' )
def test_is_balanced(self): # Tests that balanced_reduce returns the object as a balanced tree class CombinedNode: def __init__(self, a, b): self.a = a self.b = b def __repr__(self): return "(%s %s)" % (self.a, self.b) self.assertEqual( repr( balanced_reduce(CombinedNode, ["A", "B", "C", "D", "E", "F", "G", "H"])), "(((A B) (C D)) ((E F) (G H)))" # Note: functools.reduce will return '(((((((A B) C) D) E) F) G) H)' )
def test_iterator_usage(self): class SequenceClass: def __init__(self, n): self.n = n def __getitem__(self, i): if 0 <= i < self.n: return i else: raise IndexError from operator import add self.assertEqual(balanced_reduce(add, SequenceClass(5)), 10) self.assertEqual(balanced_reduce(add, SequenceClass(5), 42), 52) self.assertRaises(TypeError, balanced_reduce, add, SequenceClass(0)) self.assertEqual(balanced_reduce(add, SequenceClass(0), 42), 42) self.assertEqual(balanced_reduce(add, SequenceClass(1)), 0) self.assertEqual(balanced_reduce(add, SequenceClass(1), 42), 42) d = {"one": 1, "two": 2, "three": 3} self.assertEqual(balanced_reduce(add, d), "".join(d.keys()))
def test_reduce(self): class Squares: def __init__(self, max): self.max = max self.sofar = [] def __len__(self): return len(self.sofar) def __getitem__(self, i): if not 0 <= i < self.max: raise IndexError n = len(self.sofar) while n <= i: self.sofar.append(n * n) n += 1 return self.sofar[i] def add(x, y): return x + y self.assertEqual(balanced_reduce(add, ['a', 'b', 'c'], ''), 'abc') self.assertEqual( balanced_reduce(add, [['a', 'c'], [], ['d', 'w']], []), ['a', 'c', 'd', 'w']) self.assertEqual(balanced_reduce(lambda x, y: x * y, range(2, 8), 1), 5040) self.assertEqual(balanced_reduce(lambda x, y: x * y, range(2, 21), 1), 2432902008176640000) self.assertEqual(balanced_reduce(add, Squares(10)), 285) self.assertEqual(balanced_reduce(add, Squares(10), 0), 285) self.assertEqual(balanced_reduce(add, Squares(0), 0), 0) self.assertRaises(TypeError, balanced_reduce) self.assertRaises(TypeError, balanced_reduce, 42, 42) self.assertRaises(TypeError, balanced_reduce, 42, 42, 42) self.assertEqual(balanced_reduce(42, "1"), "1") # func is never called with one item self.assertEqual(balanced_reduce(42, "", "1"), "1") # func is never called with one item self.assertRaises(TypeError, balanced_reduce, 42, (42, 42)) self.assertRaises( TypeError, balanced_reduce, add, []) # arg 2 must not be empty sequence with no initial value self.assertRaises(TypeError, balanced_reduce, add, "") self.assertRaises(TypeError, balanced_reduce, add, ()) self.assertRaises(TypeError, balanced_reduce, add, object()) class TestFailingIter: def __iter__(self): raise RuntimeError self.assertRaises(RuntimeError, balanced_reduce, add, TestFailingIter()) self.assertEqual(balanced_reduce(add, [], None), None) self.assertEqual(balanced_reduce(add, [], 42), 42) class BadSeq: def __getitem__(self, index): raise ValueError self.assertRaises(ValueError, balanced_reduce, 42, BadSeq())
def build_search_query_content(self, query, invert=False): if isinstance(query, PlainText): terms = query.query_string.split() if not terms: return None last_term = terms.pop() lexemes = Lexeme(last_term, invert=invert, prefix=self.LAST_TERM_IS_PREFIX) for term in terms: new_lexeme = Lexeme(term, invert=invert) if query.operator == "and": lexemes &= new_lexeme else: lexemes |= new_lexeme return SearchQuery(lexemes) elif isinstance(query, Phrase): return SearchQuery(query.query_string, search_type="phrase") elif isinstance(query, Boost): # Not supported msg = "The Boost query is not supported by the MySQL search backend." warnings.warn(msg, RuntimeWarning) return self.build_search_query_content(query.subquery, invert=invert) elif isinstance(query, Not): return self.build_search_query_content(query.subquery, invert=not invert) elif isinstance(query, (And, Or)): # If this part of the query is inverted, we swap the operator and # pass down the inversion state to the child queries. # This works thanks to De Morgan's law. # # For example, the following query: # # Not(And(Term("A"), Term("B"))) # # Is equivalent to: # # Or(Not(Term("A")), Not(Term("B"))) # # It's simpler to code it this way as we only need to store the # invert status of the terms rather than all the operators. subquery_lexemes = [ self.build_search_query_content(subquery, invert=invert) for subquery in query.subqueries ] is_and = isinstance(query, And) if invert: is_and = not is_and if is_and: return balanced_reduce(lambda a, b: a & b, subquery_lexemes) else: return balanced_reduce(lambda a, b: a | b, subquery_lexemes) raise NotImplementedError( "`%s` is not supported by the MySQL search backend." % query.__class__.__name__)