• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from contextlib import suppress
2
3from . import abc
4
5
6class SpecLoaderAdapter:
7    """
8    Adapt a package spec to adapt the underlying loader.
9    """
10
11    def __init__(self, spec, adapter=lambda spec: spec.loader):
12        self.spec = spec
13        self.loader = adapter(spec)
14
15    def __getattr__(self, name):
16        return getattr(self.spec, name)
17
18
19class TraversableResourcesLoader:
20    """
21    Adapt a loader to provide TraversableResources.
22    """
23
24    def __init__(self, spec):
25        self.spec = spec
26
27    def get_resource_reader(self, name):
28        return DegenerateFiles(self.spec)._native()
29
30
31class DegenerateFiles:
32    """
33    Adapter for an existing or non-existant resource reader
34    to provide a degenerate .files().
35    """
36
37    class Path(abc.Traversable):
38        def iterdir(self):
39            return iter(())
40
41        def is_dir(self):
42            return False
43
44        is_file = exists = is_dir  # type: ignore
45
46        def joinpath(self, other):
47            return DegenerateFiles.Path()
48
49        @property
50        def name(self):
51            return ''
52
53        def open(self, mode='rb', *args, **kwargs):
54            raise ValueError()
55
56    def __init__(self, spec):
57        self.spec = spec
58
59    @property
60    def _reader(self):
61        with suppress(AttributeError):
62            return self.spec.loader.get_resource_reader(self.spec.name)
63
64    def _native(self):
65        """
66        Return the native reader if it supports files().
67        """
68        reader = self._reader
69        return reader if hasattr(reader, 'files') else self
70
71    def __getattr__(self, attr):
72        return getattr(self._reader, attr)
73
74    def files(self):
75        return DegenerateFiles.Path()
76
77
78def wrap_spec(package):
79    """
80    Construct a package spec with traversable compatibility
81    on the spec/loader/reader.
82    """
83    return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
84