[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 -*-
# 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 -*-
#
# 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
# 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 xml_base import xmln
from xml_base import xmlroot
from xml_base import xmlchild
from xml_base import xml_write
from xml_base import xmln, xmlroot, xmlchild, xml_write
class TestXMLBase(unittest.TestCase):
""" XML Helpers tests """
def test_xmln(self):
""" Text xmln """
# Tags
(self.assertEquals)(*(xmln()._asdict(), {'tag': '', 'attrs': {}, 'children': []}))
(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
(self.assertEquals)(*(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'}))
self.assertDictEqual(xmln(attrs={'a good': 'one'}).attrs, {'a good': 'one'})
# Childrens
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']))
self.assertListEqual(xmln(children=[1, 2, 3]).children, [1, 2, 3])
self.assertListEqual(xmln(children='Some text').children, ['Some text'])
with self.assertRaisesRegexp(TypeError, 'Invalid arguments'):
(xmln)(**{'children': False})
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)
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):
parent = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []})
xmlc_par = (_coconut.functools.partial(_coconut.functools.partial, xmlchild))(parent)
""" Test xmlchild """
parent = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
xmlc_par = partial(xmlchild, parent)
# Bad arguments
with self.assertRaisesRegexp(TypeError, 'Invalid arguments for xmlchild'):
(xmlc_par)(False)
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([{'tag': 't', 'attrs': {'a': 'b'}, 'children': []}])
(xmlc_par)(['some text'])
(self.assertEquals)(parent.text, 'some text')
xmlc_par(['some text'])
self.assertEqual(parent.text, 'some text')
(xmlc_par)([(xmln)(**{'tag': 't', 'attrs': {'a': 'b'}, 'children': []})])
child = (next)((parent.iter)('t'))
(self.assertEquals)(child.tag, 't')
(self.assertEquals)(child.attrib, {'a': 'b'})
(self.assertEquals)(((list)(child)), [])
xmlc_par([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)(**{'tag': 't2', 'attrs': {1: 2}, 'children': []})])
child = (next)((parent.iter)('t2'))
(self.assertEquals)(child.attrib, {'1': '2'})
xmlc_par([xmln('t2', {1: 2}, [])])
child = parent.iter('t2').next()
self.assertDictEqual(child.attrib, {'1': '2'})
(xmlc_par)([(xmln)(**{'tag': 'tchildren', 'attrs': {}, 'children': [(xmln)(**{'tag': 'subchild', 'attrs': {}, 'children': []})]})])
child = (next)((parent.iter)('tchildren'))
subchildren = ((list)(child))
(self.assertEquals)(((len)(subchildren)), 1)
(self.assertEquals)(subchildren[0].tag, 'subchild')
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):
root = (xmlroot)({'tag': 'root', 'attrs': {}, 'children': []})
(self.assertTrue)((isinstance)(*(root, ET.Element)))
""" Test xmlroot """
root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
self.assertIsInstance(root, ET.Element)
with self.assertRaisesRegexp(TypeError, 'has no attribute'):
(xmlroot)(False)
xmlroot(False)
with self.assertRaisesRegexp(KeyError, 'tag'):
(xmlroot)({})
xmlroot({})
with self.assertRaisesRegexp(KeyError, 'attrs'):
(xmlroot)({'tag': 'root'})
xmlroot({'tag': 'root'})
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})
xmlw = _coconut_partial(xml_write, {1: tree}, 2)
xmlw = lambda p: xml_write(p, tree)
(self.assertEquals)(((xmlw)('/badpath')), None)
(self.assertEquals)(((xmlw)('/bad.ext')), None)
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)
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__':

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 -*-
#
# 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
# 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
from typing import List
from typing import Union
from typing import Text
from typing import Any
from collections import namedtuple
from functools import partial
XMLDictElement = namedtuple('XMLDictElement', ['tag', 'attrs', 'children'])
# TODO: fix MyPy / typing
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
def xmlroot(tree):
""" Special process for root XML Node """
rootel = (ET.Element)(*(tree['tag'], tree['attrs']))
rootel = ET.Element(tree['tag'], tree['attrs'])
if 'children' in tree:
(xmlchild)(*(rootel, tree['children']))
xmlchild(rootel, tree['children'])
return rootel
def xmlchild(parent, # type: ET.Element
children # type: XMLDictElement
):
# type: (...) -> None
def xmlchild(parent, children):
""" Handling of children (ie non root) XML Nodes with/o text and
subchildren (recursive) """
_coconut_match_to = children
_coconut_case_check_0 = False
if _coconut.isinstance(_coconut_match_to, Text):
_coconut_case_check_0 = True
if _coconut_case_check_0:
if isinstance(children, str):
parent.text = children
if not _coconut_case_check_0:
if _coconut.isinstance(_coconut_match_to, XMLDictElement):
_coconut_case_check_0 = True
if _coconut_case_check_0:
attrs = dict(((unicode(k)), (unicode(v))) for [k, v] in children.attrs.items())
new_parent = (ET.SubElement)(*(parent, children.tag, attrs))
elif isinstance(children, XMLDictElement):
attrs = {unicode(k): unicode(v) for [k, v] in children.attrs.items()}
new_parent = ET.SubElement(parent, children.tag, attrs)
subchildren = children.children
if subchildren:
(xmlchild)(*(new_parent, subchildren))
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:
xmlchild(new_parent, subchildren)
elif isinstance(children, list):
map(partial(xmlchild, parent), children)
else:
raise TypeError('Invalid arguments for xmlchild')
def xmln(tag='', # type: Text
attrs={}, # type: XMLAttrs
children=[] # type: Union[Text, List]
):
# type: (...) -> XMLDictElement
def xmln(tag='', attrs={}, children=[]):
""" XMLDictElement building from dict object, with defaults """
_coconut_match_to = attrs
_coconut_case_check_1 = False
if _coconut.isinstance(_coconut_match_to, list):
_coconut_case_check_1 = True
if _coconut_case_check_1:
if isinstance(attrs, list):
children = attrs
attrs = {}
xmldictel = (_coconut.functools.partial(_coconut.functools.partial, XMLDictElement))(*(tag, attrs))
_coconut_match_to = children
_coconut_case_check_2 = False
if _coconut.isinstance(_coconut_match_to, Text):
c = _coconut_match_to
_coconut_case_check_2 = True
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:
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):
""" Write XML file according to filename and given tree """
if (filepath.endswith)('.py'): # if .pyc, no need to generate XML
output_xml = ((minidom.parseString)((ET.tostring)(tree))).toprettyxml(indent=' ')
output_path = (path.dirname)((path.abspath)(filepath))
fpath = (('/'.join)([output_path, (path.basename)(filepath)])).replace('.py', '_views.xml')
if filepath.endswith('.py'): # if .pyc, no need to generate XML
output_xml = minidom.parseString(ET.tostring(tree)).toprettyxml(indent=' ')
output_path = path.dirname(path.abspath(filepath))
fpath = u'/'.join([output_path, path.basename(filepath)]).replace('.py', '_views.xml')
with open(fpath, 'w') as output_file:
output_file.write(output_xml)