[MIG][WIP]Yaltik DSL : XML Base on pure Python

This commit is contained in:
Fabien BOURGEOIS 2020-05-03 12:03:47 +02:00
parent 0a86aa3eab
commit 315f4a82ae
3 changed files with 89 additions and 208 deletions

View File

@ -1,24 +1,3 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x73d2bb80
# Compiled with Coconut version 1.4.3 [Ernest Scribbler]
# Coconut Header: -------------------------------------------------------------
from __future__ import print_function, absolute_import, unicode_literals, division
import sys as _coconut_sys, os.path as _coconut_os_path
_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__")
if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path:
del _coconut_sys.modules[b"__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_path)
from __coconut__ import *
from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>

View File

@ -1,26 +1,3 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x84c101a5
# Compiled with Coconut version 1.4.3 [Ernest Scribbler]
""" XML Helpers tests """
# Coconut Header: -------------------------------------------------------------
from __future__ import print_function, absolute_import, unicode_literals, division
import sys as _coconut_sys, os.path as _coconut_os_path
_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__")
if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path:
del _coconut_sys.modules[b"__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_path)
from __coconut__ import *
from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2020 Fabien Bourgeois <fabien@yaltik.com>
@ -38,108 +15,105 @@ from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _cocon
# 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
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 xml_base import xmln from xml_base import xmln, xmlroot, xmlchild, xml_write
from xml_base import xmlroot
from xml_base import xmlchild
from xml_base import xml_write
class TestXMLBase(unittest.TestCase): class TestXMLBase(unittest.TestCase):
""" XML Helpers tests """ """ XML Helpers tests """
def test_xmln(self): def test_xmln(self):
# Tags """ Text xmln """
(self.assertEquals)(*(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []})) # Tags
(self.assertEquals)(*((_coconut_partial(getattr, {1: 'tag'}, 2))((xmln)('a tag')), 'a tag')) self.assertDictEqual(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []})
self.assertEqual(xmln('a tag').tag, 'a tag')
# Attrs # Attrs
(self.assertEquals)(*(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'})) self.assertDictEqual(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'})
(self.assertEquals)(*((_coconut_partial(getattr, {1: 'attrs'}, 2))((xmln)(**{'attrs': {'a good': 'one'}})), {'a good': 'one'})) # Childrens
self.assertListEqual(xmln(children=[1, 2, 3]).children, [1, 2, 3])
# Childrens self.assertListEqual(xmln(children='Some text').children, ['Some text'])
attrs = {'children': [1, 2, 3]}
(self.assertTrue)(((_coconut_partial(getattr, {1: 'children'}, 2))((xmln)(**attrs)) == [1, 2, 3]))
attrs = {'children': 'Some text'}
(self.assertTrue)(((_coconut_partial(getattr, {1: 'children'}, 2))((xmln)(**attrs)) == ['Some text']))
with self.assertRaisesRegexp(TypeError, 'Invalid arguments'): with self.assertRaisesRegexp(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)
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)
def test_xmlchild(self): def test_xmlchild(self):
parent = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []}) """ Test xmlchild """
xmlc_par = (_coconut.functools.partial(_coconut.functools.partial, xmlchild))(parent) parent = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
xmlc_par = partial(xmlchild, parent)
# Bad arguments # Bad arguments
with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'): with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'):
(xmlc_par)(False) xmlc_par(False)
# Need XMLDictElement, not dict # Need XMLDictElement, not dict
with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'): with self.assertRaisesRegexp(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.assertEquals)(parent.text, 'some text') self.assertEqual(parent.text, 'some text')
(xmlc_par)([(xmln)(**{'tag': 't', 'attrs': {'a': 'b'}, 'children': []})]) xmlc_par([xmln('t', {'a': 'b'}, [])])
child = (next)((parent.iter)('t')) child = parent.iter('t').next()
(self.assertEquals)(child.tag, 't') self.assertEqual(child.tag, 't')
(self.assertEquals)(child.attrib, {'a': 'b'}) self.assertDictEqual(child.attrib, {'a': 'b'})
(self.assertEquals)(((list)(child)), []) self.assertListEqual(list(child), [])
(xmlc_par)([(xmln)(**{'tag': 't2', 'attrs': {1: 2}, 'children': []})]) xmlc_par([xmln('t2', {1: 2}, [])])
child = (next)((parent.iter)('t2')) child = parent.iter('t2').next()
(self.assertEquals)(child.attrib, {'1': '2'}) self.assertDictEqual(child.attrib, {'1': '2'})
(xmlc_par)([(xmln)(**{'tag': 'tchildren', 'attrs': {}, 'children': [(xmln)(**{'tag': 'subchild', 'attrs': {}, 'children': []})]})]) xmlc_par([xmln('tchildren', {}, [xmln('subchild', {}, [])])])
child = (next)((parent.iter)('tchildren')) child = parent.iter('tchildren').next()
subchildren = ((list)(child)) subchildren = list(child)
(self.assertEquals)(((len)(subchildren)), 1) self.assertEqual(len(subchildren), 1)
(self.assertEquals)(subchildren[0].tag, 'subchild') self.assertEqual(subchildren[0].tag, 'subchild')
def test_xmlroot(self): def test_xmlroot(self):
root = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []}) """ Test xmlroot """
(self.assertTrue)((isinstance)(*(root, ET.Element))) root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
self.assertIsInstance(root, ET.Element)
with self.assertRaisesRegexp(TypeError, 'has no attribute'): with self.assertRaisesRegexp(TypeError, 'has no attribute'):
(xmlroot)(False) xmlroot(False)
with self.assertRaisesRegexp(KeyError, 'tag'): with self.assertRaisesRegexp(KeyError, 'tag'):
(xmlroot)({}) xmlroot({})
with self.assertRaisesRegexp(KeyError, 'attrs'): with self.assertRaisesRegexp(KeyError, 'attrs'):
(xmlroot)({'tag': 'root'}) xmlroot({'tag': 'root'})
def test_xml_write(self): def test_xml_write(self):
children = [(xmln)(*('child1', {'attr': 'value'}, [])), (xmln)(*('child2', {}, "Some text"))] """ test xml_write """
children = [xmln('child1', {'attr': 'value'}, []),
xmln('child2', {}, "Some text")]
tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children}) tree = xmlroot({'tag': 'root', 'attrs': {}, 'children': children})
xmlw = _coconut_partial(xml_write, {1: tree}, 2) xmlw = lambda p: xml_write(p, tree)
(self.assertEquals)(((xmlw)('/badpath')), None) self.assertIsNone(xmlw('/badpath'))
(self.assertEquals)(((xmlw)('/bad.ext')), None) self.assertIsNone(xmlw('/bad.ext'))
xmlw(__file__) xmlw(__file__)
filepath = __file__.replace('.py', '_views.xml') filepath = __file__.replace('.py', '_views.xml')
with open(filepath, 'r') as output_file: with open(filepath, 'r') as output_file:
output_xml = output_file.read() output_xml = output_file.read()
(self.assertIn)('<?xml version', output_xml) self.assertIn('<?xml version', output_xml)
(self.assertIn)('<root>', output_xml) self.assertIn('<root>', output_xml)
(self.assertIn)('<child1 attr="value"/>', output_xml) self.assertIn('<child1 attr="value"/>', output_xml)
(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__':

View File

@ -1,26 +1,3 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# __coconut_hash__ = 0x178765c5
# Compiled with Coconut version 1.4.3 [Ernest Scribbler]
""" XML helpers and macros """
# Coconut Header: -------------------------------------------------------------
from __future__ import print_function, absolute_import, unicode_literals, division
import sys as _coconut_sys, os.path as _coconut_os_path
_coconut_file_path = _coconut_os_path.dirname(_coconut_os_path.abspath(__file__))
_coconut_cached_module = _coconut_sys.modules.get(b"__coconut__")
if _coconut_cached_module is not None and _coconut_os_path.dirname(_coconut_cached_module.__file__) != _coconut_file_path:
del _coconut_sys.modules[b"__coconut__"]
_coconut_sys.path.insert(0, _coconut_file_path)
from __coconut__ import *
from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_back_pipe, _coconut_star_pipe, _coconut_back_star_pipe, _coconut_dubstar_pipe, _coconut_back_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_mark_as_match
# Compiled Coconut: -----------------------------------------------------------
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com> # Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
@ -38,107 +15,58 @@ from __coconut__ import _coconut, _coconut_MatchError, _coconut_igetitem, _cocon
# 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
from xml.dom import minidom from xml.dom import minidom
from typing import Dict from collections import namedtuple
from typing import List from functools import partial
from typing import Union
from typing import Text XMLDictElement = namedtuple('XMLDictElement', ['tag', 'attrs', 'children'])
from typing import Any
# TODO: fix MyPy / typing def xmlroot(tree):
class XMLDictElement(_coconut.typing.NamedTuple("XMLDictElement", [("tag", 'Text'), ("attrs", 'XMLAttrs'), ("children", 'List[Any]')]), _coconut.object):
__slots__ = ()
__ne__ = _coconut.object.__ne__
def __eq__(self, other):
return self.__class__ is other.__class__ and _coconut.tuple.__eq__(self, other)
def __hash__(self):
return _coconut.tuple.__hash__(self) ^ hash(self.__class__)
XMLAttrs = Dict[Text, Text]
XMLChild = Union[XMLDictElement, Text, List]
def xmlroot(tree # type: Dict[str, Any]
):
# type: (...) -> ET.Element
""" Special process for root XML Node """ """ Special process for root XML Node """
rootel = (ET.Element)(*(tree['tag'], tree['attrs'])) rootel = ET.Element(tree['tag'], tree['attrs'])
if 'children' in tree: if 'children' in tree:
(xmlchild)(*(rootel, tree['children'])) xmlchild(rootel, tree['children'])
return rootel return rootel
def xmlchild(parent, # type: ET.Element def xmlchild(parent, children):
children # type: XMLDictElement
):
# type: (...) -> None
""" 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) """
_coconut_match_to = children if isinstance(children, str):
_coconut_case_check_0 = False
if _coconut.isinstance(_coconut_match_to, Text):
_coconut_case_check_0 = True
if _coconut_case_check_0:
parent.text = children parent.text = children
if not _coconut_case_check_0: elif isinstance(children, XMLDictElement):
if _coconut.isinstance(_coconut_match_to, XMLDictElement): attrs = {unicode(k): unicode(v) for [k, v] in children.attrs.items()}
_coconut_case_check_0 = True new_parent = ET.SubElement(parent, children.tag, attrs)
if _coconut_case_check_0: subchildren = children.children
attrs = dict(((unicode(k)), (unicode(v))) for [k, v] in children.attrs.items()) if subchildren:
new_parent = (ET.SubElement)(*(parent, children.tag, attrs)) xmlchild(new_parent, subchildren)
subchildren = children.children elif isinstance(children, list):
if subchildren: map(partial(xmlchild, parent), children)
(xmlchild)(*(new_parent, subchildren)) else:
if not _coconut_case_check_0:
if _coconut.isinstance(_coconut_match_to, List):
_coconut_case_check_0 = True
if _coconut_case_check_0:
(consume)((map)(*(((_coconut.functools.partial(_coconut.functools.partial, xmlchild))(parent)), children)))
if not _coconut_case_check_0:
raise TypeError('Invalid arguments for xmlchild') raise TypeError('Invalid arguments for xmlchild')
def xmln(tag='', # type: Text def xmln(tag='', attrs={}, children=[]):
attrs={}, # type: XMLAttrs
children=[] # type: Union[Text, List]
):
# type: (...) -> XMLDictElement
""" XMLDictElement building from dict object, with defaults """ """ XMLDictElement building from dict object, with defaults """
_coconut_match_to = attrs if isinstance(attrs, list):
_coconut_case_check_1 = False
if _coconut.isinstance(_coconut_match_to, list):
_coconut_case_check_1 = True
if _coconut_case_check_1:
children = attrs children = attrs
attrs = {} attrs = {}
xmldictel = (_coconut.functools.partial(_coconut.functools.partial, XMLDictElement))(*(tag, attrs)) xmldictel = partial(XMLDictElement, tag, attrs)
_coconut_match_to = children if isinstance(children, str):
_coconut_case_check_2 = False return xmldictel([children])
if _coconut.isinstance(_coconut_match_to, Text): if isinstance(children, list):
c = _coconut_match_to return xmldictel(children)
_coconut_case_check_2 = True raise TypeError('Invalid arguments for xmln')
if _coconut_case_check_2:
return (xmldictel)([c])
if not _coconut_case_check_2:
if _coconut.isinstance(_coconut_match_to, list):
c = _coconut_match_to
_coconut_case_check_2 = True
if _coconut_case_check_2:
return (xmldictel)(c)
if not _coconut_case_check_2:
raise TypeError('Invalid arguments for xmln')
def xml_write(filepath, tree): def xml_write(filepath, tree):
""" 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
output_xml = ((minidom.parseString)((ET.tostring)(tree))).toprettyxml(indent=' ') output_xml = minidom.parseString(ET.tostring(tree)).toprettyxml(indent=' ')
output_path = (path.dirname)((path.abspath)(filepath)) output_path = path.dirname(path.abspath(filepath))
fpath = (('/'.join)([output_path, (path.basename)(filepath)])).replace('.py', '_views.xml') fpath = u'/'.join([output_path, path.basename(filepath)]).replace('.py', '_views.xml')
with open(fpath, 'w') as output_file: with open(fpath, 'w') as output_file:
output_file.write(output_xml) output_file.write(output_xml)