From 1202e3c6d4b812fdcf99ba1cd29d92edbc8d4650 Mon Sep 17 00:00:00 2001 From: Fabien BOURGEOIS Date: Thu, 30 Apr 2020 03:01:37 +0200 Subject: [PATCH] [REF][WIP]Yaltik DSL : attempt to migrate to Coconut --- yaltik_dsl/__coconut__.py | 733 ++++++++++++++++++++++++++++++++++ yaltik_dsl/__init__.coco | 18 + yaltik_dsl/__init__.py | 21 + yaltik_dsl/odoo.coco | 117 ++++++ yaltik_dsl/odoo.py | 46 ++- yaltik_dsl/test_xml_base.coco | 107 +++++ yaltik_dsl/test_xml_base.py | 132 ++++++ yaltik_dsl/xml_base.coco | 78 ++++ yaltik_dsl/xml_base.py | 132 +++++- 9 files changed, 1348 insertions(+), 36 deletions(-) create mode 100644 yaltik_dsl/__coconut__.py create mode 100644 yaltik_dsl/__init__.coco create mode 100644 yaltik_dsl/odoo.coco create mode 100644 yaltik_dsl/test_xml_base.coco create mode 100644 yaltik_dsl/test_xml_base.py create mode 100644 yaltik_dsl/xml_base.coco diff --git a/yaltik_dsl/__coconut__.py b/yaltik_dsl/__coconut__.py new file mode 100644 index 0000000..4e24dbf --- /dev/null +++ b/yaltik_dsl/__coconut__.py @@ -0,0 +1,733 @@ +#!/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 +def prepattern(base_func, **kwargs): + """DEPRECATED: Use addpattern instead.""" + def pattern_prepender(func): + return addpattern(func, **kwargs)(base_func) + return pattern_prepender +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 datamaker(data_type): + """DEPRECATED: Use makedata instead.""" + return _coconut.functools.partial(makedata, data_type) +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 new file mode 100644 index 0000000..99581b9 --- /dev/null +++ b/yaltik_dsl/__init__.coco @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019 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 diff --git a/yaltik_dsl/__init__.py b/yaltik_dsl/__init__.py index 99581b9..b577fa5 100644 --- a/yaltik_dsl/__init__.py +++ b/yaltik_dsl/__init__.py @@ -1,3 +1,24 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# __coconut_hash__ = 0xe22b9d74 + +# Compiled with Coconut version 1.4.3 [Ernest Scribbler] + +# Coconut Header: ------------------------------------------------------------- + +from __future__ import print_function, absolute_import, unicode_literals, division +import sys as _coconut_sys, os.path as _coconut_os_path +_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__)) +_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__") +if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path: + del _coconut_sys.modules[b"__coconut__"] +_coconut_sys.path.insert(0, _coconut_file_path) +from __coconut__ import * +from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match + + +# Compiled Coconut: ----------------------------------------------------------- + # -*- coding: utf-8 -*- # Copyright 2019 Fabien Bourgeois diff --git a/yaltik_dsl/odoo.coco b/yaltik_dsl/odoo.coco new file mode 100644 index 0000000..ac7fe14 --- /dev/null +++ b/yaltik_dsl/odoo.coco @@ -0,0 +1,117 @@ +# -*- 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 .xml_base import xmlroot, xmln + +# XML helpers functions and macros + +odoo = lambda *args: xmlroot(xmln('odoo', {}, *args)) + +def data(*args): + """ Allow optional args on data tag """ + if len(args) == 1: + return xmln('data', {}, *args) + return xmln('data', *args) + +function = lambda *args: xmln('function', *args) +record = lambda *args: xmln('record', *args) +form = lambda *args: xmln('form', *args) +tree = lambda *args: xmln('tree', *args) +search = lambda *args: xmln('search', *args) + +act_window = lambda *args: xmln('act_window', *args) + +def act_window_model(model, attrs): + """ Build new act_window from model and args """ + model_und = model.replace('.', '_') + model_cap = ' '.join([w.capitalize() for w in model.split('.')]) + xmlid = '{:{}}'.format(model_und, '') + '_view_action' + name = '{:{}}'.format(model_cap, '') + ' Action' + attrs.update({'id': xmlid, 'name': name, 'res_model': model}) + return act_window(attrs) + +menuitem = lambda *args: xmln('menuitem', *args) + +def menuitem_model(model, attrs): + """ Build new menuitem from model and attrs """ + model_und = model.replace('.', '_') + actionid = '{:{}}'.format(model_und, '') + '_view_action' + xmlid = '{:{}}'.format(model_und, '') + '_men' + attrs.update({'id': xmlid, 'action': actionid}) + return menuitem(attrs) + + +group = lambda *args: xmln('group', *args) +header = lambda *args: xmln('header', *args) +footer = lambda *args: xmln('footer', *args) +sheet = lambda *args: xmln('sheet', *args) +button = lambda *args: xmln('button', *args) +p = lambda *args: xmln('p', *args) +xpath = lambda *args: xmln('xpath', *args) +attribute = lambda name, value: xmln('attribute', {'name': name}, [value]) + +field = lambda *args: xmln('field', *args) +field_name = lambda name: field({'name': 'name'}, [name]) +field_model = lambda model: field({'name': 'model'}, [model]) +field_inherit = lambda xmlid: field({'name': 'inherit_id', 'ref': xmlid}, []) +field_arch = lambda *args: field({'name': 'arch', 'type': 'xml'}, *args) + +filter = lambda *args: xmln('filter', *args) + +view = lambda xmlid, children: record({'id': xmlid, 'model': 'ir.ui.view'}, children) + +def view_def(xmlid, name, model, arch): + """ View and first fields simplification with record xmlid, name, targeted model """ + return view(xmlid, [field_name(name), field_model(model), field_arch(arch)]) + +def view_new(view_type, model, arch): + """ View : new view definition, based on type (form, tree, ...) and model ID """ + model_und = model.replace('.', '_') + model_cap = ' '.join([w.capitalize() for w in model.split('.')]) + xmlid = '{:{}}'.format(model_und, '') + '_view_' + '{:{}}'.format(view_type, '') + name = '{:{}}'.format(model_cap, '') + ' ' + '{:{}}'.format(view_type.capitalize(), '') + return view_def(xmlid=xmlid, name=name, model=model, arch=arch) + +def view_inherit(filename, model, inherit, arch): + """ Inherited View simplification with name of the record, xmlid for model + and inherited view """ + module = filename.split('.')[2] + inherited = inherit.split('.')[1] + xmlid = '{:{}}'.format(inherited, '') + '_inherit_' + '{:{}}'.format( + module, '') + model_cap = ' '.join([w.capitalize() for w in model.split('.')]) + name = '{:{}}'.format(model_cap, '') + ' Adaptations' + return view(xmlid, [field_name(name), field_model(model), + field_inherit(inherit), field_arch(arch)]) + +def actions_server_code(xmlid, name, modelref, code): + """ Server actions of type code """ + return record({'id': xmlid, 'model': 'ir.actions.server'}, [ + field_name(name), field({'name': 'model_id', 'ref': modelref}, [ + ]), field({'name': 'state'}, ['code']), field({'name': 'code'}, [code])]) + +def client_action_multi(xmlid, name, model, action): + """ Client action multi (ir.values), with own xmlid, name, targeted model + and action """ + action = u"'ir.actions.server,%d'%" + '{:{}}'.format(action, '') + return record({'id': xmlid, 'model': 'ir.values'}, + [field_name(name), + field({'name': 'key2', 'eval': u"'client_action_multi'"}, []), + field({'name': 'model', 'eval': u"'" + model + u"'"}, []), + field({'name': 'value', 'eval': action})]) diff --git a/yaltik_dsl/odoo.py b/yaltik_dsl/odoo.py index ac7fe14..02a8648 100644 --- a/yaltik_dsl/odoo.py +++ b/yaltik_dsl/odoo.py @@ -1,3 +1,26 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# __coconut_hash__ = 0x76f908d8 + +# Compiled with Coconut version 1.4.3 [Ernest Scribbler] + +""" Odoo XML DSL """ + +# Coconut Header: ------------------------------------------------------------- + +from __future__ import print_function, absolute_import, unicode_literals, division +import sys as _coconut_sys, os.path as _coconut_os_path +_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__)) +_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__") +if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path: + del _coconut_sys.modules[b"__coconut__"] +_coconut_sys.path.insert(0, _coconut_file_path) +from __coconut__ import * +from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match + + +# Compiled Coconut: ----------------------------------------------------------- + # -*- coding: utf-8 -*- # # Copyright 2019-2020 Fabien Bourgeois @@ -15,9 +38,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Odoo XML DSL """ -from .xml_base import xmlroot, xmln + +from .xml_base import xmlroot +from .xml_base import xmln # XML helpers functions and macros @@ -93,25 +117,17 @@ def view_inherit(filename, model, inherit, arch): and inherited view """ module = filename.split('.')[2] inherited = inherit.split('.')[1] - xmlid = '{:{}}'.format(inherited, '') + '_inherit_' + '{:{}}'.format( - module, '') + xmlid = '{:{}}'.format(inherited, '') + '_inherit_' + '{:{}}'.format(module, '') model_cap = ' '.join([w.capitalize() for w in model.split('.')]) name = '{:{}}'.format(model_cap, '') + ' Adaptations' - return view(xmlid, [field_name(name), field_model(model), - field_inherit(inherit), field_arch(arch)]) + return view(xmlid, [field_name(name), field_model(model), field_inherit(inherit), field_arch(arch)]) def actions_server_code(xmlid, name, modelref, code): """ Server actions of type code """ - return record({'id': xmlid, 'model': 'ir.actions.server'}, [ - field_name(name), field({'name': 'model_id', 'ref': modelref}, [ - ]), field({'name': 'state'}, ['code']), field({'name': 'code'}, [code])]) + return record({'id': xmlid, 'model': 'ir.actions.server'}, [field_name(name), field({'name': 'model_id', 'ref': modelref}, []), field({'name': 'state'}, ['code']), field({'name': 'code'}, [code])]) def client_action_multi(xmlid, name, model, action): """ Client action multi (ir.values), with own xmlid, name, targeted model and action """ - action = u"'ir.actions.server,%d'%" + '{:{}}'.format(action, '') - return record({'id': xmlid, 'model': 'ir.values'}, - [field_name(name), - field({'name': 'key2', 'eval': u"'client_action_multi'"}, []), - field({'name': 'model', 'eval': u"'" + model + u"'"}, []), - field({'name': 'value', 'eval': action})]) + action = "'ir.actions.server,%d'%" + '{:{}}'.format(action, '') + return record({'id': xmlid, 'model': 'ir.values'}, [field_name(name), field({'name': 'key2', 'eval': "'client_action_multi'"}, []), field({'name': 'model', 'eval': "'" + model + "'"}, []), field({'name': 'value', 'eval': action})]) diff --git a/yaltik_dsl/test_xml_base.coco b/yaltik_dsl/test_xml_base.coco new file mode 100644 index 0000000..d579fb5 --- /dev/null +++ b/yaltik_dsl/test_xml_base.coco @@ -0,0 +1,107 @@ +# -*- 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 tests """ + +import unittest +import xml.etree.ElementTree as ET +from xml_base import xmln, xmlroot, xmlchild + + +class TestXMLBase(unittest.TestCase): + """ XML Helpers tests """ + + def test_xmln(self): + # Tags + (xmln(), {'tag': '', 'attrs': {}, 'children': []}) |*> self.assertEquals + (xmln <| 'a tag' |> .get <| 'tag', 'a tag') |*> self.assertEquals + + # Attrs + (xmln(attrs={'a good': 'one'}).get('attrs'), {'a good': 'one'}) |*> self.assertEquals + (xmln <**| {'attrs': {'a good': 'one'}} |> .get <| 'attrs', {'a good': 'one'}) |*> self.assertEquals + + # Childrens + attrs ={'children': [1, 2, 3]} + (xmln <**| attrs |> .get <| 'children' == [1, 2, 3]) |> self.assertTrue + + attrs = {'children': 'Some text'} + (xmln <**| attrs |> .get <| 'children' == ['Some text']) |> self.assertTrue + + try: + xmln <**| {'children': False} + except TypeError as err: + ('Invalid arguments', err.message) |*> self.assertIn + + + def test_xmlchild(self): + parent = {'tag': 'root', 'attrs': {}, 'children': []} |> xmlroot + xmlc_par = xmlchild$ <| parent + + (xmlc_par <| []) `self.assertEquals` None + try: + xmlc_par <| False + except TypeError as err: + 'is not iterable' `self.assertIn` err.message + + xmlc_par <| ['some text'] + parent.text `self.assertEquals` 'some text' + + xmlc_par <| [{'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 <| [{'tag': 't2', 'attrs': {1: 2}, 'children': []}] + child = parent.iter <| 't2' |> next + child.attrib `self.assertEquals` {'1': '2'} + + xmlc_par <| [{'tag': 'tchildren', 'attrs': {}, + 'children': [{'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 + + try: + False |> xmlroot + except TypeError as err: + ('has no attribute', err.message) |*> self.assertIn + try: + {} |> xmlroot + except KeyError as err: + ('tag', err.message) |*> self.assertIn + try: + {'tag': 'root'} |> xmlroot + except KeyError as err: + ('attrs', err.message) |*> self.assertIn + try: + {'tag': 'root', 'attrs': {}} |> xmlroot + except KeyError as err: + ('children', err.message) |*> self.assertIn + + + def test_xml_write(self): pass + + +if __name__ == '__main__': + unittest.main() diff --git a/yaltik_dsl/test_xml_base.py b/yaltik_dsl/test_xml_base.py new file mode 100644 index 0000000..32d5746 --- /dev/null +++ b/yaltik_dsl/test_xml_base.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# __coconut_hash__ = 0xf82711d8 + +# Compiled with Coconut version 1.4.3 [Ernest Scribbler] + +""" XML Helpers tests """ + +# Coconut Header: ------------------------------------------------------------- + +from __future__ import print_function, absolute_import, unicode_literals, division +import sys as _coconut_sys, os.path as _coconut_os_path +_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__)) +_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__") +if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path: + del _coconut_sys.modules[b"__coconut__"] +_coconut_sys.path.insert(0, _coconut_file_path) +from __coconut__ import * +from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match + + +# Compiled Coconut: ----------------------------------------------------------- + +# -*- 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 . + + + +import unittest +import xml.etree.ElementTree as ET +from xml_base import xmln +from xml_base import xmlroot +from xml_base import xmlchild + + +class TestXMLBase(unittest.TestCase): + """ XML Helpers tests """ + + def test_xmln(self): +# Tags + (self.assertEquals)(*(xmln(), {'tag': '', 'attrs': {}, 'children': []})) + (self.assertEquals)(*((((xmln)('a tag')).get)('tag'), 'a tag')) + +# Attrs + (self.assertEquals)(*(xmln(attrs={'a good': 'one'}).get('attrs'), {'a good': 'one'})) + (self.assertEquals)(*((((xmln)(**{'attrs': {'a good': 'one'}})).get)('attrs'), {'a good': 'one'})) + +# Childrens + attrs = {'children': [1, 2, 3]} + (self.assertTrue)(((((xmln)(**attrs)).get)('children') == [1, 2, 3])) + + attrs = {'children': 'Some text'} + (self.assertTrue)(((((xmln)(**attrs)).get)('children') == ['Some text'])) + + try: + (xmln)(**{'children': False}) + except TypeError as err: + (self.assertIn)(*('Invalid arguments', err.message)) + + + def test_xmlchild(self): + parent = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []}) + xmlc_par = (_coconut.functools.partial(_coconut.functools.partial, xmlchild))(parent) + + (self.assertEquals)(((xmlc_par)([])), None) + try: + (xmlc_par)(False) + except TypeError as err: + (self.assertIn)('is not iterable', err.message) + + (xmlc_par)(['some text']) + (self.assertEquals)(parent.text, 'some text') + + (xmlc_par)([{'tag': 't', 'attrs': {'a': 'b'}, 'children': []}]) + child = (next)((parent.iter)('t')) + (self.assertEquals)(child.tag, 't') + (self.assertEquals)(child.attrib, {'a': 'b'}) + (self.assertEquals)(((list)(child)), []) + + (xmlc_par)([{'tag': 't2', 'attrs': {1: 2}, 'children': []}]) + child = (next)((parent.iter)('t2')) + (self.assertEquals)(child.attrib, {'1': '2'}) + + (xmlc_par)([{'tag': 'tchildren', 'attrs': {}, 'children': [{'tag': 'subchild', 'attrs': {}, 'children': []}]}]) + child = (next)((parent.iter)('tchildren')) + subchildren = ((list)(child)) + (self.assertEquals)(((len)(subchildren)), 1) + (self.assertEquals)(subchildren[0].tag, 'subchild') + + + def test_xmlroot(self): + root = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []}) + (self.assertTrue)((isinstance)(*(root, ET.Element))) + + try: + (xmlroot)(False) + except TypeError as err: + (self.assertIn)(*('has no attribute', err.message)) + try: + (xmlroot)({}) + except KeyError as err: + (self.assertIn)(*('tag', err.message)) + try: + (xmlroot)({'tag': 'root'}) + except KeyError as err: + (self.assertIn)(*('attrs', err.message)) + try: + (xmlroot)({'tag': 'root', 'attrs': {}}) + except KeyError as err: + (self.assertIn)(*('children', err.message)) + + + def test_xml_write(self): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/yaltik_dsl/xml_base.coco b/yaltik_dsl/xml_base.coco new file mode 100644 index 0000000..12c8dad --- /dev/null +++ b/yaltik_dsl/xml_base.coco @@ -0,0 +1,78 @@ +# -*- 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, Any + + +# TODO: ADT for XMLchild children**s** +# REF xml_write + +data XMLChildren(clist: List[Union[XMLDict, XMLText]]) + +data XMLText(text: Union[str, unicode]) +data XMLDict(tag: str, attrs: Dict[str, str], children: XMLChildren) + +data XMLTag(tag: str) +data XMLAttrs(attrs: Dict[str, str]) + +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: Any) -> None: + """ Handling of children (ie non root) XML Nodes with/o text and + subchildren (recursive) """ + match _ is (str, unicode) in children: + parent.text = children + return + match _ is dict in children: + 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 in children: + ((xmlchild$ <| parent), children) |*> map |> consume + + +def xmln(tag: str = '', + attrs: Dict[str, str] = {}, + children: Union[str, List] = []) -> Dict: + """ XMLNode with default children, not attributes """ + match c is str in children: + return {'tag': tag, 'attrs': attrs, 'children': [c]} + else: match c is list in children: + return {'tag': tag, 'attrs': attrs, 'children': c} + else: + raise TypeError('Invalid arguments for xmln') + +def xml_write(filepath, tree): + """ Write XML file according to filename and given tree """ + if filepath.endswith('.py'): # if .pyc, no need to generate XML + output_xml = minidom.parseString(ET.tostring(tree)).toprettyxml(indent=' ') + output_path = path.dirname(path.abspath(filepath)) + fpath = '%s/%s' % (output_path, path.basename(filepath).replace('.py', '_views.xml')) + with open(fpath, 'w') as output_file: + output_file.write(output_xml) diff --git a/yaltik_dsl/xml_base.py b/yaltik_dsl/xml_base.py index a38fbb8..d1f7142 100644 --- a/yaltik_dsl/xml_base.py +++ b/yaltik_dsl/xml_base.py @@ -1,3 +1,26 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# __coconut_hash__ = 0xd881e1a + +# Compiled with Coconut version 1.4.3 [Ernest Scribbler] + +""" XML helpers and macros """ + +# Coconut Header: ------------------------------------------------------------- + +from __future__ import print_function, absolute_import, unicode_literals, division +import sys as _coconut_sys, os.path as _coconut_os_path +_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__)) +_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__") +if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path: + del _coconut_sys.modules[b"__coconut__"] +_coconut_sys.path.insert(0, _coconut_file_path) +from __coconut__ import * +from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match + + +# Compiled Coconut: ----------------------------------------------------------- + # -*- coding: utf-8 -*- # # Copyright 2019-2020 Fabien Bourgeois @@ -15,44 +38,111 @@ # 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 +from typing import List +from typing import Union +from typing import Any -def xmlroot(tree): +# TODO: ADT for XMLchild children**s** + +class XMLChildren(_coconut.typing.NamedTuple("XMLChildren", [("clist", 'List[Union[XMLChildDict, XMLChildText]]')]), _coconut.object): + __slots__ = () + __ne__ = _coconut.object.__ne__ + def __eq__(self, other): + return self.__class__ is other.__class__ and _coconut.tuple.__eq__(self, other) + def __hash__(self): + return _coconut.tuple.__hash__(self) ^ hash(self.__class__) + +class XMLChildText(_coconut.typing.NamedTuple("XMLChildText", [("text", 'Union[str, unicode]')]), _coconut.object): + __slots__ = () + __ne__ = _coconut.object.__ne__ + def __eq__(self, other): + return self.__class__ is other.__class__ and _coconut.tuple.__eq__(self, other) + def __hash__(self): + return _coconut.tuple.__hash__(self) ^ hash(self.__class__) + +class XMLChildDict(_coconut.typing.NamedTuple("XMLChildDict", [("tag", 'str'), ("attrs", 'Dict[str, str]'), ("children", 'XMLChildren')]), _coconut.object): + __slots__ = () + __ne__ = _coconut.object.__ne__ + def __eq__(self, other): + return self.__class__ is other.__class__ and _coconut.tuple.__eq__(self, other) + def __hash__(self): + return _coconut.tuple.__hash__(self) ^ hash(self.__class__) + + +def xmlroot(tree # type: Dict[str, Any] + ): +# type: (...) -> ET.Element """ Special process for root XML Node """ - rootel = ET.Element(tree['tag'], tree['attrs']) - children = tree['children'] - if children: - xmlchild(rootel, children) + rootel = (ET.Element)(*(tree['tag'], tree['attrs'])) + if 'children' in tree: + (xmlchild)(*(rootel, tree['children'])) return rootel - -def xmlchild(parent, children): +def xmlchild(parent, # type: ET.Element + children # type: Any + ): +# type: (...) -> None """ Handling of children (ie non root) XML Nodes with/o text and subchildren (recursive) """ - for child in children: - if isinstance(child, (str, unicode)): - parent.text = child - else: - attrs = {unicode(k): unicode(v) for [k, v] in child['attrs'].items()} - new_parent = ET.SubElement(parent, child['tag'], attrs) - subchildren = child['children'] - if subchildren: - xmlchild(new_parent, subchildren) + _coconut_match_to = children + _coconut_match_check = False + if _coconut.isinstance(_coconut_match_to, (str, unicode)): + _coconut_match_check = True + if _coconut_match_check: + parent.text = children + return + _coconut_match_to = children + _coconut_match_check = False + if _coconut.isinstance(_coconut_match_to, dict): + _coconut_match_check = True + if _coconut_match_check: + attrs = dict(((unicode(k)), (unicode(v))) for [k, v] in children['attrs'].items()) + new_parent = (ET.SubElement)(*(parent, children['tag'], attrs)) + subchildren = children['children'] + if subchildren: + (xmlchild)(*(new_parent, subchildren)) + _coconut_match_to = children + _coconut_match_check = False + if _coconut.isinstance(_coconut_match_to, list): + _coconut_match_check = True + if _coconut_match_check: + (consume)((map)(*(((_coconut.functools.partial(_coconut.functools.partial, xmlchild))(parent)), children))) -def xmln(tag='', attrs=None, children=None, text=False): +def xmln(tag='', # type: str + attrs={}, # type: Dict[str, str] + children=[] # type: Union[str, List] + ): +# type: (...) -> Dict """ XMLNode with default children, not attributes """ - children = ([text] if text else children) or [] - return {'tag': tag, 'attrs': attrs or {}, 'children': children} + _coconut_match_to = children + _coconut_match_check = False + if _coconut.isinstance(_coconut_match_to, str): + c = _coconut_match_to + _coconut_match_check = True + if _coconut_match_check: + return {'tag': tag, 'attrs': attrs, 'children': [c]} + else: + _coconut_match_to = children + _coconut_match_check = False + if _coconut.isinstance(_coconut_match_to, list): + c = _coconut_match_to + _coconut_match_check = True + if _coconut_match_check: + return {'tag': tag, 'attrs': attrs, 'children': c} + else: + raise TypeError('Invalid arguments for xmln') def xml_write(filepath, tree): """ Write XML file according to filename and given tree """ - if filepath.endswith('.py'): # if .pyc, no need to generate XML + if filepath.endswith('.py'): # if .pyc, no need to generate XML output_xml = minidom.parseString(ET.tostring(tree)).toprettyxml(indent=' ') output_path = path.dirname(path.abspath(filepath)) fpath = '%s/%s' % (output_path, path.basename(filepath).replace('.py', '_views.xml'))