diff --git a/yaltik_dsl/__coconut__.py b/yaltik_dsl/__coconut__.py deleted file mode 100644 index 410ec79..0000000 --- a/yaltik_dsl/__coconut__.py +++ /dev/null @@ -1,725 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -# type: ignore - -# Compiled with Coconut version 1.4.3 [Ernest Scribbler] - -"""Built-in Coconut utilities.""" - -# Coconut Header: ------------------------------------------------------------- - -from __future__ import print_function, absolute_import, unicode_literals, division -import sys as _coconut_sys -from __builtin__ import chr, filter, hex, input, int, map, object, oct, open, print, range, str, zip, filter, reversed, enumerate, raw_input, xrange -py_chr, py_hex, py_input, py_int, py_map, py_object, py_oct, py_open, py_print, py_range, py_str, py_zip, py_filter, py_reversed, py_enumerate, py_raw_input, py_xrange, py_repr = chr, hex, input, int, map, object, oct, open, print, range, str, zip, filter, reversed, enumerate, raw_input, xrange, repr -_coconut_NotImplemented, _coconut_raw_input, _coconut_xrange, _coconut_int, _coconut_long, _coconut_print, _coconut_str, _coconut_unicode, _coconut_repr = NotImplemented, raw_input, xrange, int, long, print, str, unicode, repr -from future_builtins import * -chr, str = unichr, unicode -from io import open -class object(object): - __slots__ = () - def __ne__(self, other): - eq = self == other - if eq is _coconut_NotImplemented: - return eq - return not eq -class int(_coconut_int): - __slots__ = () - if hasattr(_coconut_int, "__doc__"): - __doc__ = _coconut_int.__doc__ - class __metaclass__(type): - def __instancecheck__(cls, inst): - return _coconut.isinstance(inst, (_coconut_int, _coconut_long)) - def __subclasscheck__(cls, subcls): - return _coconut.issubclass(subcls, (_coconut_int, _coconut_long)) -class range(object): - __slots__ = ("_xrange",) - if hasattr(_coconut_xrange, "__doc__"): - __doc__ = _coconut_xrange.__doc__ - def __init__(self, *args): - self._xrange = _coconut_xrange(*args) - def __iter__(self): - return _coconut.iter(self._xrange) - def __reversed__(self): - return _coconut.reversed(self._xrange) - def __len__(self): - return _coconut.len(self._xrange) - def __contains__(self, elem): - return elem in self._xrange - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - args = _coconut.slice(*self._args) - start, stop, step, ind_step = (args.start if args.start is not None else 0), args.stop, (args.step if args.step is not None else 1), (index.step if index.step is not None else 1) - return self.__class__((start if ind_step >= 0 else stop - step) if index.start is None else start + step * index.start if index.start >= 0 else stop + step * index.start, (stop if ind_step >= 0 else start - step) if index.stop is None else start + step * index.stop if index.stop >= 0 else stop + step * index.stop, step if index.step is None else step * index.step) - else: - return self._xrange[index] - def count(self, elem): - """Count the number of times elem appears in the range.""" - return _coconut_int(elem in self._xrange) - def index(self, elem): - """Find the index of elem in the range.""" - if elem not in self._xrange: raise _coconut.ValueError(_coconut.repr(elem) + " is not in range") - start, _, step = self._xrange.__reduce_ex__(2)[1] - return (elem - start) // step - def __repr__(self): - return _coconut.repr(self._xrange)[1:] - @property - def _args(self): - return self._xrange.__reduce__()[1] - def __reduce_ex__(self, protocol): - return (self.__class__, self._xrange.__reduce_ex__(protocol)[1]) - def __reduce__(self): - return self.__reduce_ex__(_coconut.pickle.DEFAULT_PROTOCOL) - def __hash__(self): - return _coconut.hash(self._args) - def __copy__(self): - return self.__class__(*self._args) - def __eq__(self, other): - return _coconut.isinstance(other, self.__class__) and self._args == other._args -from collections import Sequence as _coconut_Sequence -_coconut_Sequence.register(range) -from functools import wraps as _coconut_wraps -@_coconut_wraps(_coconut_print) -def print(*args, **kwargs): - file = kwargs.get("file", _coconut_sys.stdout) - flush = kwargs.get("flush", False) - if "flush" in kwargs: - del kwargs["flush"] - if _coconut.hasattr(file, "encoding") and file.encoding is not None: - _coconut_print(*(_coconut_unicode(x).encode(file.encoding) for x in args), **kwargs) - else: - _coconut_print(*(_coconut_unicode(x).encode() for x in args), **kwargs) - if flush: - file.flush() -@_coconut_wraps(_coconut_raw_input) -def input(*args, **kwargs): - if _coconut.hasattr(_coconut_sys.stdout, "encoding") and _coconut_sys.stdout.encoding is not None: - return _coconut_raw_input(*args, **kwargs).decode(_coconut_sys.stdout.encoding) - return _coconut_raw_input(*args, **kwargs).decode() -@_coconut_wraps(_coconut_repr) -def repr(obj): - if isinstance(obj, _coconut_unicode): - return _coconut_unicode(_coconut_repr(obj)[1:]) - if isinstance(obj, _coconut_str): - return "b" + _coconut_unicode(_coconut_repr(obj)) - return _coconut_unicode(_coconut_repr(obj)) -ascii = repr -def raw_input(*args): - """Coconut uses Python 3 "input" instead of Python 2 "raw_input".""" - raise _coconut.NameError('Coconut uses Python 3 "input" instead of Python 2 "raw_input"') -def xrange(*args): - """Coconut uses Python 3 "range" instead of Python 2 "xrange".""" - raise _coconut.NameError('Coconut uses Python 3 "range" instead of Python 2 "xrange"') -class _coconut(object): - import collections, copy, functools, types, itertools, operator, threading, weakref, os, warnings - try: - from backports.functools_lru_cache import lru_cache - functools.lru_cache = lru_cache - except ImportError: pass - try: - import trollius as asyncio - except ImportError: - class you_need_to_install_trollius: pass - asyncio = you_need_to_install_trollius() - import cPickle as pickle - OrderedDict = collections.OrderedDict - abc = collections - class typing(object): - @staticmethod - def NamedTuple(name, fields): - return _coconut.collections.namedtuple(name, [x for x, t in fields]) - Ellipsis, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, classmethod, dict, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, map, min, max, next, object, property, range, reversed, set, slice, str, sum, super, tuple, type, zip, repr, bytearray = Ellipsis, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, classmethod, dict, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, map, min, max, next, object, property, range, reversed, set, slice, str, sum, super, tuple, type, zip, staticmethod(repr), bytearray -_coconut_sentinel = _coconut.object() -class MatchError(Exception): - """Pattern-matching error. Has attributes .pattern and .value.""" - __slots__ = ("pattern", "value") -def _coconut_igetitem(iterable, index): - if isinstance(iterable, (_coconut_reversed, _coconut_map, _coconut.zip, _coconut_enumerate, _coconut_count, _coconut.abc.Sequence)): - return iterable[index] - if not _coconut.isinstance(index, _coconut.slice): - if index < 0: - return _coconut.collections.deque(iterable, maxlen=-index)[0] - return _coconut.next(_coconut.itertools.islice(iterable, index, index + 1)) - if index.start is not None and index.start < 0 and (index.stop is None or index.stop < 0) and index.step is None: - queue = _coconut.collections.deque(iterable, maxlen=-index.start) - if index.stop is not None: - queue = _coconut.list(queue)[:index.stop - index.start] - return queue - if (index.start is not None and index.start < 0) or (index.stop is not None and index.stop < 0) or (index.step is not None and index.step < 0): - return _coconut.list(iterable)[index] - return _coconut.itertools.islice(iterable, index.start, index.stop, index.step) -class _coconut_base_compose(object): - __slots__ = ("func", "funcstars") - def __init__(self, func, *funcstars): - self.func = func - self.funcstars = [] - for f, stars in funcstars: - if _coconut.isinstance(f, _coconut_base_compose): - self.funcstars.append((f.func, stars)) - self.funcstars += f.funcstars - else: - self.funcstars.append((f, stars)) - def __call__(self, *args, **kwargs): - arg = self.func(*args, **kwargs) - for f, stars in self.funcstars: - if stars == 0: - arg = f(arg) - elif stars == 1: - arg = f(*arg) - elif stars == 2: - arg = f(**arg) - else: - raise _coconut.ValueError("invalid arguments to " + _coconut.repr(self)) - return arg - def __repr__(self): - return _coconut.repr(self.func) + " " + " ".join(("..*> " if star == 1 else "..**>" if star == 2 else "..> ") + _coconut.repr(f) for f, star in self.funcstars) - def __reduce__(self): - return (self.__class__, (self.func,) + _coconut.tuple(self.funcstars)) - def __get__(self, obj, objtype=None): - return _coconut.functools.partial(self, obj) -def _coconut_forward_compose(func, *funcs): return _coconut_base_compose(func, *((f, 0) for f in funcs)) -def _coconut_back_compose(*funcs): return _coconut_forward_compose(*_coconut.reversed(funcs)) -def _coconut_forward_star_compose(func, *funcs): return _coconut_base_compose(func, *((f, 1) for f in funcs)) -def _coconut_back_star_compose(*funcs): return _coconut_forward_star_compose(*_coconut.reversed(funcs)) -def _coconut_forward_dubstar_compose(func, *funcs): return _coconut_base_compose(func, *((f, 2) for f in funcs)) -def _coconut_back_dubstar_compose(*funcs): return _coconut_forward_dubstar_compose(*_coconut.reversed(funcs)) -def _coconut_pipe(x, f): return f(x) -def _coconut_star_pipe(xs, f): return f(*xs) -def _coconut_dubstar_pipe(kws, f): return f(**kws) -def _coconut_back_pipe(f, x): return f(x) -def _coconut_back_star_pipe(f, xs): return f(*xs) -def _coconut_back_dubstar_pipe(f, kws): return f(**kws) -def _coconut_assert(cond, msg=None): assert cond, msg if msg is not None else "(assert) got falsey value " + _coconut.repr(cond) -def _coconut_bool_and(a, b): return a and b -def _coconut_bool_or(a, b): return a or b -def _coconut_none_coalesce(a, b): return a if a is not None else b -def _coconut_minus(a, *rest): - if not rest: - return -a - for b in rest: - a = a - b - return a -@_coconut.functools.wraps(_coconut.itertools.tee) -def tee(iterable, n=2): - if n >= 0 and _coconut.isinstance(iterable, (_coconut.tuple, _coconut.frozenset)): - return (iterable,) * n - if n > 0 and (_coconut.hasattr(iterable, "__copy__") or _coconut.isinstance(iterable, _coconut.abc.Sequence)): - return (iterable,) + _coconut.tuple(_coconut.copy.copy(iterable) for _ in _coconut.range(n - 1)) - return _coconut.itertools.tee(iterable, n) -class reiterable(object): - """Allows an iterator to be iterated over multiple times.""" - __slots__ = ("iter",) - def __init__(self, iterable): - self.iter = iterable - def _get_new_iter(self): - self.iter, new_iter = _coconut_tee(self.iter) - return new_iter - def __iter__(self): - return _coconut.iter(self._get_new_iter()) - def __getitem__(self, index): - return _coconut_igetitem(self._get_new_iter(), index) - def __reversed__(self): - return _coconut_reversed(self._get_new_iter()) - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "reiterable(%r)" % (self.iter,) - def __reduce__(self): - return (self.__class__, (self.iter,)) - def __copy__(self): - return self.__class__(self._get_new_iter()) - def __fmap__(self, func): - return _coconut_map(func, self) -class scan(object): - """Reduce func over iterable, yielding intermediate results, - optionally starting from initializer.""" - __slots__ = ("func", "iter", "initializer") - def __init__(self, function, iterable, initializer=_coconut_sentinel): - self.func = function - self.iter = iterable - self.initializer = initializer - def __iter__(self): - acc = self.initializer - if acc is not _coconut_sentinel: - yield acc - for item in self.iter: - if acc is _coconut_sentinel: - acc = item - else: - acc = self.func(acc, item) - yield acc - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "scan(%r, %r)" % (self.func, self.iter) - def __reduce__(self): - return (self.__class__, (self.func, self.iter)) - def __copy__(self): - return self.__class__(self.func, _coconut.copy.copy(self.iter)) - def __fmap__(self, func): - return _coconut_map(func, self) -class reversed(object): - __slots__ = ("iter",) - if hasattr(_coconut.map, "__doc__"): - __doc__ = _coconut.reversed.__doc__ - def __new__(cls, iterable): - if _coconut.isinstance(iterable, _coconut.range): - return iterable[::-1] - if not _coconut.hasattr(iterable, "__reversed__") or _coconut.isinstance(iterable, (_coconut.list, _coconut.tuple)): - return _coconut.object.__new__(cls) - return _coconut.reversed(iterable) - def __init__(self, iterable): - self.iter = iterable - def __iter__(self): - return _coconut.iter(_coconut.reversed(self.iter)) - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - return _coconut_igetitem(self.iter, _coconut.slice(-(index.start + 1) if index.start is not None else None, -(index.stop + 1) if index.stop else None, -(index.step if index.step is not None else 1))) - return _coconut_igetitem(self.iter, -(index + 1)) - def __reversed__(self): - return self.iter - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "reversed(%r)" % (self.iter,) - def __hash__(self): - return -_coconut.hash(self.iter) - def __reduce__(self): - return (self.__class__, (self.iter,)) - def __copy__(self): - return self.__class__(_coconut.copy.copy(self.iter)) - def __eq__(self, other): - return isinstance(other, self.__class__) and self.iter == other.iter - def __contains__(self, elem): - return elem in self.iter - def count(self, elem): - """Count the number of times elem appears in the reversed iterator.""" - return self.iter.count(elem) - def index(self, elem): - """Find the index of elem in the reversed iterator.""" - return _coconut.len(self.iter) - self.iter.index(elem) - 1 - def __fmap__(self, func): - return self.__class__(_coconut_map(func, self.iter)) -class map(_coconut.map): - __slots__ = ("func", "iters") - if hasattr(_coconut.map, "__doc__"): - __doc__ = _coconut.map.__doc__ - def __new__(cls, function, *iterables): - new_map = _coconut.map.__new__(cls, function, *iterables) - new_map.func = function - new_map.iters = iterables - return new_map - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - return self.__class__(self.func, *(_coconut_igetitem(i, index) for i in self.iters)) - return self.func(*(_coconut_igetitem(i, index) for i in self.iters)) - def __reversed__(self): - return self.__class__(self.func, *(_coconut_reversed(i) for i in self.iters)) - def __len__(self): - return _coconut.min(_coconut.len(i) for i in self.iters) - def __repr__(self): - return "map(%r, %s)" % (self.func, ", ".join((_coconut.repr(i) for i in self.iters))) - def __reduce__(self): - return (self.__class__, (self.func,) + self.iters) - def __reduce_ex__(self, _): - return self.__reduce__() - def __copy__(self): - return self.__class__(self.func, *_coconut.map(_coconut.copy.copy, self.iters)) - def __fmap__(self, func): - return self.__class__(_coconut_forward_compose(self.func, func), *self.iters) -class parallel_map(map): - """Multi-process implementation of map using concurrent.futures. - Requires arguments to be pickleable.""" - __slots__ = () - def __iter__(self): - from concurrent.futures import ProcessPoolExecutor - with ProcessPoolExecutor() as executor: - return _coconut.iter(_coconut.list(executor.map(self.func, *self.iters))) - def __repr__(self): - return "parallel_" + _coconut_map.__repr__(self) -class concurrent_map(map): - """Multi-thread implementation of map using concurrent.futures.""" - __slots__ = () - def __iter__(self): - from concurrent.futures import ThreadPoolExecutor - from multiprocessing import cpu_count # cpu_count() * 5 is the default Python 3.5 thread count - with ThreadPoolExecutor(cpu_count() * 5) as executor: - return _coconut.iter(_coconut.list(executor.map(self.func, *self.iters))) - def __repr__(self): - return "concurrent_" + _coconut_map.__repr__(self) -class filter(_coconut.filter): - __slots__ = ("func", "iter") - if hasattr(_coconut.filter, "__doc__"): - __doc__ = _coconut.filter.__doc__ - def __new__(cls, function, iterable): - new_filter = _coconut.filter.__new__(cls, function, iterable) - new_filter.func = function - new_filter.iter = iterable - return new_filter - def __reversed__(self): - return self.__class__(self.func, _coconut_reversed(self.iter)) - def __repr__(self): - return "filter(%r, %r)" % (self.func, self.iter) - def __reduce__(self): - return (self.__class__, (self.func, self.iter)) - def __reduce_ex__(self, _): - return self.__reduce__() - def __copy__(self): - return self.__class__(self.func, _coconut.copy.copy(self.iter)) - def __fmap__(self, func): - return _coconut_map(func, self) -class zip(_coconut.zip): - __slots__ = ("iters",) - if hasattr(_coconut.zip, "__doc__"): - __doc__ = _coconut.zip.__doc__ - def __new__(cls, *iterables): - new_zip = _coconut.zip.__new__(cls, *iterables) - new_zip.iters = iterables - return new_zip - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - return self.__class__(*(_coconut_igetitem(i, index) for i in self.iters)) - return _coconut.tuple(_coconut_igetitem(i, index) for i in self.iters) - def __reversed__(self): - return self.__class__(*(_coconut_reversed(i) for i in self.iters)) - def __len__(self): - return _coconut.min(_coconut.len(i) for i in self.iters) - def __repr__(self): - return "zip(%s)" % (", ".join((_coconut.repr(i) for i in self.iters)),) - def __reduce__(self): - return (self.__class__, self.iters) - def __reduce_ex__(self, _): - return self.__reduce__() - def __copy__(self): - return self.__class__(*_coconut.map(_coconut.copy.copy, self.iters)) - def __fmap__(self, func): - return _coconut_map(func, self) -class enumerate(_coconut.enumerate): - __slots__ = ("iter", "start") - if hasattr(_coconut.enumerate, "__doc__"): - __doc__ = _coconut.enumerate.__doc__ - def __new__(cls, iterable, start=0): - new_enumerate = _coconut.enumerate.__new__(cls, iterable, start) - new_enumerate.iter = iterable - new_enumerate.start = start - return new_enumerate - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - return self.__class__(_coconut_igetitem(self.iter, index), self.start + (0 if index.start is None else index.start if index.start >= 0 else len(self.iter) + index.start)) - return (self.start + index, _coconut_igetitem(self.iter, index)) - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "enumerate(%r, %r)" % (self.iter, self.start) - def __reduce__(self): - return (self.__class__, (self.iter, self.start)) - def __reduce_ex__(self, _): - return self.__reduce__() - def __copy__(self): - return self.__class__(_coconut.copy.copy(self.iter), self.start) - def __fmap__(self, func): - return _coconut_map(func, self) -class count(object): - """count(start, step) returns an infinite iterator starting at start and increasing by step. - If step is set to 0, count will infinitely repeat its first argument.""" - __slots__ = ("start", "step") - def __init__(self, start=0, step=1): - self.start = start - self.step = step - def __iter__(self): - while True: - yield self.start - if self.step: - self.start += self.step - def __contains__(self, elem): - if not self.step: - return elem == self.start - if elem < self.start: - return False - return (elem - self.start) % self.step == 0 - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice) and (index.start is None or index.start >= 0) and (index.stop is None or index.stop >= 0): - new_start, new_step = self.start, self.step - if self.step and index.start is not None: - new_start += self.step * index.start - if self.step and index.step is not None: - new_step *= index.step - if index.stop is None: - return self.__class__(new_start, new_step) - if self.step and _coconut.isinstance(self.start, _coconut.int) and _coconut.isinstance(self.step, _coconut.int): - return _coconut.range(new_start, self.start + self.step * index.stop, new_step) - return _coconut_map(self.__getitem__, _coconut.range(index.start if index.start is not None else 0, index.stop, index.step if index.step is not None else 1)) - if index < 0: - raise _coconut.IndexError("count indices must be positive") - return self.start + self.step * index if self.step else self.start - def count(self, elem): - """Count the number of times elem appears in the count.""" - if not self.step: - return _coconut.float("inf") if elem == self.start else 0 - return int(elem in self) - def index(self, elem): - """Find the index of elem in the count.""" - if elem not in self: - raise _coconut.ValueError(_coconut.repr(elem) + " not in " + _coconut.repr(self)) - return (elem - self.start) // self.step if self.step else 0 - def __reversed__(self): - if not self.step: - return self - raise _coconut.TypeError(repr(self) + " object is not reversible") - def __repr__(self): - return "count(%r, %r)" % (self.start, self.step) - def __hash__(self): - return _coconut.hash((self.start, self.step)) - def __reduce__(self): - return (self.__class__, (self.start, self.step)) - def __copy__(self): - return self.__class__(self.start, self.step) - def __eq__(self, other): - return isinstance(other, self.__class__) and self.start == other.start and self.step == other.step - def __fmap__(self, func): - return _coconut_map(func, self) -class groupsof(object): - """groupsof(n, iterable) splits iterable into groups of size n. - If the length of the iterable is not divisible by n, the last group may be of size < n.""" - __slots__ = ("group_size", "iter") - def __init__(self, n, iterable): - self.iter = iterable - try: - self.group_size = _coconut.int(n) - except _coconut.ValueError: - raise _coconut.TypeError("group size must be an int; not %r" % (n,)) - if self.group_size <= 0: - raise _coconut.ValueError("group size must be > 0; not %r" % (self.group_size,)) - def __iter__(self): - iterator = _coconut.iter(self.iter) - loop = True - while loop: - group = [] - for _ in _coconut.range(self.group_size): - try: - group.append(_coconut.next(iterator)) - except _coconut.StopIteration: - loop = False - break - if group: - yield _coconut.tuple(group) - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "groupsof(%r)" % (self.iter,) - def __reduce__(self): - return (self.__class__, (self.group_size, self.iter)) - def __copy__(self): - return self.__class__(self.group_size, _coconut.copy.copy(self.iter)) - def __fmap__(self, func): - return _coconut_map(func, self) -class recursive_iterator(object): - """Decorator that optimizes a function for iterator recursion.""" - __slots__ = ("func", "tee_store", "backup_tee_store") - def __init__(self, func): - self.func = func - self.tee_store = {} - self.backup_tee_store = [] - def __call__(self, *args, **kwargs): - key = (args, _coconut.frozenset(kwargs)) - use_backup = False - try: - hash(key) - except _coconut.Exception: - try: - key = _coconut.pickle.dumps(key, -1) - except _coconut.Exception: - use_backup = True - if use_backup: - for i, (k, v) in _coconut.enumerate(self.backup_tee_store): - if k == key: - to_tee, store_pos = v, i - break - else: # no break - to_tee = self.func(*args, **kwargs) - store_pos = None - to_store, to_return = _coconut_tee(to_tee) - if store_pos is None: - self.backup_tee_store.append([key, to_store]) - else: - self.backup_tee_store[store_pos][1] = to_store - else: - self.tee_store[key], to_return = _coconut_tee(self.tee_store.get(key) or self.func(*args, **kwargs)) - return to_return - def __repr__(self): - return "@recursive_iterator(" + _coconut.repr(self.func) + ")" - def __reduce__(self): - return (self.__class__, (self.func,)) - def __get__(self, obj, objtype=None): - return _coconut.functools.partial(self, obj) -class _coconut_FunctionMatchErrorContext(object): - __slots__ = ('exc_class', 'taken') - threadlocal_var = _coconut.threading.local() - def __init__(self, exc_class): - self.exc_class = exc_class - self.taken = False - def __enter__(self): - try: - self.threadlocal_var.contexts.append(self) - except _coconut.AttributeError: - self.threadlocal_var.contexts = [self] - def __exit__(self, type, value, traceback): - self.threadlocal_var.contexts.pop() - @classmethod - def get(cls): - try: - ctx = cls.threadlocal_var.contexts[-1] - except (_coconut.AttributeError, _coconut.IndexError): - return _coconut_MatchError - if not ctx.taken: - ctx.taken = True - return ctx.exc_class - return _coconut_MatchError -_coconut_get_function_match_error = _coconut_FunctionMatchErrorContext.get -class _coconut_base_pattern_func(object): - __slots__ = ("FunctionMatchError", "__doc__", "patterns") - _coconut_is_match = True - def __init__(self, *funcs): - self.FunctionMatchError = _coconut.type(_coconut_str("MatchError"), (_coconut_MatchError,), {}) - self.__doc__ = None - self.patterns = [] - for func in funcs: - self.add(func) - def add(self, func): - self.__doc__ = _coconut.getattr(func, "__doc__", None) or self.__doc__ - if _coconut.isinstance(func, _coconut_base_pattern_func): - self.patterns += func.patterns - else: - self.patterns.append(func) - def __call__(self, *args, **kwargs): - for func in self.patterns[:-1]: - try: - with _coconut_FunctionMatchErrorContext(self.FunctionMatchError): - return func(*args, **kwargs) - except self.FunctionMatchError: - pass - return self.patterns[-1](*args, **kwargs) - def _coconut_tco_func(self, *args, **kwargs): - for func in self.patterns[:-1]: - try: - with _coconut_FunctionMatchErrorContext(self.FunctionMatchError): - return func(*args, **kwargs) - except self.FunctionMatchError: - pass - return _coconut_tail_call(self.patterns[-1], *args, **kwargs) - def __repr__(self): - return "addpattern(" + _coconut.repr(self.patterns[0]) + ")(*" + _coconut.repr(self.patterns[1:]) + ")" - def __reduce__(self): - return (self.__class__, _coconut.tuple(self.patterns)) - def __get__(self, obj, objtype=None): - if obj is None: - return self - return _coconut.functools.partial(self, obj) -def _coconut_mark_as_match(base_func): - base_func._coconut_is_match = True - return base_func -def addpattern(base_func, **kwargs): - """Decorator to add a new case to a pattern-matching function, - where the new case is checked last.""" - allow_any_func = kwargs.pop("allow_any_func", False) - if not allow_any_func and not _coconut.getattr(base_func, "_coconut_is_match", False): - _coconut.warnings.warn("Possible misuse of addpattern with non-pattern-matching function " + _coconut.repr(base_func) + " (pass allow_any_func=True to dismiss)", stacklevel=2) - if kwargs: - raise _coconut.TypeError("addpattern() got unexpected keyword arguments " + _coconut.repr(kwargs)) - return _coconut.functools.partial(_coconut_base_pattern_func, base_func) -_coconut_addpattern = addpattern -class _coconut_partial(object): - __slots__ = ("func", "_argdict", "_arglen", "_stargs", "keywords") - if hasattr(_coconut.functools.partial, "__doc__"): - __doc__ = _coconut.functools.partial.__doc__ - def __init__(self, func, argdict, arglen, *args, **kwargs): - self.func = func - self._argdict = argdict - self._arglen = arglen - self._stargs = args - self.keywords = kwargs - def __reduce__(self): - return (self.__class__, (self.func, self._argdict, self._arglen) + self._stargs, self.keywords) - def __setstate__(self, keywords): - self.keywords = keywords - @property - def args(self): - return _coconut.tuple(self._argdict.get(i) for i in _coconut.range(self._arglen)) + self._stargs - def __call__(self, *args, **kwargs): - callargs = [] - argind = 0 - for i in _coconut.range(self._arglen): - if i in self._argdict: - callargs.append(self._argdict[i]) - elif argind >= _coconut.len(args): - raise _coconut.TypeError("expected at least " + _coconut.str(self._arglen - _coconut.len(self._argdict)) + " argument(s) to " + _coconut.repr(self)) - else: - callargs.append(args[argind]) - argind += 1 - callargs += self._stargs - callargs += args[argind:] - kwargs.update(self.keywords) - return self.func(*callargs, **kwargs) - def __repr__(self): - args = [] - for i in _coconut.range(self._arglen): - if i in self._argdict: - args.append(_coconut.repr(self._argdict[i])) - else: - args.append("?") - for arg in self._stargs: - args.append(_coconut.repr(arg)) - return _coconut.repr(self.func) + "$(" + ", ".join(args) + ")" -def consume(iterable, keep_last=0): - """consume(iterable, keep_last) fully exhausts iterable and return the last keep_last elements.""" - return _coconut.collections.deque(iterable, maxlen=keep_last) -class starmap(_coconut.itertools.starmap): - __slots__ = ("func", "iter") - if hasattr(_coconut.itertools.starmap, "__doc__"): - __doc__ = _coconut.itertools.starmap.__doc__ - def __new__(cls, function, iterable): - new_map = _coconut.itertools.starmap.__new__(cls, function, iterable) - new_map.func = function - new_map.iter = iterable - return new_map - def __getitem__(self, index): - if _coconut.isinstance(index, _coconut.slice): - return self.__class__(self.func, _coconut_igetitem(self.iter, index)) - return self.func(*_coconut_igetitem(self.iter, index)) - def __reversed__(self): - return self.__class__(self.func, *_coconut_reversed(self.iter)) - def __len__(self): - return _coconut.len(self.iter) - def __repr__(self): - return "starmap(%r, %r)" % (self.func, self.iter) - def __reduce__(self): - return (self.__class__, (self.func, self.iter)) - def __reduce_ex__(self, _): - return self.__reduce__() - def __copy__(self): - return self.__class__(self.func, _coconut.copy.copy(self.iter)) - def __fmap__(self, func): - return self.__class__(_coconut_forward_compose(self.func, func), self.iter) -def makedata(data_type, *args): - """Construct an object of the given data_type containing the given arguments.""" - if _coconut.hasattr(data_type, "_make") and _coconut.issubclass(data_type, _coconut.tuple): - return data_type._make(args) - if _coconut.issubclass(data_type, (_coconut.map, _coconut.range, _coconut.abc.Iterator)): - return args - if _coconut.issubclass(data_type, _coconut.str): - return "".join(args) - return data_type(args) -def fmap(func, obj): - """fmap(func, obj) creates a copy of obj with func applied to its contents. - Override by defining obj.__fmap__(func).""" - if _coconut.hasattr(obj, "__fmap__"): - return obj.__fmap__(func) - if obj.__class__.__module__ == "numpy": - from numpy import vectorize - return vectorize(func)(obj) - return _coconut_makedata(obj.__class__, *(_coconut_starmap(func, obj.items()) if _coconut.isinstance(obj, _coconut.abc.Mapping) else _coconut_map(func, obj))) -def memoize(maxsize=None, *args, **kwargs): - """Decorator that memoizes a function, - preventing it from being recomputed if it is called multiple times with the same arguments.""" - return _coconut.functools.lru_cache(maxsize, *args, **kwargs) -_coconut_MatchError, _coconut_count, _coconut_enumerate, _coconut_makedata, _coconut_map, _coconut_reversed, _coconut_starmap, _coconut_tee, _coconut_zip, TYPE_CHECKING, reduce, takewhile, dropwhile = MatchError, count, enumerate, makedata, map, reversed, starmap, tee, zip, False, _coconut.functools.reduce, _coconut.itertools.takewhile, _coconut.itertools.dropwhile diff --git a/yaltik_dsl/__init__.coco b/yaltik_dsl/__init__.coco deleted file mode 100644 index ee86a92..0000000 --- a/yaltik_dsl/__init__.coco +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2019-2020 Fabien Bourgeois -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from . import odoo_dsl diff --git a/yaltik_dsl/__manifest__.py b/yaltik_dsl/__manifest__.py index e388fa8..5566041 100644 --- a/yaltik_dsl/__manifest__.py +++ b/yaltik_dsl/__manifest__.py @@ -19,12 +19,12 @@ 'name': 'Yaltik Odoo DSL base module and fns', 'summary': 'Yaltik Odoo Domain Specific Language base module and functions', 'description': """ Yaltik Odoo Domain Specific Language base module and functions """, - 'version': '10.0.0.3.1', + 'version': '10.0.0.4.0', 'category': 'Yaltik', 'author': 'Fabien Bourgeois', 'license': 'AGPL-3', 'application': False, 'installable': True, 'depends': ['base'], - "external_dependencies": {'python' : ['typing']} + "external_dependencies": {'python' : ['function-pattern-matching']} } diff --git a/yaltik_dsl/odoo_dsl.coco b/yaltik_dsl/odoo_dsl.coco deleted file mode 100644 index 240dd14..0000000 --- a/yaltik_dsl/odoo_dsl.coco +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019-2020 Fabien Bourgeois -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -""" Odoo XML DSL """ - -from typing import Text, Dict -from xml_base import XMLAttrs, XMLDictElement, xmlroot, xmln - -# XML helpers functions and macros - -# Odoo root XML Node -def odoo(children: Dict) -> ET.Element = {'tag': 'odoo', 'attrs': {}, 'children': children} |> xmlroot - -# Special data Node -def data(*args): - """ Allow optional args on data tag """ - if args |> len == 1: - {} |> args.insert$(0) where: args = args |> list - return xmln('data', *args) - -# Aliases -def function(*args) -> XMLDictElement = xmln('function', *args) -def record(*args) -> XMLDictElement = xmln('record', *args) -def form(*args) -> XMLDictElement = xmln('form', *args) -def tree(*args) -> XMLDictElement = xmln('tree', *args) -def search(*args) -> XMLDictElement = xmln('search', *args) - -# Actions -def act_window(*args) -> XMLDictElement = xmln('act_window', *args) -def act_window_model(model: Text, attrs: XMLAttrs) -> XMLDictElement: - """ Build new act_window from model and args """ - xmlid = '%s_view_action' % (('.', '_') |*> model.replace) - name = '%s Action' % ('.' |> model.split |> map$(-> _.capitalize()) |> list |> ' '.join) - attrs_clone = attrs.copy() # Avoid side-effect - {'id': xmlid, 'name': name, 'res_model': model} |> attrs_clone.update - return act_window(attrs_clone) - -def action_server_code(xmlid: Text, name: Text, modelref: Text, code: Text) -> XMLDictElement: - """ Server actions of type code """ - children = [name |> field_name, - ({'name': 'model_id', 'ref': modelref}, []) |*> field, - ({'name': 'state'}, ['code']) |*> field, - ({'name': 'code'}, [code]) |*> field] - return ({'id': xmlid, 'model': 'ir.actions.server'}, children) |*> record - -def client_action_multi(xmlid: Text, name: Text, model: Text, action: Text) -> XMLDictElement: - """ Client action multi (ir.values), with own xmlid, name, targeted model - and action """ - action = action |> "'ir.actions.server,%d'%{}".format - children = [name |> field_name, - {'name': 'key2', 'eval': "'client_action_multi'"} |> field, - {'name': 'model', 'eval': "'%s'" % model} |> field, - {'name': 'value', 'eval': action} |> field] - return ({'id': xmlid, 'model': 'ir.values'}, children) |*> record - -# Menus -def menuitem(*args) -> XMLDictElement = xmln('menuitem', *args) -def menuitem_model(model: Text, attrs: XMLAttrs) -> XMLDictElement: - """ Build new menuitem from model and attrs """ - model_und = ('.', '_') |*> model.replace - xmlid = '%s_menu' % model_und - actionid = '%s_view_action' % model_und - attrs_clone = attrs.copy() # Avoid side-effect - {'id': xmlid, 'action': actionid} |> attrs_clone.update - return menuitem(attrs_clone) - -# Form aliases -def group(*args) -> XMLDictElement = xmln('group', *args) -def header(*args) -> XMLDictElement = xmln('header', *args) -def footer(*args) -> XMLDictElement = xmln('footer', *args) -def sheet(*args) -> XMLDictElement = xmln('sheet', *args) -def button(*args) -> XMLDictElement = xmln('button', *args) -def p(*args) -> XMLDictElement = xmln('p', *args) -def xpath(*args) -> XMLDictElement = xmln('xpath', *args) -def attribute(name: Text, value: Text) -> XMLDictElement: - return ('attribute', {'name': name}, [value]) |*> xmln - -# Fields -def field(*args) -> XMLDictElement = xmln('field', *args) -def field_name(name: Text) -> XMLDictElement = ({'name': 'name'}, [name]) |*> field -def field_model(model: Text) -> XMLDictElement = ({'name': 'model'}, [model]) |*> field -def field_inherit(xmlid: Text) -> XMLDictElement: - return ({'name': 'inherit_id', 'ref': xmlid}, []) |*> field -def field_arch(*args) -> XMLDictElement = {'name': 'arch', 'type': 'xml'} |> field$ <*| args - -# Search -def filter(*args) -> XMLDictElement = xmln('filter', *args) - -# Views -def view(xmlid: Text, children: List) -> XMLDictElement: - return ({'id': xmlid, 'model': 'ir.ui.view'}, children) |*> record - -def view_def(xmlid: Text, name: Text, model: Text, arch: List) -> XMLDictElement: - return (xmlid, [field_name(name), field_model(model), field_arch(arch)]) |*> view - -def view_new(view_type: Text, model: Text, arch: List) -> XMLDictElement: - """ View : new view definition, based on type (form, tree, ...) and model ID """ - model_und = ('.', '_') |*> model.replace - model_cap = ('.' |> model.split |> map$(-> _.capitalize()) |> list |> ' '.join) - xmlid = "%s_view_%s" % (model_und, view_type) - name = (model_cap, view_type.capitalize()) |> ' '.join - return (xmlid, name, model, arch) |*> view_def - -def view_inherit(filename: Text, model: Text, inherit: Text, arch: List) -> XMLDictElement: - """ Inherited View simplification with name of the record, xmlid for model - and inherited view """ - module = '.' |> filename.split |> .[2] - inherited = '.' |> inherit.split |> .[1] - xmlid = '%s_inherit_%s' % (inherited, module) - model_cap = ('.' |> model.split |> map$(-> _.capitalize()) |> list |> ' '.join) - name = '%s Adaptations' % model_cap - return view(xmlid, [name |> field_name, model |> field_model, - inherit |> field_inherit, arch |> field_arch]) diff --git a/yaltik_dsl/test_odoo.coco b/yaltik_dsl/test_odoo.coco deleted file mode 100644 index 10d96a6..0000000 --- a/yaltik_dsl/test_odoo.coco +++ /dev/null @@ -1,235 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Fabien Bourgeois -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -""" Odoo Helpers tests """ - -import unittest -import xml.etree.ElementTree as ET -from xml_base import XMLDictElement -import odoo_dsl as od - -class TestOdooBase(unittest.TestCase): - """ Odoo Helpers tests """ - - def test_odoo(self): - element = od.odoo([]) - element `self.assertIsInstance` ET.Element - element.tag `self.assertEquals` 'odoo' - - def test_data(self): - element = od.data([]) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'data' - element.attrs `self.assertEquals` {} - - element = od.data({"one": "attribute"}, []) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'data' - element.attrs `self.assertEquals` {"one": "attribute"} - - def test_aliases(self): - """ Test simple aliases to xmln """ - element = od.record({"one": "attribute"}, 'A child') - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs `self.assertEquals` {"one": "attribute"} - element.children `self.assertEquals` ['A child'] - - element = od.tree() - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'tree' - - def test_act_window_model(self): - element = ('sample.model', {'view_type': 'form'}) |*> od.act_window_model - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'act_window' - element.attrs['view_type'] `self.assertEquals` 'form' - element.attrs['id'] `self.assertEquals` 'sample_model_view_action' - element.attrs['res_model'] `self.assertEquals` 'sample.model' - element.attrs['name'] `self.assertEquals` 'Sample Model Action' - - def test_menunitem_model(self): - element = ('sample.model', {'groups': 'base.user_employee'}) |*> od.menuitem_model - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'menuitem' - element.attrs['groups'] `self.assertEquals` 'base.user_employee' - element.attrs['id'] `self.assertEquals` 'sample_model_menu' - element.attrs['action'] `self.assertEquals` 'sample_model_view_action' - - def test_attribute(self): - element = od.attribute('invisible', "1") - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'attribute' - element.attrs['name'] `self.assertEquals` 'invisible' - element.children `self.assertEquals` ["1"] - - def test_fields(self): - element = od.field({"one": "attribute"}, 'A child') - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'field' - - element = 'A name' |> od.field_name - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'field' - element.attrs['name'] `self.assertEquals` 'name' - element.children `self.assertEquals` ['A name'] - - element = 'sample.model' |> od.field_model - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'field' - element.attrs['name'] `self.assertEquals` 'model' - element.children `self.assertEquals` ['sample.model'] - - element = 'module.xml_view' |> od.field_inherit - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'field' - element.attrs['name'] `self.assertEquals` 'inherit_id' - element.attrs['ref'] `self.assertEquals` 'module.xml_view' - element.children |> self.assertFalse - - element = od.field_arch() - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'field' - element.attrs['name'] `self.assertEquals` 'arch' - element.attrs['type'] `self.assertEquals` 'xml' - element.children |> self.assertFalse - - def test_view(self): - element = od.view('view_xmlid', []) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'view_xmlid' - element.attrs['model'] `self.assertEquals` 'ir.ui.view' - element.children |> self.assertFalse - - def test_view_def(self): - element = od.view_def('view_xmlid', 'View', 'sample.model', []) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'view_xmlid' - element.attrs['model'] `self.assertEquals` 'ir.ui.view' - (element.children |> len) `self.assertEquals` 3 - element.children[0] `self.assertIsInstance` XMLDictElement - element.children[0].tag `self.assertEquals` 'field' - element.children[0].attrs['name'] `self.assertEquals` 'name' - element.children[0].children `self.assertEquals` ['View'] - element.children[1] `self.assertIsInstance` XMLDictElement - element.children[1].tag `self.assertEquals` 'field' - element.children[1].attrs['name'] `self.assertEquals` 'model' - element.children[1].children `self.assertEquals` ['sample.model'] - element.children[2] `self.assertIsInstance` XMLDictElement - element.children[2].tag `self.assertEquals` 'field' - element.children[2].attrs['name'] `self.assertEquals` 'arch' - element.children[2].attrs['type'] `self.assertEquals` 'xml' - element.children[2].children |> self.assertFalse - - def test_view_new(self): - element = od.view_new('tree', 'sample.model', []) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'sample_model_view_tree' - element.attrs['model'] `self.assertEquals` 'ir.ui.view' - (element.children |> len) `self.assertEquals` 3 - element.children[0] `self.assertIsInstance` XMLDictElement - element.children[0].tag `self.assertEquals` 'field' - element.children[0].attrs['name'] `self.assertEquals` 'name' - element.children[0].children `self.assertEquals` ['Sample Model Tree'] - element.children[1] `self.assertIsInstance` XMLDictElement - element.children[1].tag `self.assertEquals` 'field' - element.children[1].attrs['name'] `self.assertEquals` 'model' - element.children[1].children `self.assertEquals` ['sample.model'] - element.children[2] `self.assertIsInstance` XMLDictElement - element.children[2].tag `self.assertEquals` 'field' - element.children[2].attrs['name'] `self.assertEquals` 'arch' - element.children[2].attrs['type'] `self.assertEquals` 'xml' - element.children[2].children |> self.assertFalse - - def test_view_inherit(self): - element = od.view_inherit('odoo.addons.module', 'sample.model', 'parent.view', []) - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'view_inherit_module' - element.attrs['model'] `self.assertEquals` 'ir.ui.view' - (element.children |> len) `self.assertEquals` 4 - element.children[0] `self.assertIsInstance` XMLDictElement - element.children[0].tag `self.assertEquals` 'field' - element.children[0].attrs['name'] `self.assertEquals` 'name' - element.children[0].children `self.assertEquals` ['Sample Model Adaptations'] - element.children[1] `self.assertIsInstance` XMLDictElement - element.children[1].tag `self.assertEquals` 'field' - element.children[1].attrs['name'] `self.assertEquals` 'model' - element.children[1].children `self.assertEquals` ['sample.model'] - element.children[2] `self.assertIsInstance` XMLDictElement - element.children[2].tag `self.assertEquals` 'field' - element.children[2].attrs['name'] `self.assertEquals` 'inherit_id' - element.children[2].children |> self.assertFalse - element.children[3] `self.assertIsInstance` XMLDictElement - element.children[3].tag `self.assertEquals` 'field' - element.children[3].attrs['name'] `self.assertEquals` 'arch' - element.children[3].attrs['type'] `self.assertEquals` 'xml' - element.children[3].children |> self.assertFalse - - def test_action_server_code(self): - element = od.action_server_code('sample.xmlid', 'Code', 'sample.model', - '''record.do_something()''') - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'sample.xmlid' - element.attrs['model'] `self.assertEquals` 'ir.actions.server' - (element.children |> len) `self.assertEquals` 4 - element.children[0] `self.assertIsInstance` XMLDictElement - element.children[0].tag `self.assertEquals` 'field' - element.children[0].attrs['name'] `self.assertEquals` 'name' - element.children[0].children `self.assertEquals` ['Code'] - element.children[1] `self.assertIsInstance` XMLDictElement - element.children[1].tag `self.assertEquals` 'field' - element.children[1].attrs['name'] `self.assertEquals` 'model_id' - element.children[1].attrs['ref'] `self.assertEquals` 'sample.model' - element.children[1].children |> self.assertFalse - element.children[2].tag `self.assertEquals` 'field' - element.children[2].attrs['name'] `self.assertEquals` 'state' - element.children[2].children `self.assertEquals` ['code'] - element.children[3].tag `self.assertEquals` 'field' - element.children[3].attrs['name'] `self.assertEquals` 'code' - element.children[3].children `self.assertEquals` ['record.do_something()'] - - def test_client_action_multi(self): - element = od.client_action_multi('sample.xmlid', 'Multi', 'sample.model', 'sample.action') - element `self.assertIsInstance` XMLDictElement - element.tag `self.assertEquals` 'record' - element.attrs['id'] `self.assertEquals` 'sample.xmlid' - element.attrs['model'] `self.assertEquals` 'ir.values' - (element.children |> len) `self.assertEquals` 4 - element.children[0] `self.assertIsInstance` XMLDictElement - element.children[0].tag `self.assertEquals` 'field' - element.children[0].attrs['name'] `self.assertEquals` 'name' - element.children[0].children `self.assertEquals` ['Multi'] - element.children[1] `self.assertIsInstance` XMLDictElement - element.children[1].tag `self.assertEquals` 'field' - element.children[1].attrs['name'] `self.assertEquals` 'key2' - element.children[1].attrs['eval'] `self.assertEquals` "'client_action_multi'" - element.children[1].children |> self.assertFalse - element.children[2].tag `self.assertEquals` 'field' - element.children[2].attrs['name'] `self.assertEquals` 'model' - element.children[2].attrs['eval'] `self.assertEquals` "'sample.model'" - element.children[3].tag `self.assertEquals` 'field' - element.children[3].attrs['name'] `self.assertEquals` 'value' - element.children[3].attrs['eval'] `self.assertEquals` "'ir.actions.server,%d'%sample.action" - - -if __name__ == '__main__': - unittest.main() diff --git a/yaltik_dsl/test_xml_base.coco b/yaltik_dsl/test_xml_base.coco deleted file mode 100644 index 648edcb..0000000 --- a/yaltik_dsl/test_xml_base.coco +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Fabien Bourgeois -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -""" XML Helpers tests """ - -import unittest -import xml.etree.ElementTree as ET -from os import unlink -from xml_base import xmln, xmlroot, xmlchild, xml_write - - -class TestXMLBase(unittest.TestCase): - """ XML Helpers tests """ - - def test_xmln(self): - # Tags - (xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []}) |*> self.assertEquals - (xmln <| 'a tag' |> getattr$(?, 'tag'), 'a tag') |*> self.assertEquals - - # Attrs - (xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'}) |*> self.assertEquals - (xmln <**| {'attrs': {'a good': 'one'}} |> getattr$(?, 'attrs'), {'a good': 'one'}) |*> self.assertEquals - - # Childrens - attrs ={'children': [1, 2, 3]} - (xmln <**| attrs |> getattr$(?, 'children') == [1, 2, 3]) |> self.assertTrue - - attrs = {'children': 'Some text'} - (xmln <**| attrs |> getattr$(?, 'children') == ['Some text']) |> self.assertTrue - - with self.assertRaisesRegexp(TypeError, 'Invalid arguments'): - xmln <**| {'children': False} - - # Ensure that only children after tags is managed - element = xmln <*| ('tag', {'something': 'inside'}) - element.attrs `self.assertIsInstance` dict - element.children `self.assertIsInstance` list - - element = xmln <*| ('tag', ['something', 'inside']) - element.attrs `self.assertIsInstance` dict - element.children `self.assertIsInstance` list - - - def test_xmlchild(self): - parent = {'tag': 'root', 'attrs': {}, 'children': []} |> xmlroot - xmlc_par = xmlchild$ <| parent - - # Bad arguments - with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'): - xmlc_par <| False - # Need XMLDictElement, not dict - with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'): - xmlc_par <| [{'tag': 't', 'attrs': {'a': 'b'}, 'children': []}] - - xmlc_par <| ['some text'] - parent.text `self.assertEquals` 'some text' - - xmlc_par <| [xmln <**| {'tag': 't', 'attrs': {'a': 'b'}, 'children': []}] - child = parent.iter <| 't' |> next - child.tag `self.assertEquals` 't' - child.attrib `self.assertEquals` {'a': 'b'} - (child |> list) `self.assertEquals` [] - - xmlc_par <| [xmln <**| {'tag': 't2', 'attrs': {1: 2}, 'children': []}] - child = parent.iter <| 't2' |> next - child.attrib `self.assertEquals` {'1': '2'} - - xmlc_par <| [xmln <**| {'tag': 'tchildren', 'attrs': {}, - 'children': [xmln <**| {'tag': 'subchild', 'attrs': {}, 'children': []}]}] - child = parent.iter <| 'tchildren' |> next - subchildren = (child |> list) - (subchildren |> len) `self.assertEquals` 1 - subchildren[0].tag `self.assertEquals` 'subchild' - - - def test_xmlroot(self): - root = {'tag': 'root', 'attrs': {}, 'children': []} |> xmlroot - isinstance <*| (root, ET.Element) |> self.assertTrue - - with self.assertRaisesRegexp(TypeError, 'has no attribute'): - False |> xmlroot - with self.assertRaisesRegexp(KeyError, 'tag'): - {} |> xmlroot - with self.assertRaisesRegexp(KeyError, 'attrs'): - {'tag': 'root'} |> xmlroot - - - def test_xml_write(self): - children = [('child1', {'attr': 'value'}, []) |*> xmln, - ('child2', {}, "Some text") |*> xmln] - tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children}) - xmlw = xml_write$(?, tree) - - ('/badpath' |> xmlw) `self.assertEquals` None - ('/bad.ext' |> xmlw) `self.assertEquals` None - - xmlw(__file__) - filepath = __file__.replace('.py', '_views.xml') - with open(filepath, 'r') as output_file: - output_xml = output_file.read() - '' `self.assertIn` output_xml - '' `self.assertIn` output_xml - 'Some text' `self.assertIn` output_xml - unlink(filepath) - -if __name__ == '__main__': - unittest.main() diff --git a/yaltik_dsl/xml_base.coco b/yaltik_dsl/xml_base.coco deleted file mode 100644 index 801309b..0000000 --- a/yaltik_dsl/xml_base.coco +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019-2020 Fabien Bourgeois -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -""" XML helpers and macros """ - -from os import path -import xml.etree.ElementTree as ET -from xml.dom import minidom -from typing import Dict, List, Union, Text, Any - - -# TODO: fix MyPy / typing - -data XMLDictElement(tag: Text, attrs: XMLAttrs, children: List[Any]) - -XMLAttrs = Dict[Text, Text] -XMLChild = Union[XMLDictElement, Text, List] - - -def xmlroot(tree: Dict[str, Any]) -> ET.Element: - """ Special process for root XML Node """ - rootel = (tree['tag'], tree['attrs']) |*> ET.Element - if 'children' in tree: - (rootel, tree['children']) |*> xmlchild - return rootel - -def xmlchild(parent: ET.Element, children: XMLDictElement) -> None: - """ Handling of children (ie non root) XML Nodes with/o text and - subchildren (recursive) """ - case children: - match _ is Text: - parent.text = children - match _ is XMLDictElement: - attrs = {unicode(k): unicode(v) for [k, v] in children.attrs.items()} - new_parent = (parent, children.tag, attrs) |*> ET.SubElement - subchildren = children.children - if subchildren: - (new_parent, subchildren) |*> xmlchild - match _ is List: - ((xmlchild$ <| parent), children) |*> map |> consume - else: - raise TypeError('Invalid arguments for xmlchild') - -def xmln(tag: Text = '', - attrs: XMLAttrs = {}, - children: Union[Text, List] = []) -> XMLDictElement: - """ XMLDictElement building from dict object, with defaults """ - case attrs: - match _ is list: - children = attrs - attrs = {} - xmldictel = XMLDictElement$ <*| (tag, attrs) - case children: - match c is Text: - return [c] |> xmldictel - match c is list: - return c |> xmldictel - else: - raise TypeError('Invalid arguments for xmln') - - -def xml_write(filepath, tree): - """ Write XML file according to filename and given tree """ - if '.py' |> filepath.endswith: # if .pyc, no need to generate XML - output_xml = tree |> ET.tostring |> minidom.parseString |> .toprettyxml(indent=' ') - output_path = filepath |> path.abspath |> path.dirname - fpath = [output_path, filepath |> path.basename] |> '/'.join |> .replace('.py', '_views.xml') - with open(fpath, 'w') as output_file: - output_file.write(output_xml)