def test_data_is_an_opendict(): c1 = object() with context(c1, a=1, b=1) as cc1: with context(c1, a=2) as cc2: assert cc2 is cc1 assert cc2.a == 2 assert cc2.b == 1 # Given by the upper enclosing level cc2.b = 'jaile!d' assert cc1.a == 1 assert cc1['b'] == 1
def test_simple_contexts(self): with context('CONTEXT-1'): self.assertIsNot(None, context['CONTEXT-1']) with context('CONTEXT-1'): with context('context-2'): self.assertIsNot(None, context['CONTEXT-1']) self.assertIsNot(None, context['context-2']) self.assertEquals(False, bool(context['context-2'])) self.assertIsNot(None, context['CONTEXT-1']) self.assertEquals(False, bool(context['CONTEXT-1']))
def test_20120822_reversed_eq_and_ne_should_compare_equal(self): expr = 1 == q("2") expr2 = q(1) == "2" with context(UNPROXIFING_CONTEXT): self.assertEqual(expr, expr2) # But we have not a reversing equality stuff. expr = 1 < q(2) expr2 = q(2) > 1 with context(UNPROXIFING_CONTEXT): self.assertNotEqual(expr, expr2)
def test_with_objects(self): CONTEXT1 = object() CONTEXT2 = object() with context(CONTEXT1): self.assertIsNot(None, context[CONTEXT1]) with context(CONTEXT1): with context(CONTEXT2): self.assertIsNot(None, context[CONTEXT1]) self.assertIsNot(None, context[CONTEXT2]) self.assertEquals(False, bool(context[CONTEXT2])) self.assertIsNot(None, context[CONTEXT1]) self.assertEquals(False, bool(context[CONTEXT1]))
def __iter__(self): '''Yields a single instance of a :class:`bound term <xotl.ql.interfaces.IBoundTerm>` that is a clone of the current. This allows an idiomatic way to express queries:: query_expression = ((parent, child) for parent in this('parent') for child in parent.children) See :class:`these`. ''' with context(UNPROXIFING_CONTEXT): name = self.name parent = self.parent if not name: # When iterating an instance without a name (i.e the `this` # object), we should generate a new name (of those simple # mortals can't use) with context('_INVALID_THESE_NAME'): name = self._newname() bound_term = Term(name, parent=parent, binding=None) else: bound_term = Term(name, parent=parent, binding=None) token = GeneratorToken(expression=bound_term) bound_term.binding = token # XXX: Since bound_terms might span accross subqueries, there might # be several stacked bubbles around. For instance:: # # (parent # for parent in this('parent') # if any_(child for child in parent.children if child.age < 6)) # # In this case, the enclosed parent.children emits to the top-most # bubble, but when the iter is invoked by `any_` to resolve its # argument a second bubble is in place and the hack below (XXX) # won't work. Furthermore, the token's expression is the bound-term # while the emmited part was the original one. So we keep the # original_term so that operations that resolver their arguments as # subquery may remove the escaped part. bound_term.original_term = self # XXX: When emiting a token in the context of query, if this token's # expression is also a part, then it was emitted without binding, so we # need to manually check this case here bubble = context[EXPRESSION_CAPTURING].get('bubble', None) if bubble: parts = bubble._parts if parts and self is parts[-1]: parts.pop(-1) _emit_token(token) yield bound_term
def test_regression_test_calling_terms_directly(self): 'Tests that invoke(term, ...) is equivalent to term(...)' from xotl.ql.expressions import invoke e1 = this('parent').children.updated_since(1, threshold=0.99) e2 = invoke(this('parent').children.updated_since, 1, threshold=0.99) with context(UNPROXIFING_CONTEXT): self.assertEqual(e1, e2)
def __init__(self, **kwargs): with context(UNPROXIFING_CONTEXT): self._expression = expression = kwargs.get('expression') assert IExpressionCapable.providedBy(expression) self._token = None self.token = token = kwargs.get('token') assert IGeneratorToken.providedBy(token)
def get_term_path(term): '''Returns a tuple of all the names in the path to a term. For example:: >>> from xotl.ql import this >>> get_term_path(this('p').a.b.c) ('p', 'a', 'b', 'c') The unnamed term ``this`` is treated specially by returning None. For example:: >>> get_term_path(this.a) (None, 'a') ''' with context(UNPROXIFING_CONTEXT): from xotl.ql.core import this res = [] current = term while current: if current is not this: res.insert(0, current.name) else: res.insert(0, None) current = current.parent return tuple(res)
def get_term_vm_path(term): '''Gets the root and the "path" of a term in the VM. The root is actually the token to which this term is bound to. The path is a list of identifiers which are traversal steps from the root. For instance:: these(child for parent in this if parent.children & (parent.age > 33) for child in parent.children if child.age < 5) The term `child.age` in the last filter is actually encoded by the names: parent, children, age; but since this term is bound to `parent.children`, only `age` is part of the path. ''' with context(UNPROXIFING_CONTEXT): token = term.binding root = token.expression res = [] current = term while current and current is not root: res.insert(0, current.name) current = current.parent return (token, res)
def invoke(self, *args): from xotl.ql.expressions import invoke as f with context(UNPROXIFING_CONTEXT): instance = self.expression token = self.token result = QueryPart(expression=f(instance, *args), token=token) return result
def test_cotraverse_expression(): from xoutil.compat import zip from xotl.ql.expressions import is_a from xotl.ql.translation import cotraverse_expression @thesefy class Person(object): pass @thesefy class Partnership(object): pass query = these((person, partner) for person, partner in zip(Person, Person) for rel in Partnership if (rel.subject == person) & (rel.obj == partner)) filters = list(query.filters) person, partner = query.selection person_is_a_person = is_a(person, Person) partner_is_a_person = is_a(partner, Person) rel_token = query.tokens[-1] rel_subject = rel_token.expression.subject rel_obj = rel_token.expression.obj with context(UNPROXIFING_CONTEXT): assert person != partner assert person_is_a_person in filters assert partner_is_a_person in filters expected_terms_order = [person, partner, rel_token.expression, rel_subject, person, rel_obj, partner] assert expected_terms_order == list(cotraverse_expression(query)) assert UNPROXIFING_CONTEXT not in context
def test_named_terms_matches_a_token(): ''' Ensures that all terms are named, and they are bound to a token that is in the query. ''' from xoutil.iterators import zip from xotl.ql.core import thesefy from xotl.ql.translation import cotraverse_expression @thesefy class Person(object): pass @thesefy class Partnership(object): pass query = these((person, partner) for person, partner in zip(Person, Person) for rel in Partnership if (rel.subject == person) & (rel.obj == partner) if person.age > 35) tokens = [tk.expression for tk in query.tokens] matches_token = lambda term: (term.name and ( term.binding.expression in tokens or matches_token(term.parent))) with context(UNPROXIFING_CONTEXT): assert all(matches_token(term) for term in cotraverse_expression(*query.filters))
def serialize(self, children, ctx=None): results = defaultdict(lambda: defaultdict(dict)) more_objects_required = True level = 0 while more_objects_required: future_children = defaultdict(lambda: { 'children': {}, 'parameters': { 'ids': set(), 'ctx': ctx }, }) with context('carbon14', children=future_children): results = self._serialize(level, results, children, ctx) more_objects_required = False for collection, objs in results.items(): ids = set(objs) parameters = future_children[collection]['parameters'] parameters['ids'] = parameters['ids'].difference(ids) for query in future_children.values(): if query['parameters']['ids']: more_objects_required = True children = future_children level += 1 return results
def method(self): with context(UNPROXIFING_CONTEXT): instance = self.expression token = self.token result = QueryPart(expression=operation(instance), token=token) return result
def capture_token(self, token): '''Captures an emitted token. When a token is emitted if the last previously created part is a term that *is* the same as the :attr:`IGeneratorToken.expression`, then this last term should be removed from the particles collection. This is because in a query like:: these((parent, child) for parent in this for child in parent.children) The `parent.children` emits itself as a query part and immediately it is transformed to a token. :param token: The emitted token :type token: :class:`IGeneratorToken` ''' tokens = self._tokens with context(UNPROXIFING_CONTEXT): assert IGeneratorToken.providedBy(token) parts = self._parts if parts: top = parts.pop(-1) if token.expression is not top: parts.append(top) if token not in tokens: tokens.append(token) self._particles.append(token)
def _resolve_arguments(cls, *children, **kwargs): '''Resolves the first and only positional argument as a sub-query by calling :class:`~xotl.ql.core.these`. If there is more than one positional argument, or even one keyword argument, or the first argument is not a generator object; then it leaves all the arguments the same. ''' import types first, rest = children[0], children[1:] if not first or (rest or kwargs): return children, kwargs else: if isinstance(first, types.GeneratorType): from xotl.ql.core import these first = these(first) # XXX: If this operation itself is enclosed in a # EXPRESSION_CAPTURING it might have occurred that a part # (actually a token's term) was emitted but then used as the # generator, so if the first token's binding original_term *is* # the last part emitted it should be removed from the bubble. bubble = context[EXPRESSION_CAPTURING].get('bubble', None) if bubble: parts = bubble._parts with context(UNPROXIFING_CONTEXT): term = first.tokens[0].expression if getattr(term, 'original_term', None) is parts[-1]: parts.pop(-1) return (first, ), {}
def test_worst_case_must_have_3_filters_and_3_tokens(self): from itertools import izip query = these(person for person, partner in izip(this('person'), this('partner')) for rel in this('relation') if rel.type == 'partnership' if rel.subject == person if rel.object == partner if partner.age > 32) filters = list(query.filters) tokens = list(query.tokens) person, partner, rel = this('person'), this('partner'), this('relation') expected_rel_type_filter = rel.type == 'partnership' expected_rel_subject_filter = rel.subject == person expected_rel_obj_filter = rel.object == partner expected_partner_age = partner.age > 32 with context(UNPROXIFING_CONTEXT): self.assertIs(4, len(filters)) self.assertIn(expected_rel_type_filter, filters) self.assertIn(expected_rel_subject_filter, filters) self.assertIn(expected_rel_obj_filter, filters) self.assertIn(expected_partner_age, filters) self.assertIn(person, tokens) self.assertIn(rel, tokens) self.assertIs(3, len(tokens)) self.assertIn(partner, tokens)
def __eq__(self, other): ''' >>> with context(UNPROXIFING_CONTEXT): ... this('parent') == this('parent') True >>> from xotl.ql.expressions import _true >>> (this('parent') == this('parent')) is _true True ''' from xotl.ql.expressions import eq with context(UNPROXIFING_CONTEXT): if isinstance(other, Term): res = validate_attrs(self, other, ('name', 'parent')) else: res = False if context[UNPROXIFING_CONTEXT]: return res else: if not res: return eq(self, other) else: # In logic A == A is always true so we don't produce nothing # for it. return _true
def test_named_terms_matches_a_token(self): ''' Ensures that all terms are named, and they are bound to a token that is in the query. ''' from itertools import izip from xotl.ql.core import thesefy from xotl.ql.translate import cofind_tokens @thesefy class Person(object): pass @thesefy class Partnership(object): pass query = these((person, partner) for person, partner in izip(Person, Person) for rel in Partnership if (rel.subject == person) & (rel.obj == partner) if person.age > 35) tokens = query.tokens matches_token = lambda term: (term.name and ( term.binding.expression in tokens or matches_token(term.parent))) with context(UNPROXIFING_CONTEXT): self.assertTrue(all(matches_token(term) for term in cofind_tokens(*query.filters)))
def __call__(self, *args, **kwargs): with context(UNPROXIFING_CONTEXT): instance = self.expression token = self.token result = QueryPart(expression=instance(*args, **kwargs), token=token) return result
def test_complex_query_with_3_tokens(self): query = these((parent.title + parent.name, child.name + child.nick, toy.name) for parent in this('parent') if parent.children for child in parent.children if child.toys for toy in child.toys if (parent.age > 32) & (child.age < 5) | (toy.type == 'laptop')) p = this('parent') c = p.children t = c.toys filters = query.filters expected_filters = [((p.age > 32) & (c.age < 5) | (t.type == 'laptop')), p.children, c.toys] tokens = query.tokens expected_tokens = [p, c, t] selection = query.selection expected_selection = (p.title + p.name, c.name + c.nick, t.name) with context(UNPROXIFING_CONTEXT): self.assertEqual(selection, expected_selection) self.assertEqual(len(expected_filters), len(filters)) for f in expected_filters: self.assertIn(f, filters) self.assertEqual(len(expected_tokens), len(tokens)) for t in expected_tokens: self.assertIn(t, tokens)
def _detect_class(self, query): '''Detects the class for top-level (i.e has no parent) token. Finds if there is any filter containing an ``is_instance(token, SomeClass)``. If SomeClass has an attribute `this_instances` and it returns an iterable, it is assumed it will yield all objects from this class. ''' token = self.token term = token.expression with context(UNPROXIFING_CONTEXT): parent = term.parent if not parent: from xotl.ql.expressions import is_instance, IExpressionTree def matches(node): with context(UNPROXIFING_CONTEXT): return (IExpressionTree.providedBy(node) and node.operation is is_instance and node.children[0] == term) from xotl.ql.translation import cotraverse_expression found = next(cotraverse_expression(*query.filters, accept=matches), None) if found: self._token_class = found.children[-1] else: self._token_class = None else: self._token_class = None
def test_complex_query_with_3_tokens(): query = these((parent.title + parent.name, child.name + child.nick, toy.name) for parent in this('parent') if parent.children for child in parent.children if child.toys for toy in child.toys if (parent.age > 32) & (child.age < 5) | (toy.type == 'laptop')) p = this('parent') c = p.children t = c.toys filters = query.filters expected_filters = [((p.age > 32) & (c.age < 5) | (t.type == 'laptop')), p.children, c.toys] tokens = [tk.expression for tk in query.tokens] expected_tokens = [p, c, t] selection = query.selection expected_selection = (p.title + p.name, c.name + c.nick, t.name) with context(UNPROXIFING_CONTEXT): assert selection == expected_selection assert len(expected_filters) == len(filters) for f in expected_filters: assert f in filters assert len(expected_tokens) == len(tokens) for t in expected_tokens: assert t in tokens
def test(self): operator = getattr(op, '_python_operator', op) query = these(parent for parent in this('p') if operator(parent.age, parent.check, parent.names)) expected = operator(this('p').age, this('p').check, this('p').names) self.assertIs(1, len(query.filters)) with context(UNPROXIFING_CONTEXT): self.assertEqual(expected, query.filters[0])
def test_named_terms_matches_a_token(self): ''' Ensures that all terms are named, and they are bound to a token that is in the query. ''' from itertools import izip from xotl.ql.core import thesefy from xotl.ql.translate import cofind_tokens @thesefy class Person(object): pass @thesefy class Partnership(object): pass query = these((person, partner) for person, partner in izip(Person, Person) for rel in Partnership if (rel.subject == person) & (rel.obj == partner) if person.age > 35) tokens = query.tokens matches_token = lambda term: (term.name and ( term.binding.expression in tokens or matches_token(term.parent))) with context(UNPROXIFING_CONTEXT): self.assertTrue( all( matches_token(term) for term in cofind_tokens(*query.filters)))
def test_basic_queries(): from xotl.ql.expressions import count query = these((parent.title + parent.name, count(child.toys)) for parent in this('parent') if parent.age < 40 for child in parent.children if child.age > 5) assert provides_any(query, IQueryObject) (parent_full_name, child_toys) = query.selection full_name_expectation = this('parent').title + this('parent').name child_toys_expectation = count(this('parent').children.toys) parent_age_test = this('parent').age < 40 children_age_test = this('parent').children.age > 5 parent_token = this('parent') children_token = this('parent').children with context(UNPROXIFING_CONTEXT): assert parent_full_name == full_name_expectation assert child_toys == child_toys_expectation filters = query.filters assert len(filters) == 2 assert parent_age_test in filters assert children_age_test in filters tokens = [tk.expression for tk in query.tokens] assert len(tokens) == 2 assert parent_token in tokens assert children_token in tokens
def test_thesefy_doesnot_messup_identities(self): from itertools import izip from xotl.ql.core import thesefy from xotl.ql.expressions import is_a @thesefy class Person(object): pass @thesefy class Partnership(object): pass query = these((person, partner) for person, partner in izip(Person, Person) for rel in Partnership if (rel.subject == person) & (rel.obj == partner)) filters = list(query.filters) person, partner = query.selection person_is_a_person = is_a(person, Person) partner_is_a_person = is_a(partner, Person) with context(UNPROXIFING_CONTEXT): self.assertNotEqual(person, partner) self.assertIn(person_is_a_person, filters) self.assertIn(partner_is_a_person, filters) filters.remove(person_is_a_person) filters.remove(partner_is_a_person)
def test_worst_case_must_have_3_filters_and_3_tokens(self): from itertools import izip query = these( person for person, partner in izip(this('person'), this('partner')) for rel in this('relation') if rel.type == 'partnership' if rel.subject == person if rel.object == partner if partner.age > 32) filters = list(query.filters) tokens = list(query.tokens) person, partner, rel = this('person'), this('partner'), this( 'relation') expected_rel_type_filter = rel.type == 'partnership' expected_rel_subject_filter = rel.subject == person expected_rel_obj_filter = rel.object == partner expected_partner_age = partner.age > 32 with context(UNPROXIFING_CONTEXT): self.assertIs(4, len(filters)) self.assertIn(expected_rel_type_filter, filters) self.assertIn(expected_rel_subject_filter, filters) self.assertIn(expected_rel_obj_filter, filters) self.assertIn(expected_partner_age, filters) self.assertIn(person, tokens) self.assertIn(rel, tokens) self.assertIs(3, len(tokens)) self.assertIn(partner, tokens)
def test_complex_query_with_3_tokens(self): query = these( (parent.title + parent.name, child.name + child.nick, toy.name) for parent in this('parent') if parent.children for child in parent.children if child.toys for toy in child.toys if (parent.age > 32) & (child.age < 5) | (toy.type == 'laptop')) p = this('parent') c = p.children t = c.toys filters = query.filters expected_filters = [ ((p.age > 32) & (c.age < 5) | (t.type == 'laptop')), p.children, c.toys ] tokens = query.tokens expected_tokens = [p, c, t] selection = query.selection expected_selection = (p.title + p.name, c.name + c.nick, t.name) with context(UNPROXIFING_CONTEXT): self.assertEqual(selection, expected_selection) self.assertEqual(len(expected_filters), len(filters)) for f in expected_filters: self.assertIn(f, filters) self.assertEqual(len(expected_tokens), len(tokens)) for t in expected_tokens: self.assertIn(t, tokens)
def test_basic_queries(self): from xotl.ql.expressions import count query = these((parent.title + parent.name, count(child.toys)) for parent in this('parent') if parent.age < 40 for child in parent.children if child.age > 5) self.assertTrue(provides_any(query, IQueryObject)) (parent_full_name, child_toys) = query.selection full_name_expectation = this('parent').title + this('parent').name # child_name_expectation = this('parent').children.name child_toys_expectation = count(this('parent').children.toys) parent_age_test = this('parent').age < 40 children_age_test = this('parent').children.age > 5 parent_token = this('parent') children_token = this('parent').children with context(UNPROXIFING_CONTEXT): self.assertEqual(parent_full_name, full_name_expectation) # self.assertEqual(child_name, child_name_expectation) self.assertEqual(child_toys, child_toys_expectation) filters = query.filters self.assertEqual(2, len(filters)) self.assertIn(parent_age_test, filters) self.assertIn(children_age_test, filters) tokens = query.tokens self.assertEqual(2, len(tokens)) self.assertIn(parent_token, tokens) self.assertIn(children_token, tokens)
def test_worst_case_must_have_3_filters_and_3_tokens(): from xoutil.iterators import zip query = these(person for person, partner in zip(this('person'), this('partner')) for rel in this('relation') if rel.type == 'partnership' if rel.subject == person if rel.object == partner if partner.age > 32) filters = list(query.filters) tokens = [tk.expression for tk in query.tokens] person, partner, rel = this('person'), this('partner'), this('relation') expected_rel_type_filter = rel.type == 'partnership' expected_rel_subject_filter = rel.subject == person expected_rel_obj_filter = rel.object == partner expected_partner_age = partner.age > 32 with context(UNPROXIFING_CONTEXT): assert len(filters) == 4 assert expected_rel_type_filter in filters assert expected_rel_subject_filter in filters assert expected_rel_obj_filter in filters assert expected_partner_age in filters assert len(tokens) == 3 assert person in tokens assert rel in tokens assert partner in tokens
def all_(self, *args): from xotl.ql.expressions import all_ as f with context(UNPROXIFING_CONTEXT): instance = self.expression token = self.token result = QueryPart(expression=f(instance, *args), token=token) _emit_part(result) return result
def __call__(self, *args, **kwargs): with context(UNPROXIFING_CONTEXT): parent = self.parent if parent is not None: from xotl.ql.expressions import invoke return ExpressionTree(invoke, self, *args, **kwargs) else: raise TypeError()
def __init__(self, name=None, **kwargs): with context(UNPROXIFING_CONTEXT): self.validate_name(name) self._name = name self._parent = kwargs.get('parent', None) binding = kwargs.get('binding', None) if binding: self.binding = binding
def test_named_children_equivalence(self): from xotl.ql.expressions import new expr1 = new(object, a=1, b=3) expr2 = new(object, b=3, a=1) expr3 = new(object, b=1) with context(UNPROXIFING_CONTEXT): self.assertEqual(expr1, expr2) self.assertNotEqual(expr1, expr3)
def test_right_bindings(): with bubbling() as current: next((parent, child) for parent in this('parent') if parent.children.updated_since(days=1) for child in parent.children if child.age < 4) # The query has two filters: # # this('parent').children & (count(this('parent').children) > 4) # this('parent').children.age < 5 # # If we regard every term `this('parent').children` as the *token*, # what would be the meaning of the first condition? How do we # distinguish from conditions over the named-token and the expression # that generates the token? i.e in `for child in parent.children`, the # `child` token is *not* the same as the term `parent.children`. # # Now the token of the relevant query might help, but then the machine # should not strip those tokens from query-parts. parts = current.bubble.parts bubble_tokens = current.bubble.tokens assert len(parts) == 2 with context(UNPROXIFING_CONTEXT): parent_token = next((token for token in bubble_tokens if token.expression == this('parent')), None) children_token = next((token for token in bubble_tokens if token.expression != this('parent')), None) child_age_filter = parts.pop(-1) parent_children_updated_filter = parts.pop(-1) with pytest.raises(IndexError): parts.pop(-1) with context(UNPROXIFING_CONTEXT): child_age_term = child_age_filter.children[0] assert child_age_term.binding == children_token # Note that: parent.children.updated_since(days=1) # is equivalent to invoke(parent.children.updated_since, days=1) parent_children_term = parent_children_updated_filter.children[0] assert parent_children_term.binding == parent_token assert dict(days=1) == parent_children_updated_filter.named_children
def enter(cls): '''Enter the context. Use as ``with _bag_assign.enter()``: ... ''' from xoutil.context import context return context(cls)
def test_expression(self): from xotl.ql.expressions import (count, ExpressionTree, or_, and_, pow_, lt, eq, add) expr = ((q(1) < 3) & (1 == q("1")) | (q("a") + q("b")**2 == q("x") + count("y"))) expected = or_(and_(lt(1, 3), eq(1, "1")), eq(add("a", pow_("b", 2)), add("x", count("y")))) self.assertIsInstance(expr, ExpressionTree) with context(UNPROXIFING_CONTEXT): self.assertTrue(expr == expected, "%s ---- %s" % (expected, expr))
def test_free_terms_are_not_captured(self): from xotl.ql.expressions import any_ next(parent for parent in this('parent') if parent.name if any_(this.children, this.age < 6)) parts = self.bubble.parts self.assertIs(1, len(parts)) pname = this('parent').name with context(UNPROXIFING_CONTEXT): self.assertIn(pname, parts)
def __iter__(self): with context(UNPROXIFING_CONTEXT): expression = self.expression # XXX: In cases of sub-queries the part will be emitted, but the # iter will be hold, so the token won't be emitted and the # part won't be removed. So we're bringing this check back. bubble = _get_current_bubble() assert bubble if bubble._parts and expression is bubble._parts[-1]: bubble._parts.pop(-1) return iter(expression)
def __getattribute__(self, attr): get = super(QueryPart, self).__getattribute__ if context[UNPROXIFING_CONTEXT]: return get(attr) else: with context(UNPROXIFING_CONTEXT): instance = get('expression') token = get('token') result = QueryPart(expression=getattr(instance, attr), token=token) _emit_part(result) return result
def test_is_a_partnership_is_not_forgotten(self): from itertools import izip query = these( (person, partner) for person, partner in izip(this('person'), this('partner')) for rel in this('relation') if rel.type == 'partnership' if (rel.subject == person) & (rel.object == partner)) filters = list(query.filters) expected_rel_type = this('relation').type == 'partnership' with context(UNPROXIFING_CONTEXT): self.assertIn(expected_rel_type, filters) self.assertIs(2, len(filters))
def __str__(self): with context(UNPROXIFING_CONTEXT): name = self.name parent = self.parent if parent is None and not name: return 'this' elif parent is None and name: return "this('{name}')".format(name=name) elif parent is not None and name: return "{parent}.{name}".format(parent=str(parent), name=name) else: # parent and not name: assert False
def test_right_bindings(self): next((parent, child) for parent in this('parent') if parent.children.updated_since(days=1) for child in parent.children if child.age < 4) # The query has two filters: # # this('parent').children & (count(this('parent').children) > 4) # this('parent').children.age < 5 # # If we regard every term `this('parent').children` as the *token*, # what would be the meaning of the first condition? How do we # distinguish from conditions over the named-token and the # expression that generates the token? # i.e in `for child in parent.children`, the `child` token # is *not* the same as the term `parent.children`. # # Now the token of the relevant query might help, but then the # machine should not strip those tokens from query-parts. parts = self.bubble.parts bubble_tokens = self.bubble.tokens with context(UNPROXIFING_CONTEXT): parent_token = next((token for token in bubble_tokens if token.expression == this('parent')), None) children_token = next((token for token in bubble_tokens if token.expression != this('parent')), None) child_age_filter = parts.pop(-1) parent_children_updated_filter = parts.pop(-1) with self.assertRaises(IndexError): parts.pop(-1) with context(UNPROXIFING_CONTEXT): child_age_term = child_age_filter.children[0] self.assertEqual(child_age_term.binding, children_token) # Note that: parent.children.updated_since(days=1) # is equivalent to invoke(parent.children.updated_since, days=1) parent_children_term = parent_children_updated_filter.children[0] self.assertEqual(parent_children_term.binding, parent_token) self.assertEqual(dict(days=1), parent_children_updated_filter.named_children)
def method(self, other): with context(UNPROXIFING_CONTEXT): instance = self.expression token = self.token if IQueryPart.providedBy(other): other = other.expression if not inverse: result = QueryPart(expression=operation(instance, other), token=token) else: result = QueryPart(expression=operation(other, instance), token=token) return result
def test_calling_functions(self): from xotl.ql.expressions import call expression = this.startswith('manu') self.assertIsInstance(expression, ExpressionTree) self.assertEqual("call(this.startswith, manu)", str(expression)) equiv_expr = call(this.startswith, 'manu') with context(UNPROXIFING_CONTEXT): self.assertEqual(equiv_expr, expression) # But the calling a these instance directly is not supported # (I think is not pretty) with self.assertRaises(TypeError): this('someone')('cannot', 'call', 'me')
def capture_part(self, part): '''Captures an emitted query part. When a given part is captured, it might replace the lastly previously emitted parts if either of the following conditions hold: - The capture part *is* the same last emitted part. - The captured part is a term, and the last emitted part *is* its parent, then the parent part is replaced by the newly captured part. - The captured part is an expression and the last emitted part *is* one of its children (named or positional). The previous conditions are cycled while any of them hold against the particle at the "end" of the :attr:`particles` collection. Note that in an expression like ``invoke(some, a > b, b > c, argument=(c > d))`` before the whole expression is formed (and thus the part that represents it is captured), all of the arguments emitted particles, so we should remove those contained parts and just keep the bigger one that has them all. .. note:: Checks **are** done with the `is` operator and not with `==`. Doing otherwise may lead to undesired results:: these(parent.name for parent in this if parent.name) If `==` would be used, then the filter part `parent.name` would be lost. :param part: The emitted query part :type part: :class:`IQueryPart` ''' with context(UNPROXIFING_CONTEXT): if provides_all(part, IQueryPart): expression = part.expression parts = self._parts if parts: mergable = self.mergable while parts and mergable(expression): top = parts.pop() self._particles.remove(top) parts.append(expression) self._particles.append(expression) else: assert False
def test_yield_once_per_query(self): q = (a for c in this for b in c.bs for a in b.a) self.assertIsNotNone(next(q)) with self.assertRaises(StopIteration): next(q) # TODO: Document how to obtain the queries qs = (a for i in range(3) for b in this('b' + str(i)) for a in b.a) for i in range(3): expected = this('b' + str(i)).a returned = unboxed(next(qs)).expression with context(UNPROXIFING_CONTEXT): self.assertEqual(expected, returned) with self.assertRaises(StopIteration): next(qs)
def test_theres_a_token_for_partnership(self): from itertools import izip query = these( (person, partner) for person, partner in izip(this('person'), this('partner')) for rel in this('relation') if rel.type == 'partnership' if (rel.subject == person) & (rel.object == partner)) tokens = list(query.tokens) person, partner, rel = this('person'), this('partner'), this( 'relation') with context(UNPROXIFING_CONTEXT): self.assertIs(3, len(tokens)) self.assertIn(rel, tokens) self.assertIn(person, tokens) self.assertIn(partner, tokens)
def test_thesefy_good_meta(self): from xotl.ql.core import thesefy class Meta(type): def __iter__(self): from xoutil.objects import nameof return iter(this(nameof(self))) @thesefy("Person") class Person(object): __metaclass__ = Meta q = these(who for who in Person if who.age > 30) q1 = these(who for who in this('Person') if who.age > 30) with context(UNPROXIFING_CONTEXT): self.assertEqual(q.selection, q1.selection)
def test_most_basic_query(self): query = these(parent for parent in this('parent') if parent.age > 40) self.assertTrue(provides_any(query, IQueryObject)) (p, ) = query.selection token_expectation = p_expected = this('parent') filter_expectation = this('parent').age > 40 with context(UNPROXIFING_CONTEXT): self.assertEqual(p, p_expected) filters = query.filters self.assertEqual(1, len(filters)) self.assertIn(filter_expectation, filters) tokens = query.tokens self.assertEqual(1, len(tokens)) self.assertIn(token_expectation, tuple(tokens))