Skip to content

xmlchemy

xmlchemy

Enabling declarative definition of lxml custom element classes.

XmlString

Bases: str

Provides string comparison override suitable for serialized XML that is useful for tests.

MetaOxmlElement

MetaOxmlElement(
    clsname: str,
    bases: tuple[type, ...],
    namespace: dict[str, Any],
)

Bases: type

Metaclass for BaseOxmlElement.

Source code in src/docx/oxml/xmlchemy.py
def __init__(cls, clsname: str, bases: tuple[type, ...], namespace: dict[str, Any]):
    dispatchable = (
        OneAndOnlyOne,
        OneOrMore,
        OptionalAttribute,
        RequiredAttribute,
        ZeroOrMore,
        ZeroOrOne,
        ZeroOrOneChoice,
    )
    for key, value in namespace.items():
        if isinstance(value, dispatchable):
            value.populate_class_members(cls, key)

BaseAttribute

BaseAttribute(
    attr_name: str,
    simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType],
)

Base class for OptionalAttribute and RequiredAttribute.

Provides common methods.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, attr_name: str, simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType]):
    super(BaseAttribute, self).__init__()
    self._attr_name = attr_name
    self._simple_type = simple_type

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    self._element_cls = element_cls
    self._prop_name = prop_name

    self._add_attr_property()

OptionalAttribute

OptionalAttribute(
    attr_name: str,
    simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType],
    default: BaseXmlEnum
    | BaseSimpleType
    | str
    | bool
    | None = None,
)

Bases: BaseAttribute

Defines an optional attribute on a custom element class.

An optional attribute returns a default value when not present for reading. When assigned None, the attribute is removed, but still returns the default value when one is specified.

Source code in src/docx/oxml/xmlchemy.py
def __init__(
    self,
    attr_name: str,
    simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType],
    default: BaseXmlEnum | BaseSimpleType | str | bool | None = None,
):
    super(OptionalAttribute, self).__init__(attr_name, simple_type)
    self._default = default

RequiredAttribute

RequiredAttribute(
    attr_name: str,
    simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType],
)

Bases: BaseAttribute

Defines a required attribute on a custom element class.

A required attribute is assumed to be present for reading, so does not have a default value; its actual value is always used. If missing on read, an InvalidXmlError is raised. It also does not remove the attribute if None is assigned. Assigning None raises TypeError or ValueError, depending on the simple type of the attribute.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, attr_name: str, simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType]):
    super(BaseAttribute, self).__init__()
    self._attr_name = attr_name
    self._simple_type = simple_type

_BaseChildElement

_BaseChildElement(
    nsptagname: str, successors: tuple[str, ...] = ()
)

Base class for the child-element classes.

