import os from contextlib import contextmanager from zipfile import ZipFile def parse_dict(parsers, data): """ Make a generator that yields callables applying parser functions to their matching input data. *parsers* and *data* must both be dictionaries. Parser functions are matched to input data using their dictionary keys. If a parser's key is not present in *data*, it is ignored. A key in *parsers* may map to another dictionary of parsers. In this case, this function will be applied recursively to the matching value in *data*, which is assumed to be a dictionary as well. If a parser is matched to a list type, one callable for each list item is yielded. :param dict parsers: A mapping of parsers. :param dict data: A mapping of data to be parsed. """ for field, parser in parsers.items(): if field not in data: continue value = data[field] if isinstance(value, list): yield from [lambda i=x: parser(i) for x in value] elif isinstance(value, dict): yield from parse_dict(parser, value) else: yield lambda: parser(value) @contextmanager def open_archive_file(archive, member, check_sidecar=True): # pragma: no cover """ Open an archive file for use with the :ref:`with ` statement. Yields a :term:`file object` obtained from: 1. The archive's :ref:`sidecar file `, if it exists and *check_sidecar* is ``True``. 2. Otherwise, the archive itself. :param Archive archive: The archive. :param str member: The name of the file within the archive (or its sidecar suffix). :param bool check_sidecar: Whether to check for the sidecar file. """ if check_sidecar: sidecar = f"{archive.path}.{member}" if os.path.exists(sidecar): with open(sidecar, "r") as file: yield file return with ZipFile(archive.path, "r") as zip: with zip.open(member, "r") as file: yield file