Fri 17 April 2020 | -- (permalink)
More silly Python tricks.
If you've done much work with XML in Python, you probably know about
the lxml.etree
toolkit, an excellent Pythonic API on top of
the libxml2
library. For most purposes, this is by far the best set
of tools for dealing with XML in Python (or, really, any language).
Even lxml.etree
, however, falters at the task of composing complex
XML, eg, the XML representation of YANG data, where .95 of the XML
text is hideous boilerplate in a dozen different namespaces and the
part you care about is a small number of values that you need to
insert somewhere deep in this rat's nest. One can of course
construct such things using lxml.etree.Element()
and so forth, but
the result is unreadable, and, more to the point, very difficult to
compare with the generated XML, reference texts, and so forth.
So one usually ends up using some kind of templating package, such as
jinja2
, which is a perfectly reasonable solution as such
things go but is rather weak from the standpoint of all the things
that lxml.etree
is good at doing, like schema validation.
The solution is to combine the two approaches: use jinja2
to
generate a text version of the desired XML, then immediately feed the
result into lxml.etree
for RelaxNG validation and any further
manipulation. For example:
class XMLTemplate:
environment = jinja2.Environment()
def __init__(self, template, schema):
self.template = self.environment.from_string(template)
self.schema = lxml.etree.RelaxNG(file = schema)
def __call__(self, **kwargs):
x = lxml.etree.XML(self.template.render(**kwargs))
self.schema.assertValid(x)
return x
The other unrelated fun Python toy I've been playing with recently is
argcomplete
, a bash-completion extension for Python's
argparse
library. I tried this once before, years ago, but couldn't
get it to work properly. It's still a little tricky in some usage
scenarios, but if one is careful it does the job, and it's very nice
to have a tool that automates integration of one's Python code with
bash's completion mechanism. Mostly it just works, although there are
complex cases where one might need to write a "completer" function.