6 from contextlib import suppress
8 if sys.version_info >= (3, 10):
9 from zipfile import Path as ZipPath # type: ignore
11 from ..zipp import Path as ZipPath # type: ignore
15 from typing import runtime_checkable # type: ignore
18 def runtime_checkable(cls): # type: ignore
23 from typing import Protocol # type: ignore
25 Protocol = abc.ABC # type: ignore
28 class TraversableResourcesLoader:
30 Adapt loaders to provide TraversableResources and other
33 Used primarily for Python 3.9 and earlier where the native
34 loaders do not yet implement TraversableResources.
37 def __init__(self, spec):
42 return self.spec.origin
44 def get_resource_reader(self, name):
45 from . import readers, _adapters
47 def _zip_reader(spec):
48 with suppress(AttributeError):
49 return readers.ZipReader(spec.loader, spec.name)
51 def _namespace_reader(spec):
52 with suppress(AttributeError, ValueError):
53 return readers.NamespaceReader(spec.submodule_search_locations)
55 def _available_reader(spec):
56 with suppress(AttributeError):
57 return spec.loader.get_resource_reader(spec.name)
59 def _native_reader(spec):
60 reader = _available_reader(spec)
61 return reader if hasattr(reader, 'files') else None
63 def _file_reader(spec):
65 path = pathlib.Path(self.path)
69 return readers.FileReader(self)
72 # native reader if it supplies 'files'
73 _native_reader(self.spec)
75 # local ZipReader if a zip module
76 _zip_reader(self.spec)
78 # local NamespaceReader if a namespace module
79 _namespace_reader(self.spec)
82 _file_reader(self.spec)
83 # fallback - adapt the spec ResourceReader to TraversableReader
84 or _adapters.CompatibilityFiles(self.spec)
88 def wrap_spec(package):
90 Construct a package spec with traversable compatibility
91 on the spec/loader/reader.
93 Supersedes _adapters.wrap_spec to use TraversableResourcesLoader
94 from above for older Python compatibility (<3.10).
96 from . import _adapters
98 return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)