|
|
|
# flake8: noqa
|
|
|
|
|
|
|
|
import abc
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import pathlib
|
|
|
|
from contextlib import suppress
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
|
|
|
|
|
if sys.version_info >= (3, 10):
|
|
|
|
from zipfile import Path as ZipPath # type: ignore
|
|
|
|
else:
|
|
|
|
from zipp import Path as ZipPath # type: ignore
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
from typing import runtime_checkable # type: ignore
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
def runtime_checkable(cls): # type: ignore
|
|
|
|
return cls
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
from typing import Protocol # type: ignore
|
|
|
|
except ImportError:
|
|
|
|
Protocol = abc.ABC # type: ignore
|
|
|
|
|
|
|
|
|
|
|
|
class TraversableResourcesLoader:
|
|
|
|
"""
|
|
|
|
Adapt loaders to provide TraversableResources and other
|
|
|
|
compatibility.
|
|
|
|
|
|
|
|
Used primarily for Python 3.9 and earlier where the native
|
|
|
|
loaders do not yet implement TraversableResources.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, spec):
|
|
|
|
self.spec = spec
|
|
|
|
|
|
|
|
@property
|
|
|
|
def path(self):
|
|
|
|
return self.spec.origin
|
|
|
|
|
|
|
|
def get_resource_reader(self, name):
|
|
|
|
from . import readers, _adapters
|
|
|
|
|
|
|
|
def _zip_reader(spec):
|
|
|
|
with suppress(AttributeError):
|
|
|
|
return readers.ZipReader(spec.loader, spec.name)
|
|
|
|
|
|
|
|
def _namespace_reader(spec):
|
|
|
|
with suppress(AttributeError, ValueError):
|
|
|
|
return readers.NamespaceReader(spec.submodule_search_locations)
|
|
|
|
|
|
|
|
def _available_reader(spec):
|
|
|
|
with suppress(AttributeError):
|
|
|
|
return spec.loader.get_resource_reader(spec.name)
|
|
|
|
|
|
|
|
def _native_reader(spec):
|
|
|
|
reader = _available_reader(spec)
|
|
|
|
return reader if hasattr(reader, 'files') else None
|
|
|
|
|
|
|
|
def _file_reader(spec):
|
|
|
|
try:
|
|
|
|
path = pathlib.Path(self.path)
|
|
|
|
except TypeError:
|
|
|
|
return None
|
|
|
|
if path.exists():
|
|
|
|
return readers.FileReader(self)
|
|
|
|
|
|
|
|
return (
|
|
|
|
# native reader if it supplies 'files'
|
|
|
|
_native_reader(self.spec)
|
|
|
|
or
|
|
|
|
# local ZipReader if a zip module
|
|
|
|
_zip_reader(self.spec)
|
|
|
|
or
|
|
|
|
# local NamespaceReader if a namespace module
|
|
|
|
_namespace_reader(self.spec)
|
|
|
|
or
|
|
|
|
# local FileReader
|
|
|
|
_file_reader(self.spec)
|
|
|
|
# fallback - adapt the spec ResourceReader to TraversableReader
|
|
|
|
or _adapters.CompatibilityFiles(self.spec)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def wrap_spec(package):
|
|
|
|
"""
|
|
|
|
Construct a package spec with traversable compatibility
|
|
|
|
on the spec/loader/reader.
|
|
|
|
|
|
|
|
Supersedes _adapters.wrap_spec to use TraversableResourcesLoader
|
|
|
|
from above for older Python compatibility (<3.10).
|
|
|
|
"""
|
|
|
|
from . import _adapters
|
|
|
|
|
|
|
|
return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader)
|
|
|
|
|
|
|
|
|
|
|
|
if sys.version_info >= (3, 9):
|
|
|
|
StrPath = Union[str, os.PathLike[str]]
|
|
|
|
else:
|
|
|
|
# PathLike is only subscriptable at runtime in 3.9+
|
|
|
|
StrPath = Union[str, "os.PathLike[str]"]
|