123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- from contextlib import suppress
- from io import TextIOWrapper
- 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 CompatibilityFiles(self.spec)._native()
- def _io_wrapper(file, mode='r', *args, **kwargs):
- if mode == 'r':
- return TextIOWrapper(file, *args, **kwargs)
- elif mode == 'rb':
- return file
- raise ValueError(f"Invalid mode value '{mode}', only 'r' and 'rb' are supported")
- class CompatibilityFiles:
- """
- Adapter for an existing or non-existent resource reader
- to provide a compatibility .files().
- """
- class SpecPath(abc.Traversable):
- """
- Path tied to a module spec.
- Can be read and exposes the resource reader children.
- """
- def __init__(self, spec, reader):
- self._spec = spec
- self._reader = reader
- def iterdir(self):
- if not self._reader:
- return iter(())
- return iter(
- CompatibilityFiles.ChildPath(self._reader, path)
- for path in self._reader.contents()
- )
- def is_file(self):
- return False
- is_dir = is_file
- def joinpath(self, other):
- if not self._reader:
- return CompatibilityFiles.OrphanPath(other)
- return CompatibilityFiles.ChildPath(self._reader, other)
- @property
- def name(self):
- return self._spec.name
- def open(self, mode='r', *args, **kwargs):
- return _io_wrapper(self._reader.open_resource(None), mode, *args, **kwargs)
- class ChildPath(abc.Traversable):
- """
- Path tied to a resource reader child.
- Can be read but doesn't expose any meaningful children.
- """
- def __init__(self, reader, name):
- self._reader = reader
- self._name = name
- def iterdir(self):
- return iter(())
- def is_file(self):
- return self._reader.is_resource(self.name)
- def is_dir(self):
- return not self.is_file()
- def joinpath(self, other):
- return CompatibilityFiles.OrphanPath(self.name, other)
- @property
- def name(self):
- return self._name
- def open(self, mode='r', *args, **kwargs):
- return _io_wrapper(
- self._reader.open_resource(self.name), mode, *args, **kwargs
- )
- class OrphanPath(abc.Traversable):
- """
- Orphan path, not tied to a module spec or resource reader.
- Can't be read and doesn't expose any meaningful children.
- """
- def __init__(self, *path_parts):
- if len(path_parts) < 1:
- raise ValueError('Need at least one path part to construct a path')
- self._path = path_parts
- def iterdir(self):
- return iter(())
- def is_file(self):
- return False
- is_dir = is_file
- def joinpath(self, other):
- return CompatibilityFiles.OrphanPath(*self._path, other)
- @property
- def name(self):
- return self._path[-1]
- def open(self, mode='r', *args, **kwargs):
- raise FileNotFoundError("Can't open orphan path")
- 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 CompatibilityFiles.SpecPath(self.spec, self._reader)
- def wrap_spec(package):
- """
- Construct a package spec with traversable compatibility
- on the spec/loader/reader.
- """
- return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
|