Coverage for gws-app/gws/lib/xmlx/tag.py: 96%
67 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +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('geometry/gml:Point', {'gml:id': 'xy'}, ['gml:coordinates', '12.345,56.789'], srsName=3857)
21creates the following element: ::
23 <geometry>
24 <gml:Point gml:id="xy" srsName="3857">
25 <gml:coordinates>12.345,56.789</gml:coordinates>
26 </gml:Point>
27 </geometry>
29"""
31import re
33import gws
35from . import element, error, util
38def tag(name: str, *args, **kwargs) -> gws.XmlElement:
39 """Build an XML element from arguments."""
41 stack = []
43 for n in _split_name(name):
44 el = element.XmlElement(n)
45 if stack:
46 stack[-1].append(el)
47 stack.append(el)
49 if not stack:
50 raise error.BuildError(f'invalid tag name: {name!r}')
52 for arg in args:
53 _add(stack[-1], arg)
55 if kwargs:
56 _add(stack[-1], kwargs)
58 return stack[0]
61##
64def _add(el: gws.XmlElement, arg):
65 if arg is None:
66 return
68 if hasattr(arg, 'tag'):
69 # mimic ElementTree.iselement
70 el.append(arg)
71 return
73 s, ok = util.atom_to_string(arg)
74 if ok:
75 _add_text(el, s)
76 return
78 if isinstance(arg, dict):
79 for k, v in arg.items():
80 if v is not None:
81 el.set(k, v)
82 return
84 if isinstance(arg, (list, tuple)):
85 _add_list(el, arg)
86 return
88 try:
89 ls = list(arg)
90 except Exception as exc:
91 raise error.BuildError(f'invalid argument: in {el.tag!r}, {arg=}') from exc
93 _add_list(el, ls)
96def _add_text(el, s):
97 if not s:
98 return
99 if len(el) == 0:
100 el.text = (el.text or '') + s
101 else:
102 el[-1].tail = (el[-1].tail or '') + s
105def _add_list(el, ls):
106 if not ls:
107 return
108 if isinstance(ls[0], str):
109 _add(el, tag(*ls))
110 return
111 for arg in ls:
112 _add(el, arg)
115def _split_name(name):
116 if '{' not in name:
117 return [s.strip() for s in name.split('/')]
119 parts = []
120 ns = ''
122 for n, s in re.findall(r'({.+?})|([^/{}]+)', name):
123 if n:
124 ns = n.strip()
125 else:
126 s = s.strip()
127 if s:
128 parts.append(ns + s)
129 ns = ''
131 return parts