1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
from __future__ import absolute_import
from copy import copy
from xml.sax.saxutils import escape
import six
class X(object):
"""
Class to construct and print XML nodes in python syntax. Here is an
example of use::
X("foo", bar="baz").children(
X("qux", pouet="kanasson"),
"bar<>",
)
will render::
<foo bar="baz">
<qux pouet="kanasson" />
bar<>
</foo>
"""
def __init__(self, tag_name, *args, **kwargs):
"""
Construct a new node
:param string tag_name: the name of the XML tag
:param kwargs: attributes of the tag
:type kwargs: dict[string, string]
"""
self.tag_name = tag_name
self.kws = {k: w for k, w in kwargs.items() if w is not None}
self._children = list(args)
def children(self, *args):
"""
Returns a new version of self with args as the list of children to
the node.
If passed only one argument that is an iterable, will add the
content of it to X. Else, will add every passed arg to X.
:rtype: X
"""
c = copy(self)
if len(args) == 1 and not isinstance(args[0], six.binary_type):
try:
c._children = c._children + list(args[0])
except TypeError:
c._children = c._children + list(args)
else:
c._children = c._children + list(args)
return c
def params(self, **kwargs):
"""
Returns a new version of self with kwargs as new parameters to the
node
:param kwargs: The parameters to add
:type kwargs: dict[string, string]
:rtype: X
"""
c = copy(self)
c.kws = c.kws.copy()
c.kws.update({k: w for k, w in kwargs.items() if w is not None})
return c
@staticmethod
def child_to_str(child):
"""
Returns the seriazilation of `child`. Handle both XML nodes and string
ones.
"""
return escape(child) if isinstance(child, six.binary_type) else str(child)
def __str__(self):
"""
String representation of self. Used to get the XML repr
:rtype: string
"""
kws = " ".join('{0}="{1}"'.format(k, v) for k, v in self.kws.items())
if self._children:
return "<{0} {1}>{2}</{0}>".format(
self.tag_name, kws,
"\n".join(self.child_to_str(c) for c in self._children)
)
else:
return "<{0} {1} />".format(self.tag_name, kws)
def with_header(self):
"""
String representation of self with xml header added
:rtype: string
"""
return '<?xml version="1.0"?>\n' + self.__str__()
|