summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/_adapters.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/importlib/_adapters.py')
-rw-r--r--Lib/importlib/_adapters.py108
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):