Source code for javaproperties.xmlprops

from typing import (
    AnyStr,
    BinaryIO,
    Callable,
    Dict,
    IO,
    Iterable,
    Iterator,
    Mapping,
    Optional,
    Tuple,
    Type,
    TypeVar,
    Union,
    overload,
)
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape, quoteattr
from .util import itemize

T = TypeVar("T")


@overload
def load_xml(fp: IO) -> Dict[str, str]:
    ...


@overload
def load_xml(fp: IO, object_pairs_hook: Type[T]) -> T:
    ...


@overload
def load_xml(fp: IO, object_pairs_hook: Callable[[Iterator[Tuple[str, str]]], T]) -> T:
    ...


[docs]def load_xml(fp, object_pairs_hook=dict): # type: ignore[no-untyped-def] r""" Parse the contents of the file-like object ``fp`` as an XML properties file and return a `dict` of the key-value pairs. Beyond basic XML well-formedness, `load_xml` only checks that the root element is named "``properties``" and that all of its ``<entry>`` children have ``key`` attributes. No further validation is performed; if any ``<entry>``\s happen to contain nested tags, the behavior is undefined. By default, the key-value pairs extracted from ``fp`` are combined into a `dict` with later occurrences of a key overriding previous occurrences of the same key. To change this behavior, pass a callable as the ``object_pairs_hook`` argument; it will be called with one argument, a generator of ``(key, value)`` pairs representing the key-value entries in ``fp`` (including duplicates) in order of occurrence. `load_xml` will then return the value returned by ``object_pairs_hook``. :param IO fp: the file from which to read the XML properties document :param callable object_pairs_hook: class or function for combining the key-value pairs :rtype: `dict` or the return value of ``object_pairs_hook`` :raises ValueError: if the root of the XML tree is not a ``<properties>`` tag or an ``<entry>`` element is missing a ``key`` attribute """ tree = ET.parse(fp) return object_pairs_hook(_fromXML(tree.getroot()))
@overload def loads_xml(s: AnyStr) -> Dict[str, str]: ... @overload def loads_xml(fp: IO, object_pairs_hook: Type[T]) -> T: ... @overload def loads_xml( s: AnyStr, object_pairs_hook: Callable[[Iterator[Tuple[str, str]]], T] ) -> T: ...
[docs]def loads_xml(s, object_pairs_hook=dict): # type: ignore[no-untyped-def] r""" Parse the contents of the string ``s`` as an XML properties document and return a `dict` of the key-value pairs. Beyond basic XML well-formedness, `loads_xml` only checks that the root element is named "``properties``" and that all of its ``<entry>`` children have ``key`` attributes. No further validation is performed; if any ``<entry>``\s happen to contain nested tags, the behavior is undefined. By default, the key-value pairs extracted from ``s`` are combined into a `dict` with later occurrences of a key overriding previous occurrences of the same key. To change this behavior, pass a callable as the ``object_pairs_hook`` argument; it will be called with one argument, a generator of ``(key, value)`` pairs representing the key-value entries in ``s`` (including duplicates) in order of occurrence. `loads_xml` will then return the value returned by ``object_pairs_hook``. :param Union[str,bytes] s: the string from which to read the XML properties document :param callable object_pairs_hook: class or function for combining the key-value pairs :rtype: `dict` or the return value of ``object_pairs_hook`` :raises ValueError: if the root of the XML tree is not a ``<properties>`` tag or an ``<entry>`` element is missing a ``key`` attribute """ elem = ET.fromstring(s) return object_pairs_hook(_fromXML(elem))
def _fromXML(root: ET.Element) -> Iterator[Tuple[str, str]]: if root.tag != "properties": raise ValueError("XML tree is not rooted at <properties>") for entry in root.findall("entry"): key = entry.get("key") if key is None: raise ValueError('<entry> is missing "key" attribute') yield (key, entry.text or "")
[docs]def dump_xml( props: Union[Mapping[str, str], Iterable[Tuple[str, str]]], fp: BinaryIO, comment: Optional[str] = None, encoding: str = "UTF-8", sort_keys: bool = False, ) -> None: """ Write a series ``props`` of key-value pairs to a binary filehandle ``fp`` in the format of an XML properties file. The file will include both an XML declaration and a doctype declaration. :param props: A mapping or iterable of ``(key, value)`` pairs to write to ``fp``. All keys and values in ``props`` must be `str` values. If ``sort_keys`` is `False`, the entries are output in iteration order. :param BinaryIO fp: a file-like object to write the values of ``props`` to :param Optional[str] comment: if non-`None`, ``comment`` will be output as a ``<comment>`` element before the ``<entry>`` elements :param str encoding: the name of the encoding to use for the XML document (also included in the XML declaration) :param bool sort_keys: if true, the elements of ``props`` are sorted lexicographically by key in the output :return: `None` """ # This gives type errors <https://github.com/python/typeshed/issues/4793>: # fptxt = codecs.lookup(encoding).streamwriter(fp, errors='xmlcharrefreplace') # print('<?xml version="1.0" encoding={0} standalone="no"?>' # .format(quoteattr(encoding)), file=fptxt) # for s in _stream_xml(props, comment, sort_keys): # print(s, file=fptxt) fp.write( '<?xml version="1.0" encoding={0} standalone="no"?>\n'.format( quoteattr(encoding) ).encode(encoding, "xmlcharrefreplace") ) for s in _stream_xml(props, comment, sort_keys): fp.write((s + "\n").encode(encoding, "xmlcharrefreplace"))
[docs]def dumps_xml( props: Union[Mapping[str, str], Iterable[Tuple[str, str]]], comment: Optional[str] = None, sort_keys: bool = False, ) -> str: """ Convert a series ``props`` of key-value pairs to a `str` containing an XML properties document. The document will include a doctype declaration but not an XML declaration. :param props: A mapping or iterable of ``(key, value)`` pairs to serialize. All keys and values in ``props`` must be `str` values. If ``sort_keys`` is `False`, the entries are output in iteration order. :param Optional[str] comment: if non-`None`, ``comment`` will be output as a ``<comment>`` element before the ``<entry>`` elements :param bool sort_keys: if true, the elements of ``props`` are sorted lexicographically by key in the output :rtype: str """ return "".join(s + "\n" for s in _stream_xml(props, comment, sort_keys))
def _stream_xml( props: Union[Mapping[str, str], Iterable[Tuple[str, str]]], comment: Optional[str] = None, sort_keys: bool = False, ) -> Iterator[str]: yield '<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">' yield "<properties>" if comment is not None: yield "<comment>" + escape(comment) + "</comment>" for k, v in itemize(props, sort_keys=sort_keys): yield "<entry key={0}>{1}</entry>".format(quoteattr(k), escape(v)) yield "</properties>"