[MIG]Yaltik DSL : base conversion to Coconut

This commit is contained in:
Fabien BOURGEOIS 2022-11-17 15:49:41 +01:00
parent 40f6c88fee
commit 214503ae4f
14 changed files with 3647 additions and 55 deletions

View File

@ -1,3 +1,13 @@
.PHONY: setup
setup:
pip install --user -U coconut[watch,jobs,backports,mypy]
.PHONY: testdev
testdev: clean build test
.PHONY: testprod
testprod: clean prod test
.PHONY: clean .PHONY: clean
clean: clean:
find . -name '*.pyc' -delete find . -name '*.pyc' -delete
@ -7,3 +17,15 @@ clean:
test: test:
python3 tests/test_xml_base.py python3 tests/test_xml_base.py
python3 tests/test_odoo.py python3 tests/test_odoo.py
.PHONY: build
build:
coconut -t sys -j sys .
.PHONY: prod
prod:
coconut --notco -t 3.9 -j sys -f .
.PHONY: watch
watch:
coconut --strict -t sys -j sys -w .

View File

@ -19,7 +19,7 @@
'name': 'Yaltik Odoo DSL base module and fns', 'name': 'Yaltik Odoo DSL base module and fns',
'summary': 'Yaltik Odoo Domain Specific Language base module and functions', 'summary': 'Yaltik Odoo Domain Specific Language base module and functions',
'description': """ Yaltik Odoo Domain Specific Language base module and functions """, 'description': """ Yaltik Odoo Domain Specific Language base module and functions """,
'version': '16.0.0.4.5', 'version': '16.0.0.5.0',
'category': 'Yaltik', 'category': 'Yaltik',
'author': 'Fabien Bourgeois', 'author': 'Fabien Bourgeois',
'license': 'AGPL-3', 'license': 'AGPL-3',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
# -*- 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/>.
from . import odoo_dsl

View File

