| | 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 |
| |
|
| | def joinpath(self, other): |
| | return DegenerateFiles.Path() |
| |
|
| | @property |
| | def name(self): |
| | return '' |
| |
|
| | def open(self, mode='rb', *args, **kwargs): |
| | 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) |
| |
|