diff options
Diffstat (limited to 'bsie/reader/chain.py')
-rw-r--r-- | bsie/reader/chain.py | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/bsie/reader/chain.py b/bsie/reader/chain.py new file mode 100644 index 0000000..5e9e0d5 --- /dev/null +++ b/bsie/reader/chain.py @@ -0,0 +1,88 @@ +""" + +Part of the bsie module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import logging +import typing + +# bsie imports +from bsie.utils import bsfs, errors + +# inner-module imports +from . import base +from . import builder + +# exports +__all__: typing.Sequence[str] = ( + 'ReaderChain', + ) + + +## code ## + +logger = logging.getLogger(__name__) + +# Content type. +T_CONTENT = typing.TypeVar('T_CONTENT') # pylint: disable=invalid-name + +class ReaderChain(base.Reader, typing.Generic[T_CONTENT]): + """Read an image.""" + + # sub-readers for specific file formats. + _children: typing.Tuple[base.Reader, ...] + + def __init__( + self, + subreader_names: typing.Iterable[str], + cfg: typing.Optional[typing.Any] = None, + ): + rbuild = builder.ReaderBuilder(cfg) + children = [] + for name in subreader_names: + try: + # build sub-reader + children.append(rbuild.build(name)) + except (ValueError, + TypeError, + errors.LoaderError, + errors.BuilderError) as err: + # failed to build a child; skip and notify + logger.warning('failed to load reader: %s', err) + + if len(children) == 0: + logger.warning('%s failed to load any sub-readers.', bsfs.typename(self)) + + # copy children to member + self._children = tuple(children) + + def __str__(self) -> str: + substr = ', '.join(str(child) for child in self._children) + return f'{bsfs.typename(self)}({substr})' + + def __repr__(self) -> str: + return f'{bsfs.typename(self)}({self._children})' + + def __eq__(self, other: typing.Any) -> bool: + return super().__eq__(other) \ + and self._children == other._children + + def __hash__(self) -> int: + return hash((super().__hash__(), self._children)) + + def __call__(self, path: str) -> T_CONTENT: + raise_error = errors.UnsupportedFileFormatError + for child in self._children: + try: + return child(path) + except errors.UnsupportedFileFormatError: + pass + except errors.ReaderError: + # child cannot read the file, skip. + raise_error = errors.ReaderError # type: ignore [assignment] # mypy is confused + + raise raise_error(path) + +## EOF ## |