@ -1,3 +1,38 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0xf09e1c4d
# Compiled with Coconut version 2.1.1 [The Spanish Inquisition]
# Coconut Header: -------------------------------------------------------------
from __future__ import generator_stop, annotations
import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = _coconut_os.path.dirname(_coconut_os.path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get("__coconut__")
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir: # type: ignore
del _coconut_sys.modules["__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
_coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
import __coconut__ as _coconut__coconut__
_coconut__coconut__.__name__ = _coconut_full_module_name
for _coconut_v in vars(_coconut__coconut__).values():
if getattr(_coconut_v, "__module__", None) == "__coconut__":
try:
_coconut_v.__module__ = _coconut_full_module_name
except AttributeError:
_coconut_v_type = type(_coconut_v)
if getattr(_coconut_v_type, "__module__", None) == "__coconut__":
_coconut_v_type.__module__ = _coconut_full_module_name
_coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import _namedtuple_of, _coconut, _coconut_super, _coconut_MatchError, _coconut_iter_getitem, _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_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_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_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul
_coconut_sys.path.pop(0)
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>

View File

@ -0,0 +1,126 @@
# -*- 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 .xml_base import xmlroot, xmln
# XML helpers functions and macros
# Odoo root XML Node
odoo = lambda children: xmlroot({'tag': 'odoo', 'attrs': {}, 'children': children})
# Special data Node
def data(*args):
""" Allow optional args on data tag """
if len(args) == 1:
args = list(args)
args.insert(0, {})
return xmln('data', *args)
# Aliases
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)
# Actions
act_window = lambda *args: xmln('act_window', *args)
def act_window_model(model, attrs):
""" Build new act_window from model and args """
xmlid = '%s_view_action' % (model.replace('.', '_'))
name = '%s Action' % ' '.join(map(lambda w: w.capitalize(), model.split('.')))
attrs_clone = attrs.copy() # Avoid side-effect
attrs_clone.update({'id': xmlid, 'name': name, 'res_model': model})
return act_window(attrs_clone)
def action_server_code(xmlid, name, modelref, code):
""" Server actions of type code """
children = [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'}, children)
def client_action_multi(xmlid, name, model, action):
""" Client action multi (ir.values), with own xmlid, name, targeted model
and action """
action = "'ir.actions.server,%d'%{}".format(action)
children = [field_name(name),
field({'name': 'key2', 'eval': "'client_action_multi'"}),
field({'name': 'model', 'eval': "'%s'" % model}),
field({'name': 'value', 'eval': action})]
return record({'id': xmlid, 'model': 'ir.values'}, children)
# Menus
menuitem = lambda *args: xmln('menuitem', *args)
def menuitem_model(model, attrs):
""" 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
attrs_clone.update({'id': xmlid, 'action': actionid})
return menuitem(attrs_clone)
# Form aliases
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])
# Fields
field = lambda *args: xmln('field', *args)
field_name = lambda name: field({'name': 'name'}, [name])
field_nval = lambda name, value: field({'name': name}, [value])
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)
# Search
filter = lambda *args: xmln('filter', *args)
# Views
view = lambda xmlid, children: record({'id': xmlid, 'model': 'ir.ui.view'}, children)
def view_def(xmlid, name, model, arch):
""" Shortcut for new view """
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(map(lambda w: w.capitalize(), model.split('.')))
xmlid = "%s_view_%s" % (model_und, view_type)
name = ' '.join([model_cap, view_type.capitalize()])
return view_def(xmlid, name, model, 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 = '%s_inherit_%s' % (inherited, module)
model_cap = ' '.join(map(lambda w: w.capitalize(), model.split('.')))
name = '%s Adaptations' % model_cap
return view(xmlid, [field_name(name), field_model(model),
field_inherit(inherit), field_arch(arch)])

View File

@ -1,3 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x98b383b7
# Compiled with Coconut version 2.1.1 [The Spanish Inquisition]
""" Odoo XML DSL """
# Coconut Header: -------------------------------------------------------------
from __future__ import generator_stop, annotations
import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = _coconut_os.path.dirname(_coconut_os.path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get("__coconut__")
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir: # type: ignore
del _coconut_sys.modules["__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
_coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
import __coconut__ as _coconut__coconut__
_coconut__coconut__.__name__ = _coconut_full_module_name
for _coconut_v in vars(_coconut__coconut__).values():
if getattr(_coconut_v, "__module__", None) == "__coconut__":
try:
_coconut_v.__module__ = _coconut_full_module_name
except AttributeError:
_coconut_v_type = type(_coconut_v)
if getattr(_coconut_v_type, "__module__", None) == "__coconut__":
_coconut_v_type.__module__ = _coconut_full_module_name
_coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import _namedtuple_of, _coconut, _coconut_super, _coconut_MatchError, _coconut_iter_getitem, _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_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_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_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul
_coconut_sys.path.pop(0)
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
@ -15,9 +52,10 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Odoo XML DSL """
from .xml_base import xmlroot, xmln
from .xml_base import xmlroot
from .xml_base import xmln
# XML helpers functions and macros # XML helpers functions and macros
@ -33,6 +71,7 @@ def data(*args):
return xmln('data', *args) return xmln('data', *args)
# Aliases # Aliases
function = lambda *args: xmln('function', *args) function = lambda *args: xmln('function', *args)
record = lambda *args: xmln('record', *args) record = lambda *args: xmln('record', *args)
form = lambda *args: xmln('form', *args) form = lambda *args: xmln('form', *args)
@ -49,25 +88,22 @@ def act_window_model(model, attrs):
attrs_clone.update({'id': xmlid, 'name': name, 'res_model': model}) attrs_clone.update({'id': xmlid, 'name': name, 'res_model': model})
return act_window(attrs_clone) return act_window(attrs_clone)
def action_server_code(xmlid, name, modelref, code): def action_server_code(xmlid, name, modelref, code):
""" Server actions of type code """ """ Server actions of type code """
children = [field_name(name), children = [field_name(name), field({'name': 'model_id', 'ref': modelref}, []), field({'name': 'state'}, ['code',]), field({'name': 'code'}, [code,])]
field({'name': 'model_id', 'ref': modelref}, []),
field({'name': 'state'}, ['code']),
field({'name': 'code'}, [code])]
return record({'id': xmlid, 'model': 'ir.actions.server'}, children) return record({'id': xmlid, 'model': 'ir.actions.server'}, children)
def client_action_multi(xmlid, name, model, action): def client_action_multi(xmlid, name, model, action):
""" Client action multi (ir.values), with own xmlid, name, targeted model """ Client action multi (ir.values), with own xmlid, name, targeted model
and action """ and action """
action = "'ir.actions.server,%d'%{}".format(action) action = "'ir.actions.server,%d'%{}".format(action)
children = [field_name(name), children = [field_name(name), field({'name': 'key2', 'eval': "'client_action_multi'"}), field({'name': 'model', 'eval': "'%s'" % model}), field({'name': 'value', 'eval': action})]
field({'name': 'key2', 'eval': "'client_action_multi'"}),
field({'name': 'model', 'eval': "'%s'" % model}),
field({'name': 'value', 'eval': action})]
return record({'id': xmlid, 'model': 'ir.values'}, children) return record({'id': xmlid, 'model': 'ir.values'}, children)
# Menus # Menus
menuitem = lambda *args: xmln('menuitem', *args) menuitem = lambda *args: xmln('menuitem', *args)
def menuitem_model(model, attrs): def menuitem_model(model, attrs):
""" Build new menuitem from model and attrs """ """ Build new menuitem from model and attrs """
@ -79,6 +115,7 @@ def menuitem_model(model, attrs):
return menuitem(attrs_clone) return menuitem(attrs_clone)
# Form aliases # Form aliases
group = lambda *args: xmln('group', *args) group = lambda *args: xmln('group', *args)
header = lambda *args: xmln('header', *args) header = lambda *args: xmln('header', *args)
footer = lambda *args: xmln('footer', *args) footer = lambda *args: xmln('footer', *args)
@ -86,13 +123,13 @@ sheet = lambda *args: xmln('sheet', *args)
button = lambda *args: xmln('button', *args) button = lambda *args: xmln('button', *args)
p = lambda *args: xmln('p', *args) p = lambda *args: xmln('p', *args)
xpath = lambda *args: xmln('xpath', *args) xpath = lambda *args: xmln('xpath', *args)
attribute = lambda name, value: xmln('attribute', {'name': name}, [value]) attribute = lambda name, value: xmln('attribute', {'name': name}, [value,])
# Fields # Fields
field = lambda *args: xmln('field', *args) field = lambda *args: xmln('field', *args)
field_name = lambda name: field({'name': 'name'}, [name]) field_name = lambda name: field({'name': 'name'}, [name,])
field_nval = lambda name, value: field({'name': name}, [value]) field_nval = lambda name, value: field({'name': name}, [value,])
field_model = lambda model: field({'name': 'model'}, [model]) field_model = lambda model: field({'name': 'model'}, [model,])
field_inherit = lambda xmlid: field({'name': 'inherit_id', 'ref': xmlid}, []) field_inherit = lambda xmlid: field({'name': 'inherit_id', 'ref': xmlid}, [])
field_arch = lambda *args: field({'name': 'arch', 'type': 'xml'}, *args) field_arch = lambda *args: field({'name': 'arch', 'type': 'xml'}, *args)
@ -106,6 +143,7 @@ def view_def(xmlid, name, model, arch):
""" Shortcut for new view """ """ Shortcut for new view """
return view(xmlid, [field_name(name), field_model(model), field_arch(arch)]) return view(xmlid, [field_name(name), field_model(model), field_arch(arch)])
def view_new(view_type, model, arch): def view_new(view_type, model, arch):
""" View : new view definition, based on type (form, tree, ...) and model ID """ """ View : new view definition, based on type (form, tree, ...) and model ID """
model_und = model.replace('.', '_') model_und = model.replace('.', '_')
@ -114,6 +152,7 @@ def view_new(view_type, model, arch):
name = ' '.join([model_cap, view_type.capitalize()]) name = ' '.join([model_cap, view_type.capitalize()])
return view_def(xmlid, name, model, arch) return view_def(xmlid, name, model, arch)
def view_inherit(filename, model, inherit, arch): def view_inherit(filename, model, inherit, arch):
""" Inherited View simplification with name of the record, xmlid for model """ Inherited View simplification with name of the record, xmlid for model
and inherited view """ and inherited view """
@ -122,5 +161,4 @@ def view_inherit(filename, model, inherit, arch):
xmlid = '%s_inherit_%s' % (inherited, module) xmlid = '%s_inherit_%s' % (inherited, module)
model_cap = ' '.join(map(lambda w: w.capitalize(), model.split('.'))) model_cap = ' '.join(map(lambda w: w.capitalize(), model.split('.')))
name = '%s Adaptations' % model_cap name = '%s Adaptations' % model_cap
return view(xmlid, [field_name(name), field_model(model), return view(xmlid, [field_name(name), field_model(model), field_inherit(inherit), field_arch(arch)])
field_inherit(inherit), field_arch(arch)])

View File

@ -0,0 +1,75 @@
# -*- 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 collections import namedtuple
from functools import partial
XMLDictElement = namedtuple('XMLDictElement', ['tag', 'attrs', 'children'])
def xmlroot(tree):
""" Special process for root XML Node """
rootel = ET.Element(tree['tag'], tree['attrs'])
if 'children' in tree:
xmlchild(rootel, tree['children'])
return rootel
def xmlchild(parent, children):
""" Handling of children (ie non root) XML Nodes with/o text and
subchildren (recursive) """
if isinstance(children, str):
parent.text = children
elif isinstance(children, XMLDictElement):
attrs = {str(k): str(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)
elif isinstance(children, list):
list(map(partial(xmlchild, parent), children))
else:
raise TypeError('Invalid arguments for xmlchild')
def xmln(tag='', attrs={}, children=[]):
""" XMLDictElement building from dict object, with defaults """
if isinstance(attrs, list):
children = attrs
attrs = {}
xmldictel = partial(XMLDictElement, tag, attrs)
if isinstance(children, str):
return xmldictel([children])
if isinstance(children, list):
return xmldictel(children)
raise TypeError('Invalid arguments for xmln')
def xml_write(filepath, tree, pretty=True, suffix='_views'):
""" Write XML file according to filename and given tree """
if filepath.endswith('.py'): # if .pyc, no need to generate XML
output_xml = ET.tostring(tree)
if pretty:
output_xml = minidom.parseString(output_xml).toprettyxml(indent=' ')
output_path = path.abspath(filepath).split('/')
output_path[-1] = output_path[-1].replace('.py', '%s.xml' % suffix)
output_path = '/'.join(output_path)
with open(output_path, 'w') as output_file:
output_file.write(output_xml)

View File

@ -1,3 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x491ccdad
# Compiled with Coconut version 2.1.1 [The Spanish Inquisition]
""" XML helpers and macros """
# Coconut Header: -------------------------------------------------------------
from __future__ import generator_stop, annotations
import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = _coconut_os.path.dirname(_coconut_os.path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get("__coconut__")
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir: # type: ignore
del _coconut_sys.modules["__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
_coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
import __coconut__ as _coconut__coconut__
_coconut__coconut__.__name__ = _coconut_full_module_name
for _coconut_v in vars(_coconut__coconut__).values():
if getattr(_coconut_v, "__module__", None) == "__coconut__":
try:
_coconut_v.__module__ = _coconut_full_module_name
except AttributeError:
_coconut_v_type = type(_coconut_v)
if getattr(_coconut_v_type, "__module__", None) == "__coconut__":
_coconut_v_type.__module__ = _coconut_full_module_name
_coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import _namedtuple_of, _coconut, _coconut_super, _coconut_MatchError, _coconut_iter_getitem, _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_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_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_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul
_coconut_sys.path.pop(0)
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
@ -15,7 +52,7 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" XML helpers and macros """
from os import path from os import path
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -33,6 +70,7 @@ def xmlroot(tree):
xmlchild(rootel, tree['children']) xmlchild(rootel, tree['children'])
return rootel return rootel
def xmlchild(parent, children): def xmlchild(parent, children):
""" Handling of children (ie non root) XML Nodes with/o text and """ Handling of children (ie non root) XML Nodes with/o text and
subchildren (recursive) """ subchildren (recursive) """
@ -49,6 +87,7 @@ def xmlchild(parent, children):
else: else:
raise TypeError('Invalid arguments for xmlchild') raise TypeError('Invalid arguments for xmlchild')
def xmln(tag='', attrs={}, children=[]): def xmln(tag='', attrs={}, children=[]):
""" XMLDictElement building from dict object, with defaults """ """ XMLDictElement building from dict object, with defaults """
if isinstance(attrs, list): if isinstance(attrs, list):
@ -56,12 +95,13 @@ def xmln(tag='', attrs={}, children=[]):
attrs = {} attrs = {}
xmldictel = partial(XMLDictElement, tag, attrs) xmldictel = partial(XMLDictElement, tag, attrs)
if isinstance(children, str): if isinstance(children, str):
return xmldictel([children]) return xmldictel([children,])
if isinstance(children, list): if isinstance(children, list):
return xmldictel(children) return xmldictel(children)
raise TypeError('Invalid arguments for xmln') raise TypeError('Invalid arguments for xmln')
def xml_write(filepath, tree, pretty=True, suffix='_views'): def xml_write(filepath, tree, pretty=True, suffix='_views'):
""" Write XML file according to filename and given 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,247 @@
# -*- 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 src.xml_base import XMLDictElement
import src.odoo_dsl as od
class TestOdooBase(unittest.TestCase):
""" Odoo Helpers tests """
def test_odoo(self):
""" Test odoo function """
element = od.odoo([])
self.assertIsInstance(element, ET.Element)
self.assertEqual(element.tag, 'odoo')
def test_data(self):
""" Test data function """
element = od.data([])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'data')
self.assertEqual(element.attrs, {})
element = od.data({"one": "attribute"}, [])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'data')
self.assertEqual(element.attrs, {"one": "attribute"})
def test_aliases(self):
""" Test simple aliases to xmln """
element = od.record({"one": "attribute"}, 'A child')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs, {"one": "attribute"})
self.assertEqual(element.children, ['A child'])
element = od.tree()
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'tree')
def test_act_window_model(self):
""" Test act_window function """
element = od.act_window_model('sample.model', {'view_type': 'form'})
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'act_window')
self.assertEqual(element.attrs['view_type'], 'form')
self.assertEqual(element.attrs['id'], 'sample_model_view_action')
self.assertEqual(element.attrs['res_model'], 'sample.model')
self.assertEqual(element.attrs['name'], 'Sample Model Action')
def test_menunitem_model(self):
""" Test menuitem function """
element = od.menuitem_model('sample.model', {'groups': 'base.user_employee'})
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'menuitem')
self.assertEqual(element.attrs['groups'], 'base.user_employee')
self.assertEqual(element.attrs['id'], 'sample_model_menu')
self.assertEqual(element.attrs['action'], 'sample_model_view_action')
def test_attribute(self):
""" Test attribute function """
element = od.attribute('invisible', "1")
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'attribute')
self.assertEqual(element.attrs['name'], 'invisible')
self.assertEqual(element.children, ["1"])
def test_fields(self):
""" Test fields function """
element = od.field({"one": "attribute"}, 'A child')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field')
element = od.field_name('A name')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'name')
self.assertEqual(element.children, ['A name'])
element = od.field_model('sample.model')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'model')
self.assertEqual(element.children, ['sample.model'])
element = od.field_inherit('module.xml_view')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'inherit_id')
self.assertEqual(element.attrs['ref'], 'module.xml_view')
self.assertFalse(element.children)
element = od.field_arch()
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'arch')
self.assertEqual(element.attrs['type'], 'xml')
self.assertFalse(element.children)
def test_view(self):
""" Test view function """
element = od.view('view_xmlid', [])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'view_xmlid')
self.assertEqual(element.attrs['model'], 'ir.ui.view')
self.assertFalse(element.children)
def test_view_def(self):
""" Test view_def function """
element = od.view_def('view_xmlid', 'View', 'sample.model', [])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'view_xmlid')
self.assertEqual(element.attrs['model'], 'ir.ui.view')
self.assertEqual((len(element.children)), 3)
self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['View'])
self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model'])
self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'arch')
self.assertEqual(element.children[2].attrs['type'], 'xml')
self.assertFalse(element.children[2].children)
def test_view_new(self):
""" Test view_new function """
element = od.view_new('tree', 'sample.model', [])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'sample_model_view_tree')
self.assertEqual(element.attrs['model'], 'ir.ui.view')
self.assertEqual((len(element.children)), 3)
self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Sample Model Tree'])
self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model'])
self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'arch')
self.assertEqual(element.children[2].attrs['type'], 'xml')
self.assertFalse(element.children[2].children)
def test_view_inherit(self):
""" Test view_inherit function """
element = od.view_inherit('odoo.addons.module', 'sample.model', 'parent.view', [])
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'view_inherit_module')
self.assertEqual(element.attrs['model'], 'ir.ui.view')
self.assertEqual((len(element.children)), 4)
self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Sample Model Adaptations'])
self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model'])
self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'inherit_id')
self.assertFalse(element.children[2].children)
self.assertIsInstance(element.children[3], XMLDictElement)
self.assertEqual(element.children[3].tag, 'field')
self.assertEqual(element.children[3].attrs['name'], 'arch')
self.assertEqual(element.children[3].attrs['type'], 'xml')
self.assertFalse(element.children[3].children)
def test_action_server_code(self):
""" Test action_server_code function """
element = od.action_server_code('sample.xmlid', 'Code', 'sample.model',
'''record.do_something()''')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'sample.xmlid')
self.assertEqual(element.attrs['model'], 'ir.actions.server')
self.assertEqual((len(element.children)), 4)
self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Code'])
self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model_id')
self.assertEqual(element.children[1].attrs['ref'], 'sample.model')
self.assertFalse(element.children[1].children)
self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'state')
self.assertEqual(element.children[2].children, ['code'])
self.assertEqual(element.children[3].tag, 'field')
self.assertEqual(element.children[3].attrs['name'], 'code')
self.assertEqual(element.children[3].children, ['record.do_something()'])
def test_client_action_multi(self):
""" Test client_action_multi function """
element = od.client_action_multi('sample.xmlid', 'Multi', 'sample.model', 'sample.action')
self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'sample.xmlid')
self.assertEqual(element.attrs['model'], 'ir.values')
self.assertEqual((len(element.children)), 4)
self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Multi'])
self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'key2')
self.assertEqual(element.children[1].attrs['eval'], "'client_action_multi'")
self.assertFalse(element.children[1].children)
self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'model')
self.assertEqual(element.children[2].attrs['eval'], "'sample.model'")
self.assertEqual(element.children[3].tag, 'field')
self.assertEqual(element.children[3].attrs['name'], 'value')
self.assertEqual(element.children[3].attrs['eval'], "'ir.actions.server,%d'%sample.action")
if __name__ == '__main__':
unittest.main()

View File

@ -1,3 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x40e8d147
# Compiled with Coconut version 2.1.1 [The Spanish Inquisition]
""" Odoo Helpers tests """
# Coconut Header: -------------------------------------------------------------
from __future__ import generator_stop, annotations
import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = _coconut_os.path.dirname(_coconut_os.path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get("__coconut__")
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir: # type: ignore
del _coconut_sys.modules["__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
_coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
import __coconut__ as _coconut__coconut__
_coconut__coconut__.__name__ = _coconut_full_module_name
for _coconut_v in vars(_coconut__coconut__).values():
if getattr(_coconut_v, "__module__", None) == "__coconut__":
try:
_coconut_v.__module__ = _coconut_full_module_name
except AttributeError:
_coconut_v_type = type(_coconut_v)
if getattr(_coconut_v_type, "__module__", None) == "__coconut__":
_coconut_v_type.__module__ = _coconut_full_module_name
_coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import _namedtuple_of, _coconut, _coconut_super, _coconut_MatchError, _coconut_iter_getitem, _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_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_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_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul
_coconut_sys.path.pop(0)
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>
@ -15,7 +52,7 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Odoo Helpers tests """
import unittest import unittest
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -31,6 +68,7 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element, ET.Element) self.assertIsInstance(element, ET.Element)
self.assertEqual(element.tag, 'odoo') self.assertEqual(element.tag, 'odoo')
def test_data(self): def test_data(self):
""" Test data function """ """ Test data function """
element = od.data([]) element = od.data([])
@ -43,18 +81,20 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.tag, 'data') self.assertEqual(element.tag, 'data')
self.assertEqual(element.attrs, {"one": "attribute"}) self.assertEqual(element.attrs, {"one": "attribute"})
def test_aliases(self): def test_aliases(self):
""" Test simple aliases to xmln """ """ Test simple aliases to xmln """
element = od.record({"one": "attribute"}, 'A child') element = od.record({"one": "attribute"}, 'A child')
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record') self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs, {"one": "attribute"}) self.assertEqual(element.attrs, {"one": "attribute"})
self.assertEqual(element.children, ['A child']) self.assertEqual(element.children, ['A child',])
element = od.tree() element = od.tree()
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'tree') self.assertEqual(element.tag, 'tree')
def test_act_window_model(self): def test_act_window_model(self):
""" Test act_window function """ """ Test act_window function """
element = od.act_window_model('sample.model', {'view_type': 'form'}) element = od.act_window_model('sample.model', {'view_type': 'form'})
@ -65,6 +105,7 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.attrs['res_model'], 'sample.model') self.assertEqual(element.attrs['res_model'], 'sample.model')
self.assertEqual(element.attrs['name'], 'Sample Model Action') self.assertEqual(element.attrs['name'], 'Sample Model Action')
def test_menunitem_model(self): def test_menunitem_model(self):
""" Test menuitem function """ """ Test menuitem function """
element = od.menuitem_model('sample.model', {'groups': 'base.user_employee'}) element = od.menuitem_model('sample.model', {'groups': 'base.user_employee'})
@ -74,13 +115,15 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.attrs['id'], 'sample_model_menu') self.assertEqual(element.attrs['id'], 'sample_model_menu')
self.assertEqual(element.attrs['action'], 'sample_model_view_action') self.assertEqual(element.attrs['action'], 'sample_model_view_action')
def test_attribute(self): def test_attribute(self):
""" Test attribute function """ """ Test attribute function """
element = od.attribute('invisible', "1") element = od.attribute('invisible', "1")
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'attribute') self.assertEqual(element.tag, 'attribute')
self.assertEqual(element.attrs['name'], 'invisible') self.assertEqual(element.attrs['name'], 'invisible')
self.assertEqual(element.children, ["1"]) self.assertEqual(element.children, ["1",])
def test_fields(self): def test_fields(self):
""" Test fields function """ """ Test fields function """
@ -92,13 +135,13 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field') self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'name') self.assertEqual(element.attrs['name'], 'name')
self.assertEqual(element.children, ['A name']) self.assertEqual(element.children, ['A name',])
element = od.field_model('sample.model') element = od.field_model('sample.model')
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'field') self.assertEqual(element.tag, 'field')
self.assertEqual(element.attrs['name'], 'model') self.assertEqual(element.attrs['name'], 'model')
self.assertEqual(element.children, ['sample.model']) self.assertEqual(element.children, ['sample.model',])
element = od.field_inherit('module.xml_view') element = od.field_inherit('module.xml_view')
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
@ -114,6 +157,7 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.attrs['type'], 'xml') self.assertEqual(element.attrs['type'], 'xml')
self.assertFalse(element.children) self.assertFalse(element.children)
def test_view(self): def test_view(self):
""" Test view function """ """ Test view function """
element = od.view('view_xmlid', []) element = od.view('view_xmlid', [])
@ -123,6 +167,7 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.attrs['model'], 'ir.ui.view') self.assertEqual(element.attrs['model'], 'ir.ui.view')
self.assertFalse(element.children) self.assertFalse(element.children)
def test_view_def(self): def test_view_def(self):
""" Test view_def function """ """ Test view_def function """
element = od.view_def('view_xmlid', 'View', 'sample.model', []) element = od.view_def('view_xmlid', 'View', 'sample.model', [])
@ -134,17 +179,18 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element.children[0], XMLDictElement) self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field') self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name') self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['View']) self.assertEqual(element.children[0].children, ['View',])
self.assertIsInstance(element.children[1], XMLDictElement) self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field') self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model') self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model']) self.assertEqual(element.children[1].children, ['sample.model',])
self.assertIsInstance(element.children[2], XMLDictElement) self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field') self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'arch') self.assertEqual(element.children[2].attrs['name'], 'arch')
self.assertEqual(element.children[2].attrs['type'], 'xml') self.assertEqual(element.children[2].attrs['type'], 'xml')
self.assertFalse(element.children[2].children) self.assertFalse(element.children[2].children)
def test_view_new(self): def test_view_new(self):
""" Test view_new function """ """ Test view_new function """
element = od.view_new('tree', 'sample.model', []) element = od.view_new('tree', 'sample.model', [])
@ -156,17 +202,18 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element.children[0], XMLDictElement) self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field') self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name') self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Sample Model Tree']) self.assertEqual(element.children[0].children, ['Sample Model Tree',])
self.assertIsInstance(element.children[1], XMLDictElement) self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field') self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model') self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model']) self.assertEqual(element.children[1].children, ['sample.model',])
self.assertIsInstance(element.children[2], XMLDictElement) self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field') self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'arch') self.assertEqual(element.children[2].attrs['name'], 'arch')
self.assertEqual(element.children[2].attrs['type'], 'xml') self.assertEqual(element.children[2].attrs['type'], 'xml')
self.assertFalse(element.children[2].children) self.assertFalse(element.children[2].children)
def test_view_inherit(self): def test_view_inherit(self):
""" Test view_inherit function """ """ Test view_inherit function """
element = od.view_inherit('odoo.addons.module', 'sample.model', 'parent.view', []) element = od.view_inherit('odoo.addons.module', 'sample.model', 'parent.view', [])
@ -178,11 +225,11 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element.children[0], XMLDictElement) self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field') self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name') self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Sample Model Adaptations']) self.assertEqual(element.children[0].children, ['Sample Model Adaptations',])
self.assertIsInstance(element.children[1], XMLDictElement) self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field') self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model') self.assertEqual(element.children[1].attrs['name'], 'model')
self.assertEqual(element.children[1].children, ['sample.model']) self.assertEqual(element.children[1].children, ['sample.model',])
self.assertIsInstance(element.children[2], XMLDictElement) self.assertIsInstance(element.children[2], XMLDictElement)
self.assertEqual(element.children[2].tag, 'field') self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'inherit_id') self.assertEqual(element.children[2].attrs['name'], 'inherit_id')
@ -193,10 +240,10 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.children[3].attrs['type'], 'xml') self.assertEqual(element.children[3].attrs['type'], 'xml')
self.assertFalse(element.children[3].children) self.assertFalse(element.children[3].children)
def test_action_server_code(self): def test_action_server_code(self):
""" Test action_server_code function """ """ Test action_server_code function """
element = od.action_server_code('sample.xmlid', 'Code', 'sample.model', element = od.action_server_code('sample.xmlid', 'Code', 'sample.model', '''record.do_something()''')
'''record.do_something()''')
self.assertIsInstance(element, XMLDictElement) self.assertIsInstance(element, XMLDictElement)
self.assertEqual(element.tag, 'record') self.assertEqual(element.tag, 'record')
self.assertEqual(element.attrs['id'], 'sample.xmlid') self.assertEqual(element.attrs['id'], 'sample.xmlid')
@ -205,7 +252,7 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element.children[0], XMLDictElement) self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field') self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name') self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Code']) self.assertEqual(element.children[0].children, ['Code',])
self.assertIsInstance(element.children[1], XMLDictElement) self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field') self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'model_id') self.assertEqual(element.children[1].attrs['name'], 'model_id')
@ -213,10 +260,11 @@ class TestOdooBase(unittest.TestCase):
self.assertFalse(element.children[1].children) self.assertFalse(element.children[1].children)
self.assertEqual(element.children[2].tag, 'field') self.assertEqual(element.children[2].tag, 'field')
self.assertEqual(element.children[2].attrs['name'], 'state') self.assertEqual(element.children[2].attrs['name'], 'state')
self.assertEqual(element.children[2].children, ['code']) self.assertEqual(element.children[2].children, ['code',])
self.assertEqual(element.children[3].tag, 'field') self.assertEqual(element.children[3].tag, 'field')
self.assertEqual(element.children[3].attrs['name'], 'code') self.assertEqual(element.children[3].attrs['name'], 'code')
self.assertEqual(element.children[3].children, ['record.do_something()']) self.assertEqual(element.children[3].children, ['record.do_something()',])
def test_client_action_multi(self): def test_client_action_multi(self):
""" Test client_action_multi function """ """ Test client_action_multi function """
@ -229,7 +277,7 @@ class TestOdooBase(unittest.TestCase):
self.assertIsInstance(element.children[0], XMLDictElement) self.assertIsInstance(element.children[0], XMLDictElement)
self.assertEqual(element.children[0].tag, 'field') self.assertEqual(element.children[0].tag, 'field')
self.assertEqual(element.children[0].attrs['name'], 'name') self.assertEqual(element.children[0].attrs['name'], 'name')
self.assertEqual(element.children[0].children, ['Multi']) self.assertEqual(element.children[0].children, ['Multi',])
self.assertIsInstance(element.children[1], XMLDictElement) self.assertIsInstance(element.children[1], XMLDictElement)
self.assertEqual(element.children[1].tag, 'field') self.assertEqual(element.children[1].tag, 'field')
self.assertEqual(element.children[1].attrs['name'], 'key2') self.assertEqual(element.children[1].attrs['name'], 'key2')
@ -243,5 +291,6 @@ class TestOdooBase(unittest.TestCase):
self.assertEqual(element.children[3].attrs['eval'], "'ir.actions.server,%d'%sample.action") self.assertEqual(element.children[3].attrs['eval'], "'ir.actions.server,%d'%sample.action")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,129 @@
# -*- 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 """
from functools import partial
import unittest
import xml.etree.ElementTree as ET
from os import unlink
from src.xml_base import xmln, xmlroot, xmlchild, xml_write
class TestXMLBase(unittest.TestCase):
""" XML Helpers tests """
def test_xmln(self):
""" Text xmln """
# Tags
self.assertDictEqual(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []})
self.assertEqual(xmln('a tag').tag, 'a tag')
# Attrs
self.assertDictEqual(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'})
# Childrens
self.assertListEqual(xmln(children=[1, 2, 3]).children, [1, 2, 3])
self.assertListEqual(xmln(children='Some text').children, ['Some text'])
with self.assertRaisesRegex(TypeError, 'Invalid arguments'):
xmln(children=False)
# Ensure that only children after tags is managed
element = xmln('tag', {'something': 'inside'})
self.assertIsInstance(element.attrs, dict)
self.assertIsInstance(element.children, list)
element = xmln('tag', ['something', 'inside'])
self.assertIsInstance(element.attrs, dict)
self.assertIsInstance(element.children, list)
def test_xmlchild(self):
""" Test xmlchild """
parent = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
xmlc_par = partial(xmlchild, parent)
# Bad arguments
with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'):
xmlc_par(False)
# Need XMLDictElement, not dict
with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'):
xmlc_par([{'tag': 't', 'attrs': {'a': 'b'}, 'children': []}])
xmlc_par(['some text'])
self.assertEqual(parent.text, 'some text')
xmlc_par([xmln('t', {'a': 'b'}, [])])
child = parent.iter('t').__next__()
self.assertEqual(child.tag, 't')
self.assertDictEqual(child.attrib, {'a': 'b'})
self.assertListEqual(list(child), [])
xmlc_par([xmln('t2', {1: 2}, [])])
child = parent.iter('t2').__next__()
self.assertDictEqual(child.attrib, {'1': '2'})
xmlc_par([xmln('tchildren', {}, [xmln('subchild', {}, [])])])
child = parent.iter('tchildren').__next__()
subchildren = list(child)
self.assertEqual(len(subchildren), 1)
self.assertEqual(subchildren[0].tag, 'subchild')
def test_xmlroot(self):
""" Test xmlroot """
root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
self.assertIsInstance(root, ET.Element)
with self.assertRaisesRegex(TypeError, 'not subscriptable'):
xmlroot(False)
with self.assertRaisesRegex(KeyError, 'tag'):
xmlroot({})
with self.assertRaisesRegex(KeyError, 'attrs'):
xmlroot({'tag': 'root'})
def test_xml_write(self):
""" test xml_write """
children = [xmln('child1', {'attr': 'value'}, []),
xmln('child2', {}, "Some text")]
tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children})
xmlw = lambda p: xml_write(p, tree)
self.assertIsNone(xmlw('/badpath'))
self.assertIsNone(xmlw('/bad.ext'))
xmlw(__file__)
filepath = __file__.replace('.py', '_views.xml')
with open(filepath, 'r') as output_file:
output_xml = output_file.read()
self.assertIn('<?xml version', output_xml)
self.assertIn('<root>', output_xml)
self.assertIn('<child1 attr="value"/>', output_xml)
self.assertIn('<child2>Some text</child2>', output_xml)
unlink(filepath)
xml_write(__file__, tree, suffix='_data')
filepath = __file__.replace('.py', '_data.xml')
with open(filepath, 'r') as output_file:
output_xml = output_file.read()
self.assertIn('<?xml version', output_xml)
self.assertIn('<root>', output_xml)
self.assertIn('<child1 attr="value"/>', output_xml)
self.assertIn('<child2>Some text</child2>', output_xml)
unlink(filepath)
if __name__ == '__main__':
unittest.main()