The child-element sub-classes correspond to varying cardinalities, such as ZeroOrOne and ZeroOrMore.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()):
    super(_BaseChildElement, self).__init__()
    self._nsptagname = nsptagname
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Baseline behavior for adding the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Baseline behavior for adding the appropriate methods to `element_cls`."""
    self._element_cls = element_cls
    self._prop_name = prop_name

Choice

Choice(nsptagname: str, successors: tuple[str, ...] = ())

Bases: _BaseChildElement

Defines a child element belonging to a group, only one of which may appear as a child.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()):
    super(_BaseChildElement, self).__init__()
    self._nsptagname = nsptagname
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement,
    group_prop_name: str,
    successors: tuple[str, ...],
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(  # pyright: ignore[reportIncompatibleMethodOverride]
    self,
    element_cls: MetaOxmlElement,
    group_prop_name: str,
    successors: tuple[str, ...],
) -> None:
    """Add the appropriate methods to `element_cls`."""
    self._element_cls = element_cls
    self._group_prop_name = group_prop_name
    self._successors = successors

    self._add_getter()
    self._add_creator()
    self._add_inserter()
    self._add_adder()
    self._add_get_or_change_to_method()

OneAndOnlyOne

OneAndOnlyOne(nsptagname: str)

Bases: _BaseChildElement

Defines a required child element for MetaOxmlElement.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str):
    super(OneAndOnlyOne, self).__init__(nsptagname, ())

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    super(OneAndOnlyOne, self).populate_class_members(element_cls, prop_name)
    self._add_getter()

OneOrMore

OneOrMore(
    nsptagname: str, successors: tuple[str, ...] = ()
)

Bases: _BaseChildElement

Defines a repeating child element for MetaOxmlElement that must appear at least once.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()):
    super(_BaseChildElement, self).__init__()
    self._nsptagname = nsptagname
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    super(OneOrMore, self).populate_class_members(element_cls, prop_name)
    self._add_list_getter()
    self._add_creator()
    self._add_inserter()
    self._add_adder()
    self._add_public_adder()
    delattr(element_cls, prop_name)

ZeroOrMore

ZeroOrMore(
    nsptagname: str, successors: tuple[str, ...] = ()
)

Bases: _BaseChildElement

Defines an optional repeating child element for MetaOxmlElement.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()):
    super(_BaseChildElement, self).__init__()
    self._nsptagname = nsptagname
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    super(ZeroOrMore, self).populate_class_members(element_cls, prop_name)
    self._add_list_getter()
    self._add_creator()
    self._add_inserter()
    self._add_adder()
    self._add_public_adder()
    delattr(element_cls, prop_name)

ZeroOrOne

ZeroOrOne(
    nsptagname: str, successors: tuple[str, ...] = ()
)

Bases: _BaseChildElement

Defines an optional child element for MetaOxmlElement.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()):
    super(_BaseChildElement, self).__init__()
    self._nsptagname = nsptagname
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    super(ZeroOrOne, self).populate_class_members(element_cls, prop_name)
    self._add_getter()
    self._add_creator()
    self._add_inserter()
    self._add_adder()
    self._add_get_or_adder()
    self._add_remover()

ZeroOrOneChoice

ZeroOrOneChoice(
    choices: Sequence[Choice],
    successors: tuple[str, ...] = (),
)

Bases: _BaseChildElement

Correspondes to an EG_* element group where at most one of its members may appear as a child.

Source code in src/docx/oxml/xmlchemy.py
def __init__(self, choices: Sequence[Choice], successors: tuple[str, ...] = ()):
    self._choices = choices
    self._successors = successors

populate_class_members

populate_class_members(
    element_cls: MetaOxmlElement, prop_name: str
) -> None

Add the appropriate methods to element_cls.

Source code in src/docx/oxml/xmlchemy.py
def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None:
    """Add the appropriate methods to `element_cls`."""
    super(ZeroOrOneChoice, self).populate_class_members(element_cls, prop_name)
    self._add_choice_getter()
    for choice in self._choices:
        choice.populate_class_members(element_cls, self._prop_name, self._successors)
    self._add_group_remover()

BaseOxmlElement

Bases: ElementBase

Effective base class for all custom element classes.

Adds standardized behavior to all classes in one place.

xml property

xml: str

XML string for this element, suitable for testing purposes.

Pretty printed for readability and without an XML declaration at the top.

first_child_found_in

first_child_found_in(*tagnames: str) -> _Element | None

First child with tag in tagnames, or None if not found.

Source code in src/docx/oxml/xmlchemy.py
def first_child_found_in(self, *tagnames: str) -> _Element | None:
    """First child with tag in `tagnames`, or None if not found."""
    for tagname in tagnames:
        child = self.find(qn(tagname))
        if child is not None:
            return child
    return None

remove_all

remove_all(*tagnames: str) -> None

Remove child elements with tagname (e.g. "a:p") in tagnames.

Source code in src/docx/oxml/xmlchemy.py
def remove_all(self, *tagnames: str) -> None:
    """Remove child elements with tagname (e.g. "a:p") in `tagnames`."""
    for tagname in tagnames:
        matching = self.findall(qn(tagname))
        for child in matching:
            self.remove(child)

xpath

xpath(
    xpath_str: str,
    namespaces: Dict[str, str] | None = None,
    **variables: Any,
) -> Any

Override of lxml _Element.xpath() method.

Provides standard Open XML namespace mapping (nsmap) in centralized location.

namespaces adds prefixes not in the standard mapping, which is needed to query elements from vendor or custom namespaces. Entries override the standard mapping where the prefixes collide.

variables binds values to XPath variables, avoiding unsafe string interpolation for user-supplied values.

Source code in src/docx/oxml/xmlchemy.py
def xpath(  # pyright: ignore[reportIncompatibleMethodOverride]
    self,
    xpath_str: str,
    namespaces: Dict[str, str] | None = None,
    **variables: Any,
) -> Any:
    """Override of `lxml` _Element.xpath() method.

    Provides standard Open XML namespace mapping (`nsmap`) in centralized location.

    `namespaces` adds prefixes not in the standard mapping, which is needed to
    query elements from vendor or custom namespaces. Entries override the standard
    mapping where the prefixes collide.

    `variables` binds values to XPath variables, avoiding unsafe string
    interpolation for user-supplied values.
    """
    namespace_map = nsmap if namespaces is None else {**nsmap, **namespaces}
    return super().xpath(xpath_str, namespaces=namespace_map, **variables)

serialize_for_reading

serialize_for_reading(element: ElementBase)

Serialize element to human-readable XML suitable for tests.

No XML declaration.

Source code in src/docx/oxml/xmlchemy.py
def serialize_for_reading(element: ElementBase):
    """Serialize `element` to human-readable XML suitable for tests.

    No XML declaration.
    """
    xml = etree.tostring(element, encoding="unicode", pretty_print=True)
    return XmlString(xml)