You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
2.6 KiB
99 lines
2.6 KiB
3 years ago
|
# flake8: noqa
|
||
|
|
||
|
import abc
|
||
|
import sys
|
||
|
import pathlib
|
||
|
from contextlib import suppress
|
||
|
|
||
|
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)
|