Skip to main content
`Ctrl`+`K`

# Source code for stactools.core.io.xml

    from functools import lru_cache
    from typing import Any, Callable, List, Optional, cast

    from lxml import etree
    from lxml.etree import _Element as lxmlElement
    from stactools.core.io import ReadHrefModifier, read_text

    class XmlElement:
        """Thin wrapper around `lxml <https://lxml.de/>`_ etree.Element with some
        convenience functions."""

        def __init__(self, element: lxmlElement):
            """Creates a new ``XmlElement`` by wrapping an ``lxml.etree._Element``.

            Args:
                element (lxmlElement): The ``lxml`` element to wrap.
            """
            self.element = element

        @lru_cache(maxsize=100)
        def find(self, xpath: str) -> Optional["XmlElement"]:
            """Find a child ``XmlElement`` by xpath.

            Args:
                xpath (str): The xpath to use for search.

            Returns:
                Optional[XmlElement]: The found element, or None if not found.
            """
            node = self.element.find(xpath, self.element.nsmap)
            return None if node is None else XmlElement(node)

        def find_or_throw(
            self, xpath: str, get_exception: Callable[[str], Exception]
        ) -> "XmlElement":
            """Find a child ``XmlElement`` by xpath, or throw an exception if not
            found.

            Args:
                xpath (str): The xpath to use for search.
                get_exception (Callable[[str], Exception]):
                    A callable that builds the exception to be thrown when the xpath
                    does not match any elements.

            Returns:
                XmlElement: The child xml element identified by the xpath.

            Raises:
                Exception: If no element is found.
            """
            result = self.find(xpath)
            if result is None:
                raise get_exception(xpath)
            return result

        @lru_cache(maxsize=100)
        def findall(self, xpath: str) -> List["XmlElement"]:
            """Finds all children that match the given xpath.

            Args:
                xpath (str): The xpath to use for search.

            Returns:
                list[XmlElement]: The found elements.
            """
            return [XmlElement(e) for e in self.element.findall(xpath, self.element.nsmap)]

        @lru_cache(maxsize=100)
        def find_text(self, xpath: str) -> Optional[str]:
            """Finds an element by xpath and returns its contained text.

            Args:
                xpath (str): The xpath to use for search.

            Returns:
                Optional[str]:
                    The text inside of the found element, or None if the
                    element was not found.
            """
            node = self.find(xpath)
            return None if node is None else node.text

        def find_text_or_throw(
            self, xpath: str, get_exception: Callable[[str], Exception]
        ) -> str:
            """Finds an element by xpath and returns its contained text, or throws
            an error if no element is found.

            Args:
                xpath (str): The xpath to use for search.
                get_exception (Callable[[str], Exception]):
                    A callable that builds the exception to be thrown when the xpath
                    does not match any elements.

            Returns:
                Optional[str]:
                    The text inside of the found element, or None if the
                    element was not found.

            Raises:
                Exception: If no element is found.
            """

            result = self.find_text(xpath)
            if result is None:
                raise get_exception(xpath)
            return result

        @lru_cache(maxsize=100)
        def find_attr(self, attr: str, xpath: str) -> Optional[str]:
            """Finds and returns an attribute of an element.

            Args:
                attr (str): The attribute name.
                xpath (str): The xpath of the element to find.

            Returns:
                Optional[str]:
                    The value of the attribute, or None if the element
                    was not found.
            """
            node = self.find(xpath)
            return None if node is None else node.get_attr(attr)

        @property
        def text(self) -> Optional[str]:
            """Returns the text of this element.

            Returns:
                Optional[str]:
                    The text value of this element, decoded as utf-8 if
                    necessary, or None if there is no text.
            """
            if isinstance(self.element.text, str):
                return self.element.text
            elif isinstance(self.element.text, bytes):
                return str(self.element.text, encoding="utf-8")
            else:
                assert self.element.text is None
                return None

        @lru_cache(maxsize=100)
        def get_attr(self, attr: str) -> Optional[str]:
            """Returns the value of a given attribute of this element.

            Args:
                attr (str): The name of the attribute.

            Returns:
                Optional[str]:
                    The value of the attribute, or None if the attribute is not
                    present on this element.
            """
            return cast(Optional[str], self.element.get(attr, None))

        @classmethod
        def from_file(
            cls,
            href: str,
            read_href_modifier: Optional[ReadHrefModifier] = None,
            **kwargs: Any,
        ) -> "XmlElement":
            """Reads an XmlElement from an href.

            Optionally modifies the href with ``read_href_modifier``. Uses
            :py:meth:`stactools.core.io.read_text` to read the text.

            Args:
                href (str): The href to read.
                read_href_modifier (Optional[:class:`stactools.core.io.ReadHrefModifier`]):
                    An optional callable that will be used to modify the href.
                    Defaults to None.

            Returns:
                XmlElement: The read XmlElement.
            """
            text = read_text(href, read_href_modifier, **kwargs)
            return cls(etree.fromstring(bytes(text, encoding="utf-8")))