View File

@ -1,3 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0xfa849aa3
# Compiled with Coconut version 2.1.1 [The Spanish Inquisition]
""" XML Helpers tests """
# Coconut Header: -------------------------------------------------------------
from __future__ import generator_stop, annotations
import sys as _coconut_sys, os as _coconut_os
_coconut_file_dir = _coconut_os.path.dirname(_coconut_os.path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get("__coconut__")
if _coconut_cached_module is not None and _coconut_os.path.dirname(_coconut_cached_module.__file__) != _coconut_file_dir: # type: ignore
del _coconut_sys.modules["__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_dir)
_coconut_module_name = _coconut_os.path.splitext(_coconut_os.path.basename(_coconut_file_dir))[0]
if _coconut_module_name and _coconut_module_name[0].isalpha() and all(c.isalpha() or c.isdigit() for c in _coconut_module_name) and "__init__.py" in _coconut_os.listdir(_coconut_file_dir):
_coconut_full_module_name = str(_coconut_module_name + ".__coconut__")
import __coconut__ as _coconut__coconut__
_coconut__coconut__.__name__ = _coconut_full_module_name
for _coconut_v in vars(_coconut__coconut__).values():
if getattr(_coconut_v, "__module__", None) == "__coconut__":
try:
_coconut_v.__module__ = _coconut_full_module_name
except AttributeError:
_coconut_v_type = type(_coconut_v)
if getattr(_coconut_v_type, "__module__", None) == "__coconut__":
_coconut_v_type.__module__ = _coconut_full_module_name
_coconut_sys.modules[_coconut_full_module_name] = _coconut__coconut__
from __coconut__ import *
from __coconut__ import _namedtuple_of, _coconut, _coconut_super, _coconut_MatchError, _coconut_iter_getitem, _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_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_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_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul
_coconut_sys.path.pop(0)
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>
@ -15,13 +52,16 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" XML Helpers tests """
from functools import partial from functools import partial
import unittest import unittest
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from os import unlink from os import unlink
from src.xml_base import xmln, xmlroot, xmlchild, xml_write from src.xml_base import xmln
from src.xml_base import xmlroot
from src.xml_base import xmlchild
from src.xml_base import xml_write
class TestXMLBase(unittest.TestCase): class TestXMLBase(unittest.TestCase):
@ -29,19 +69,19 @@ class TestXMLBase(unittest.TestCase):
def test_xmln(self): def test_xmln(self):
""" Text xmln """ """ Text xmln """
# Tags # Tags
self.assertDictEqual(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []}) self.assertDictEqual(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []})
self.assertEqual(xmln('a tag').tag, 'a tag') self.assertEqual(xmln('a tag').tag, 'a tag')
# Attrs # Attrs
self.assertDictEqual(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'}) self.assertDictEqual(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'})
# Childrens # Childrens
self.assertListEqual(xmln(children=[1, 2, 3]).children, [1, 2, 3]) self.assertListEqual(xmln(children=[1, 2, 3]).children, [1, 2, 3])
self.assertListEqual(xmln(children='Some text').children, ['Some text']) self.assertListEqual(xmln(children='Some text').children, ['Some text',])
with self.assertRaisesRegex(TypeError, 'Invalid arguments'): with self.assertRaisesRegex(TypeError, 'Invalid arguments'):
xmln(children=False) xmln(children=False)
# Ensure that only children after tags is managed # Ensure that only children after tags is managed
element = xmln('tag', {'something': 'inside'}) element = xmln('tag', {'something': 'inside'})
self.assertIsInstance(element.attrs, dict) self.assertIsInstance(element.attrs, dict)
self.assertIsInstance(element.children, list) self.assertIsInstance(element.children, list)
@ -51,38 +91,40 @@ class TestXMLBase(unittest.TestCase):
self.assertIsInstance(element.children, list) self.assertIsInstance(element.children, list)
def test_xmlchild(self): def test_xmlchild(self):
""" Test xmlchild """ """ Test xmlchild """
parent = xmlroot({'tag': 'root', 'attrs': {}, 'children': []}) parent = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
xmlc_par = partial(xmlchild, parent) xmlc_par = partial(xmlchild, parent)
# Bad arguments # Bad arguments
with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'): with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'):
xmlc_par(False) xmlc_par(False)
# Need XMLDictElement, not dict # Need XMLDictElement, not dict
with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'): with self.assertRaisesRegex(TypeError, 'Invalid arguments for xmlchild'):
xmlc_par([{'tag': 't', 'attrs': {'a': 'b'}, 'children': []}]) xmlc_par([{'tag': 't', 'attrs': {'a': 'b'}, 'children': []},])
xmlc_par(['some text']) xmlc_par(['some text',])
self.assertEqual(parent.text, 'some text') self.assertEqual(parent.text, 'some text')
xmlc_par([xmln('t', {'a': 'b'}, [])]) xmlc_par([xmln('t', {'a': 'b'}, []),])
child = parent.iter('t').__next__() child = parent.iter('t').__next__()
self.assertEqual(child.tag, 't') self.assertEqual(child.tag, 't')
self.assertDictEqual(child.attrib, {'a': 'b'}) self.assertDictEqual(child.attrib, {'a': 'b'})
self.assertListEqual(list(child), []) self.assertListEqual(list(child), [])
xmlc_par([xmln('t2', {1: 2}, [])]) xmlc_par([xmln('t2', {1: 2}, []),])
child = parent.iter('t2').__next__() child = parent.iter('t2').__next__()
self.assertDictEqual(child.attrib, {'1': '2'}) self.assertDictEqual(child.attrib, {'1': '2'})
xmlc_par([xmln('tchildren', {}, [xmln('subchild', {}, [])])]) xmlc_par([xmln('tchildren', {}, [xmln('subchild', {}, []),]),])
child = parent.iter('tchildren').__next__() child = parent.iter('tchildren').__next__()
subchildren = list(child) subchildren = list(child)
self.assertEqual(len(subchildren), 1) self.assertEqual(len(subchildren), 1)
self.assertEqual(subchildren[0].tag, 'subchild') self.assertEqual(subchildren[0].tag, 'subchild')
def test_xmlroot(self): def test_xmlroot(self):
""" Test xmlroot """ """ Test xmlroot """
root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []}) root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
@ -96,10 +138,10 @@ class TestXMLBase(unittest.TestCase):
xmlroot({'tag': 'root'}) xmlroot({'tag': 'root'})
def test_xml_write(self): def test_xml_write(self):
""" test xml_write """ """ test xml_write """
children = [xmln('child1', {'attr': 'value'}, []), children = [xmln('child1', {'attr': 'value'}, []), xmln('child2', {}, "Some text")]
xmln('child2', {}, "Some text")]
tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children}) tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children})
xmlw = lambda p: xml_write(p, tree) xmlw = lambda p: xml_write(p, tree)
@ -125,5 +167,6 @@ class TestXMLBase(unittest.TestCase):
self.assertIn('<child2>Some text</child2>', output_xml) self.assertIn('<child2>Some text</child2>', output_xml)
unlink(filepath) unlink(filepath)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()