diff options
Diffstat (limited to 'Lib/importlib/_adapters.py')
-rw-r--r-- | Lib/importlib/_adapters.py | 108 |
1 files changed, 98 insertions, 10 deletions
diff --git a/Lib/importlib/_adapters.py b/Lib/importlib/_adapters.py index eedde49..9907b14 100644 --- a/Lib/importlib/_adapters.py +++ b/Lib/importlib/_adapters.py @@ -1,4 +1,5 @@ from contextlib import suppress +from io import TextIOWrapper from . import abc @@ -25,32 +26,119 @@ class TraversableResourcesLoader: self.spec = spec def get_resource_reader(self, name): - return DegenerateFiles(self.spec)._native() + return CompatibilityFiles(self.spec)._native() -class DegenerateFiles: +def _io_wrapper(file, mode='r', *args, **kwargs): + if mode == 'r': + return TextIOWrapper(file, *args, **kwargs) + elif mode == 'rb': + return file + raise ValueError( + "Invalid mode value '{}', only 'r' and 'rb' are supported".format(mode) + ) + + +class CompatibilityFiles: """ Adapter for an existing or non-existant resource reader - to provide a degenerate .files(). + to provide a compability .files(). """ - class Path(abc.Traversable): + 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 meaningfull 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_file = exists = is_dir # type: ignore + is_dir = is_file def joinpath(self, other): - return DegenerateFiles.Path() + return CompatibilityFiles.OrphanPath(*self._path, other) + @property def name(self): - return '' + return self._path[-1] - def open(self): - raise ValueError() + def open(self, mode='r', *args, **kwargs): + raise FileNotFoundError("Can't open orphan path") def __init__(self, spec): self.spec = spec @@ -71,7 +159,7 @@ class DegenerateFiles: return getattr(self._reader, attr) def files(self): - return DegenerateFiles.Path() + return CompatibilityFiles.SpecPath(self.spec, self._reader) def wrap_spec(package): |