Coverage for gws-app/gws/lib/xmlx/tag.py: 85%
71 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""XML builder.
3This module provides a single function ``tag``, which creates an Xml Element from a list of arguments.
5The first argument to this function is interpreted as a tag name
6or a slash separated list of tag names, in which case nested elements are created.
8The remaining ``*args`` are interpreted as follows:
10- a simple string or number value - appended to the text content of the Element
11- an `XmlElement` - appended as a child to the Element
12- a dict - attributes of the Element are updated from this dict
13- a list, tuple or a generator - used as arguments to ``tag`` to create a child tag
15If keyword arguments are given, they are added to the Element's attributes.
17**Example:** ::
19 tag(
20 'geometry/gml:Point',
21 {'gml:id': 'xy'},
22 ['gml:coordinates', '12.345,56.789'],
23 srsName=3857
24 )
26creates the following element: ::
28 <geometry>
29 <gml:Point gml:id="xy" srsName="3857">
30 <gml:coordinates>12.345,56.789</gml:coordinates>
31 </gml:Point>
32 </geometry>
34"""
36import re
38import gws
40from . import element, namespace, error
43def tag(name: str, *args, **kwargs) -> gws.XmlElement:
44 """Build an XML element from arguments."""
46 first = last = None
48 for n in _split_name(name):
49 el = element.XmlElementImpl(n.strip())
50 if not first:
51 first = last = el
52 else:
53 last.append(el)
54 last = el
56 if not first:
57 raise error.BuildError(f'invalid tag name: {name!r}')
59 for arg in args:
60 _add(last, arg)
62 if kwargs:
63 _add(last, kwargs)
65 return first
68##
71def _add(el: gws.XmlElement, arg):
72 if arg is None:
73 return
75 if isinstance(arg, element.XmlElementImpl):
76 el.append(arg)
77 return
79 if isinstance(arg, str):
80 if arg:
81 _add_text(el, arg)
82 return
84 if isinstance(arg, (int, float, bool)):
85 _add_text(el, str(arg).lower())
86 return
88 if isinstance(arg, dict):
89 for k, v in arg.items():
90 if v is not None:
91 el.set(k, v)
92 return
94 if isinstance(arg, (list, tuple)):
95 _add_list(el, arg)
96 return
98 try:
99 ls = list(arg)
100 except Exception as exc:
101 raise error.BuildError(f'invalid argument: in {el.tag!r}, {arg=}') from exc
103 _add_list(el, ls)
106def _add_text(el, s):
107 if not s:
108 return
109 if len(el) == 0:
110 el.text = (el.text or '') + s
111 else:
112 el[-1].tail = (el[-1].tail or '') + s
115def _add_list(el, ls):
116 if not ls:
117 return
118 if isinstance(ls[0], str):
119 _add(el, tag(*ls))
120 return
121 for arg in ls:
122 _add(el, arg)
125def _split_name(name):
126 if '{' not in name:
127 return [s.strip() for s in name.split('/')]
129 parts = []
130 ns = ''
132 for n, s in re.findall(r'({.+?})|([^/{}]+)', name):
133 if n:
134 ns = n
135 else:
136 s = s.strip()
137 if s:
138 parts.append(ns + s)
139 ns = ''
141 return parts