[REM]Remove Coconut from Yaltik DSL

This commit is contained in:
Fabien BOURGEOIS 2020-05-03 12:03:17 +02:00
parent 0c037815b9
commit 0a86aa3eab
7 changed files with 2 additions and 1312 deletions

View File

@ -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

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
#
# 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 <http://www.gnu.org/licenses/>.
from . import odoo_dsl

View File

@ -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']}
}

View File

@ -1,127 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
#
# 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 <http://www.gnu.org/licenses/>.
""" 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])

View File

@ -1,235 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>
#
# 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 <http://www.gnu.org/licenses/>.
""" 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()

View File

@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>
#
# 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 <http://www.gnu.org/licenses/>.
""" 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()
'<?xml version' `self.assertIn` output_xml
'<root>' `self.assertIn` output_xml
'<child1 attr="value"/>' `self.assertIn` output_xml
'<child2>Some text</child2>' `self.assertIn` output_xml
unlink(filepath)
if __name__ == '__main__':
unittest.main()

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
#
# 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 <http://www.gnu.org/licenses/>.
""" 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)