1

Is it possible to wrap a function of an instance of a built-in class, i.e. add functionality to an already created instance of a built-in class? As a concrete example, when parsing xml files using ElementTree, the Element.find method returns an Element or None, depending on if the Element can be found. However, I would like to raise a KeyError, every time an Element cannot be found, instead of returning None. Applying the solution below (found here), leads to an TypeError: __class__ assignment only supported for heap types or ModuleType subclasses. When trying to monkeypatch the function (not my preferred solution, as the KeyError raising Element shouldn't persist globally, but would be better than nothing), also produces a TypeError: TypeError: can't set attributes of built-in/extension type 'xml.etree.ElementTree.Element'. Is updating an instance with additional class functionality not possible, or am I completely over thinking the problem and there is a way simpler solution?

from typing import Any, Dict, Optional, Text, Union
from xml.etree import ElementTree as et

_str_argument_type = Union[str, Text]


class StrictElement(et.Element):
    @classmethod
    def from_element(cls, element: et.Element) -> "StrictElement":
        assert isinstance(element, et.Element)
        element.__class__ = cls
        assert isinstance(element, StrictElement)
        return element

    @staticmethod
    def find(
        path: _str_argument_type,
        namespaces: Optional[Dict[_str_argument_type, _str_argument_type]] = ...,
    ) -> "StrictElement":
        element = super().find(path, namespaces)
        if element is None:
            raise KeyError(f"unable to find element {path}")
        return StrictElement.from_element(element)

foo = et.parse("some_path") # some xml file with foo element but not foobar element
strict_foo = StrictElement.from_element(foo)
other_strict_foo = strict_foo.find("bar")
strict_foo.find("foobar") # should raise key error and not return None if element isn't found
  • I'd just create a free helper function along the lines of `def strict_find(element, match, namespaces=None): result = element.find(match, namespaces); if not result: raise KeyError(match); return result`. Not the prettiest API, but definitely the simplest. – Thomas Feb 08 '21 at 13:46
  • That's the solution I'm going with right now, but my inner perfectionist is far from satisfied – Ferdinand Schlatt Feb 08 '21 at 13:49
  • I know how you feel :) You might be able to use a custom [`TreeBuilder`](https://docs.python.org/3/library/xml.etree.elementtree.html#treebuilder-objects) to create `StrictElement` objects directly while parsing. – Thomas Feb 08 '21 at 13:53

0 Answers0