"I'm not proud of being a congenital pain in the ass. But I will take money for it."

Python XML and Bash completion

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.