def __getitem__(self, key): '''Returns a paritioned QueryObject. If `key` is an integer it will be treated as ``slice(key, key+1)``. Otherwise it must be a slice. In this case if `self` has a partition the returning query object will have a partition that's a mix between self's partition and the `key`: - If both current start and key's start are negative, the max it's taken. Otherwise they are simply added. - If both current stop and key's stop are non-negative, the min it's taken. Otherwise they are simply added. - If both steps are non-negative, the max it's taken. Otherwise the min it's taken. ''' from copy import copy from xoutil.compat import integer from xoutil.objects import extract_attrs if isinstance(key, integer): key = slice(key, key+1) assert isinstance(key, slice) result = copy(self) partition = result.partition if partition: start, stop, step = extract_attrs(partition, 'start', 'stop', 'step') if not start: start = key.start elif not key.start: pass # Safe-keeper: In Py3k None is not comparable elif start <= 0 and key.start <= 0: start = max(start, key.start) else: start = start + key.start if not stop: stop = key.stop elif not key.stop: pass # Safe-keeper: In Py3k None is not comparable elif stop >= 0 and key.stop >= 0: stop = min(stop, key.stop) else: stop = stop + key.stop if not step: step = key.step elif not key.step: pass # Safe-keeper: In Py3k None is not comparable elif step >= 0 and key.step >= 0: step = max(step, key.step) else: step = min(step, key.step) partition = slice(start, stop, step) else: partition = key result.partition = partition return result
def test_extract_attrs(): from xoutil.objects import extract_attrs d = dict(a=(1,), b=2, c=3, x=4) assert extract_attrs(d, 'a') == (1,) assert extract_attrs(d, 'a', 'b', 'c', 'x') == ((1,), 2, 3, 4) with pytest.raises(AttributeError): assert extract_attrs(d, 'y') assert extract_attrs(d, 'y', default=None) is None class new(object): def __init__(self, **kw): self.__dict__.update(kw) d = new(a=(1,), b=2, c=3, x=4) assert extract_attrs(d, 'a') == (1,) assert extract_attrs(d, 'a', 'b', 'c', 'x') == ((1,), 2, 3, 4) with pytest.raises(AttributeError): assert extract_attrs(d, 'y') assert extract_attrs(d, 'y', default=None) is None
def naive_translation(query, **kwargs): '''Does a naive translation to Python's VM memory. ''' import functools from xotl.ql import translation as trans only = kwargs.get('only', None) def mix(filters, tokens): '''Intertwines tokens and filters.''' if not filters: return tokens return list(sorted(tokens + filters, key=functools.cmp_to_key(trans.cmp))) sorted_parts = mix(query.filters, query.tokens) assert isinstance(sorted_parts[0], GeneratorToken), sorted_parts def select(sel, vm): from xotl.ql.expressions import _false result = [] selectors = (sel, ) if not isinstance(sel, tuple) else sel for s in selectors: if isinstance(s, Term): result.append(var(s, vm)._get_current_value()) else: result.append(vminstr(s, vm)()) if any(res is _false for res in result): return _false if isinstance(sel, tuple): return tuple(result) else: assert len(result) == 1 return result[0] def plan(**plan_kwargs): # The algorithm is simple; first we "intertwine" tokens and filters # using a (stable) partial order: a filter comes before a token if and # only if neither of it's terms is bound to the token. # # The we just build several chained generators that either produce # (affect the vm) or filter. # from xoutil.objects import get_first_of from xotl.ql.expressions import _false parts = list(sorted_parts[:]) vm = get_first_of((plan_kwargs, kwargs), 'vm', default={}) # vm = plan_kwargs.get('vm', None) or kwargs.get('vm', None) or {} result = vmtoken(parts.pop(0), vm, query, only=only) while parts: part = parts.pop(0) if isinstance(part, GeneratorToken): result = vmtoken(part, vm, query, only=only).chain(result) else: result = vminstr(part, vm).chain(result) for _ in result: selected = select(query.selection, vm) if selected is not _false: yield selected if query.ordering: def plan_with_ordering(**plan_kwargs): vm = plan_kwargs.setdefault('vm', {}) key = lambda: tuple(vminstr(order_expr, vm=vm)() for order_expr in query.ordering) # XXX: Don't use sorted(plan(**plan_kwargs), key=key) # # Since the vm is constantly being updated after each yield we # must make sure, key() is called exactly when the vm has the # desired state for each object; but sorted may retreive # several items in chunks and call key aftewards which will # not yield the right results. return (sel for k, sel in sorted((key(), r) for r in plan(**plan_kwargs))) res = plan_with_ordering else: res = plan if query.partition: from xoutil.objects import extract_attrs start, stop, step = extract_attrs(query.partition, 'start', 'stop', 'step') def plan_with_partition(**kwargs): from itertools import islice return islice(res(**kwargs), start, stop, step) return plan_with_partition return res