from contextlib import suppress from . import abc class SpecLoaderAdapter: """ Adapt a package spec to adapt the underlying loader. """ def __init__(self, spec, adapter=lambda spec: spec.loader): self.spec = spec self.loader = adapter(spec) def __getattr__(self, name): return getattr(self.spec, name) class TraversableResourcesLoader: """ Adapt a loader to provide TraversableResources. """ def __init__(self, spec): self.spec = spec def get_resource_reader(self, name): return DegenerateFiles(self.spec)._native() class DegenerateFiles: """ Adapter for an existing or non-existant resource reader to provide a degenerate .files(). """ class Path(abc.Traversable): def iterdir(self): return iter(()) def is_dir(self): return False is_file = exists = is_dir # type: ignore def joinpath(self, other): return DegenerateFiles.Path() def name(self): return '' def open(self): raise ValueError() def __init__(self, spec): self.spec = spec @property def _reader(self): with suppress(AttributeError): return self.spec.loader.get_resource_reader(self.spec.name) def _native(self): """ Return the native reader if it supports files(). """ reader = self._reader return reader if hasattr(reader, 'files') else self def __getattr__(self, attr): return getattr(self._reader, attr) def files(self): return DegenerateFiles.Path() def wrap_spec(package): """ Construct a package spec with traversable compatibility on the spec/loader/reader. """ return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)