cb9fc820cb352aa6e92705aab4f55cbc2eff96bc
[SubU] /
1 # flake8: noqa
2
3 import abc
4 import sys
5 import pathlib
6 from contextlib import suppress
7
8 if sys.version_info >= (3, 10):
9     from zipfile import Path as ZipPath  # type: ignore
10 else:
11     from ..zipp import Path as ZipPath  # type: ignore
12
13
14 try:
15     from typing import runtime_checkable  # type: ignore
16 except ImportError:
17
18     def runtime_checkable(cls):  # type: ignore
19         return cls
20
21
22 try:
23     from typing import Protocol  # type: ignore
24 except ImportError:
25     Protocol = abc.ABC  # type: ignore
26
27
28 class TraversableResourcesLoader:
29     """
30     Adapt loaders to provide TraversableResources and other
31     compatibility.
32
33     Used primarily for Python 3.9 and earlier where the native
34     loaders do not yet implement TraversableResources.
35     """
36
37     def __init__(self, spec):
38         self.spec = spec
39
40     @property
41     def path(self):
42         return self.spec.origin
43
44     def get_resource_reader(self, name):
45         from . import readers, _adapters
46
47         def _zip_reader(spec):
48             with suppress(AttributeError):
49                 return readers.ZipReader(spec.loader, spec.name)
50
51         def _namespace_reader(spec):
52             with suppress(AttributeError, ValueError):
53                 return readers.NamespaceReader(spec.submodule_search_locations)
54
55         def _available_reader(spec):
56             with suppress(AttributeError):
57                 return spec.loader.get_resource_reader(spec.name)
58
59         def _native_reader(spec):
60             reader = _available_reader(spec)
61             return reader if hasattr(reader, 'files') else None
62
63         def _file_reader(spec):
64             try:
65                 path = pathlib.Path(self.path)
66             except TypeError:
67                 return None
68             if path.exists():
69                 return readers.FileReader(self)
70
71         return (
72             # native reader if it supplies 'files'
73             _native_reader(self.spec)
74             or
75             # local ZipReader if a zip module
76             _zip_reader(self.spec)
77             or
78             # local NamespaceReader if a namespace module
79             _namespace_reader(self.spec)
80             or
81             # local FileReader
82             _file_reader(self.spec)
83             # fallback - adapt the spec ResourceReader to TraversableReader
84             or _adapters.CompatibilityFiles(self.spec)
85         )
86
87
88 def wrap_spec(package):
89     """
90     Construct a package spec with traversable compatibility
91     on the spec/loader/reader.
92
93     Supersedes _adapters.wrap_spec to use TraversableResourcesLoader
94     from above for older Python compatibility (<3.10).
95     """
96     from . import _adapters
97
98     return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)