fix vendor
This commit is contained in:
53
vendor/importlib_resources/__init__.py
vendored
Normal file
53
vendor/importlib_resources/__init__.py
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Read resources contained within a package."""
|
||||
|
||||
import sys
|
||||
|
||||
from ._common import (
|
||||
as_file, files,
|
||||
)
|
||||
|
||||
# For compatibility. Ref #88.
|
||||
# Also requires hook-importlib_resources.py (Ref #101).
|
||||
__import__('importlib_resources.trees')
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Package',
|
||||
'Resource',
|
||||
'ResourceReader',
|
||||
'as_file',
|
||||
'contents',
|
||||
'files',
|
||||
'is_resource',
|
||||
'open_binary',
|
||||
'open_text',
|
||||
'path',
|
||||
'read_binary',
|
||||
'read_text',
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
from importlib_resources._py3 import (
|
||||
Package,
|
||||
Resource,
|
||||
contents,
|
||||
is_resource,
|
||||
open_binary,
|
||||
open_text,
|
||||
path,
|
||||
read_binary,
|
||||
read_text,
|
||||
)
|
||||
from importlib_resources.abc import ResourceReader
|
||||
else:
|
||||
from importlib_resources._py2 import (
|
||||
contents,
|
||||
is_resource,
|
||||
open_binary,
|
||||
open_text,
|
||||
path,
|
||||
read_binary,
|
||||
read_text,
|
||||
)
|
||||
del __all__[:3]
|
120
vendor/importlib_resources/_common.py
vendored
Normal file
120
vendor/importlib_resources/_common.py
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import contextlib
|
||||
import types
|
||||
import importlib
|
||||
|
||||
from ._compat import (
|
||||
Path, FileNotFoundError,
|
||||
singledispatch, package_spec,
|
||||
)
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
from typing import Union, Any, Optional
|
||||
from .abc import ResourceReader
|
||||
Package = Union[types.ModuleType, str]
|
||||
|
||||
|
||||
def files(package):
|
||||
"""
|
||||
Get a Traversable resource from a package
|
||||
"""
|
||||
return from_package(get_package(package))
|
||||
|
||||
|
||||
def normalize_path(path):
|
||||
# type: (Any) -> str
|
||||
"""Normalize a path by ensuring it is a string.
|
||||
|
||||
If the resulting string contains path separators, an exception is raised.
|
||||
"""
|
||||
str_path = str(path)
|
||||
parent, file_name = os.path.split(str_path)
|
||||
if parent:
|
||||
raise ValueError('{!r} must be only a file name'.format(path))
|
||||
return file_name
|
||||
|
||||
|
||||
def get_resource_reader(package):
|
||||
# type: (types.ModuleType) -> Optional[ResourceReader]
|
||||
"""
|
||||
Return the package's loader if it's a ResourceReader.
|
||||
"""
|
||||
# We can't use
|
||||
# a issubclass() check here because apparently abc.'s __subclasscheck__()
|
||||
# hook wants to create a weak reference to the object, but
|
||||
# zipimport.zipimporter does not support weak references, resulting in a
|
||||
# TypeError. That seems terrible.
|
||||
spec = package.__spec__
|
||||
reader = getattr(spec.loader, 'get_resource_reader', None)
|
||||
if reader is None:
|
||||
return None
|
||||
return reader(spec.name)
|
||||
|
||||
|
||||
def resolve(cand):
|
||||
# type: (Package) -> types.ModuleType
|
||||
return (
|
||||
cand if isinstance(cand, types.ModuleType)
|
||||
else importlib.import_module(cand)
|
||||
)
|
||||
|
||||
|
||||
def get_package(package):
|
||||
# type: (Package) -> types.ModuleType
|
||||
"""Take a package name or module object and return the module.
|
||||
|
||||
Raise an exception if the resolved module is not a package.
|
||||
"""
|
||||
resolved = resolve(package)
|
||||
if package_spec(resolved).submodule_search_locations is None:
|
||||
raise TypeError('{!r} is not a package'.format(package))
|
||||
return resolved
|
||||
|
||||
|
||||
def from_package(package):
|
||||
"""
|
||||
Return a Traversable object for the given package.
|
||||
|
||||
"""
|
||||
spec = package_spec(package)
|
||||
reader = spec.loader.get_resource_reader(spec.name)
|
||||
return reader.files()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _tempfile(reader, suffix=''):
|
||||
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
|
||||
# blocks due to the need to close the temporary file to work on Windows
|
||||
# properly.
|
||||
fd, raw_path = tempfile.mkstemp(suffix=suffix)
|
||||
try:
|
||||
os.write(fd, reader())
|
||||
os.close(fd)
|
||||
del reader
|
||||
yield Path(raw_path)
|
||||
finally:
|
||||
try:
|
||||
os.remove(raw_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
@singledispatch
|
||||
def as_file(path):
|
||||
"""
|
||||
Given a Traversable object, return that object as a
|
||||
path on the local file system in a context manager.
|
||||
"""
|
||||
return _tempfile(path.read_bytes, suffix=path.name)
|
||||
|
||||
|
||||
@as_file.register(Path)
|
||||
@contextlib.contextmanager
|
||||
def _(path):
|
||||
"""
|
||||
Degenerate behavior for pathlib.Path objects.
|
||||
"""
|
||||
yield path
|
139
vendor/importlib_resources/_compat.py
vendored
Normal file
139
vendor/importlib_resources/_compat.py
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
# flake8: noqa
|
||||
|
||||
if sys.version_info > (3,5):
|
||||
from pathlib import Path, PurePath
|
||||
else:
|
||||
from pathlib2 import Path, PurePath # type: ignore
|
||||
|
||||
|
||||
if sys.version_info > (3,):
|
||||
from contextlib import suppress
|
||||
else:
|
||||
from contextlib2 import suppress # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
from functools import singledispatch
|
||||
except ImportError:
|
||||
from singledispatch import singledispatch # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
from abc import ABC # type: ignore
|
||||
except ImportError:
|
||||
from abc import ABCMeta
|
||||
|
||||
class ABC(object): # type: ignore
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
|
||||
try:
|
||||
FileNotFoundError = FileNotFoundError # type: ignore
|
||||
except NameError:
|
||||
FileNotFoundError = OSError # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
NotADirectoryError = NotADirectoryError # type: ignore
|
||||
except NameError:
|
||||
NotADirectoryError = OSError # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
from zipfile import Path as ZipPath # type: ignore
|
||||
except ImportError:
|
||||
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 # type: ignore
|
||||
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class PackageSpec:
|
||||
def __init__(self, **kwargs):
|
||||
vars(self).update(kwargs)
|
||||
|
||||
|
||||
class TraversableResourcesAdapter:
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
self.loader = LoaderAdapter(spec)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.spec, name)
|
||||
|
||||
|
||||
class LoaderAdapter:
|
||||
"""
|
||||
Adapt loaders to provide TraversableResources and other
|
||||
compatibility.
|
||||
"""
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
# Python < 3
|
||||
return self.spec.origin
|
||||
|
||||
def get_resource_reader(self, name):
|
||||
# Python < 3.9
|
||||
from . import readers
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
readers.FileReader(self)
|
||||
)
|
||||
|
||||
|
||||
def package_spec(package):
|
||||
"""
|
||||
Construct a minimal package spec suitable for
|
||||
matching the interfaces this library relies upon
|
||||
in later Python versions.
|
||||
"""
|
||||
spec = getattr(package, '__spec__', None) or \
|
||||
PackageSpec(
|
||||
origin=package.__file__,
|
||||
loader=getattr(package, '__loader__', None),
|
||||
name=package.__name__,
|
||||
submodule_search_locations=getattr(package, '__path__', None),
|
||||
)
|
||||
return TraversableResourcesAdapter(spec)
|
107
vendor/importlib_resources/_py2.py
vendored
Normal file
107
vendor/importlib_resources/_py2.py
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
import os
|
||||
import errno
|
||||
|
||||
from . import _common
|
||||
from ._compat import FileNotFoundError
|
||||
from io import BytesIO, TextIOWrapper, open as io_open
|
||||
|
||||
|
||||
def open_binary(package, resource):
|
||||
"""Return a file-like object opened for binary reading of the resource."""
|
||||
resource = _common.normalize_path(resource)
|
||||
package = _common.get_package(package)
|
||||
# Using pathlib doesn't work well here due to the lack of 'strict' argument
|
||||
# for pathlib.Path.resolve() prior to Python 3.6.
|
||||
package_path = os.path.dirname(package.__file__)
|
||||
relative_path = os.path.join(package_path, resource)
|
||||
full_path = os.path.abspath(relative_path)
|
||||
try:
|
||||
return io_open(full_path, 'rb')
|
||||
except IOError:
|
||||
# This might be a package in a zip file. zipimport provides a loader
|
||||
# with a functioning get_data() method, however we have to strip the
|
||||
# archive (i.e. the .zip file's name) off the front of the path. This
|
||||
# is because the zipimport loader in Python 2 doesn't actually follow
|
||||
# PEP 302. It should allow the full path, but actually requires that
|
||||
# the path be relative to the zip file.
|
||||
try:
|
||||
loader = package.__loader__
|
||||
full_path = relative_path[len(loader.archive)+1:]
|
||||
data = loader.get_data(full_path)
|
||||
except (IOError, AttributeError):
|
||||
package_name = package.__name__
|
||||
message = '{!r} resource not found in {!r}'.format(
|
||||
resource, package_name)
|
||||
raise FileNotFoundError(message)
|
||||
return BytesIO(data)
|
||||
|
||||
|
||||
def open_text(package, resource, encoding='utf-8', errors='strict'):
|
||||
"""Return a file-like object opened for text reading of the resource."""
|
||||
return TextIOWrapper(
|
||||
open_binary(package, resource), encoding=encoding, errors=errors)
|
||||
|
||||
|
||||
def read_binary(package, resource):
|
||||
"""Return the binary contents of the resource."""
|
||||
with open_binary(package, resource) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def read_text(package, resource, encoding='utf-8', errors='strict'):
|
||||
"""Return the decoded string of the resource.
|
||||
|
||||
The decoding-related arguments have the same semantics as those of
|
||||
bytes.decode().
|
||||
"""
|
||||
with open_text(package, resource, encoding, errors) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def path(package, resource):
|
||||
"""A context manager providing a file path object to the resource.
|
||||
|
||||
If the resource does not already exist on its own on the file system,
|
||||
a temporary file will be created. If the file was created, the file
|
||||
will be deleted upon exiting the context manager (no exception is
|
||||
raised if the file was deleted prior to the context manager
|
||||
exiting).
|
||||
"""
|
||||
path = _common.files(package).joinpath(_common.normalize_path(resource))
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(path)
|
||||
return _common.as_file(path)
|
||||
|
||||
|
||||
def is_resource(package, name):
|
||||
"""True if name is a resource inside package.
|
||||
|
||||
Directories are *not* resources.
|
||||
"""
|
||||
package = _common.get_package(package)
|
||||
_common.normalize_path(name)
|
||||
try:
|
||||
package_contents = set(contents(package))
|
||||
except OSError as error:
|
||||
if error.errno not in (errno.ENOENT, errno.ENOTDIR):
|
||||
# We won't hit this in the Python 2 tests, so it'll appear
|
||||
# uncovered. We could mock os.listdir() to return a non-ENOENT or
|
||||
# ENOTDIR, but then we'd have to depend on another external
|
||||
# library since Python 2 doesn't have unittest.mock. It's not
|
||||
# worth it.
|
||||
raise # pragma: nocover
|
||||
return False
|
||||
if name not in package_contents:
|
||||
return False
|
||||
return (_common.from_package(package) / name).is_file()
|
||||
|
||||
|
||||
def contents(package):
|
||||
"""Return an iterable of entries in `package`.
|
||||
|
||||
Note that not all entries are resources. Specifically, directories are
|
||||
not considered resources. Use `is_resource()` on each entry returned here
|
||||
to check if it is a resource or not.
|
||||
"""
|
||||
package = _common.get_package(package)
|
||||
return list(item.name for item in _common.from_package(package).iterdir())
|
160
vendor/importlib_resources/_py3.py
vendored
Normal file
160
vendor/importlib_resources/_py3.py
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
import os
|
||||
import io
|
||||
|
||||
from . import _common
|
||||
from contextlib import suppress
|
||||
from importlib.abc import ResourceLoader
|
||||
from io import BytesIO, TextIOWrapper
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Iterable, Iterator, Optional, Set, Union # noqa: F401
|
||||
from typing import cast
|
||||
from typing.io import BinaryIO, TextIO
|
||||
from collections.abc import Sequence
|
||||
from functools import singledispatch
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
from typing import ContextManager
|
||||
|
||||
Package = Union[ModuleType, str]
|
||||
Resource = Union[str, os.PathLike]
|
||||
|
||||
|
||||
def open_binary(package: Package, resource: Resource) -> BinaryIO:
|
||||
"""Return a file-like object opened for binary reading of the resource."""
|
||||
resource = _common.normalize_path(resource)
|
||||
package = _common.get_package(package)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return reader.open_resource(resource)
|
||||
# Using pathlib doesn't work well here due to the lack of 'strict'
|
||||
# argument for pathlib.Path.resolve() prior to Python 3.6.
|
||||
if package.__spec__.submodule_search_locations is not None:
|
||||
paths = package.__spec__.submodule_search_locations
|
||||
elif package.__spec__.origin is not None:
|
||||
paths = [os.path.dirname(os.path.abspath(package.__spec__.origin))]
|
||||
|
||||
for package_path in paths:
|
||||
full_path = os.path.join(package_path, resource)
|
||||
try:
|
||||
return open(full_path, mode='rb')
|
||||
except OSError:
|
||||
# Just assume the loader is a resource loader; all the relevant
|
||||
# importlib.machinery loaders are and an AttributeError for
|
||||
# get_data() will make it clear what is needed from the loader.
|
||||
loader = cast(ResourceLoader, package.__spec__.loader)
|
||||
data = None
|
||||
if hasattr(package.__spec__.loader, 'get_data'):
|
||||
with suppress(OSError):
|
||||
data = loader.get_data(full_path)
|
||||
if data is not None:
|
||||
return BytesIO(data)
|
||||
|
||||
raise FileNotFoundError('{!r} resource not found in {!r}'.format(
|
||||
resource, package.__spec__.name))
|
||||
|
||||
|
||||
def open_text(package: Package,
|
||||
resource: Resource,
|
||||
encoding: str = 'utf-8',
|
||||
errors: str = 'strict') -> TextIO:
|
||||
"""Return a file-like object opened for text reading of the resource."""
|
||||
return TextIOWrapper(
|
||||
open_binary(package, resource), encoding=encoding, errors=errors)
|
||||
|
||||
|
||||
def read_binary(package: Package, resource: Resource) -> bytes:
|
||||
"""Return the binary contents of the resource."""
|
||||
with open_binary(package, resource) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def read_text(package: Package,
|
||||
resource: Resource,
|
||||
encoding: str = 'utf-8',
|
||||
errors: str = 'strict') -> str:
|
||||
"""Return the decoded string of the resource.
|
||||
|
||||
The decoding-related arguments have the same semantics as those of
|
||||
bytes.decode().
|
||||
"""
|
||||
with open_text(package, resource, encoding, errors) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def path(
|
||||
package: Package, resource: Resource,
|
||||
) -> 'ContextManager[Path]':
|
||||
"""A context manager providing a file path object to the resource.
|
||||
|
||||
If the resource does not already exist on its own on the file system,
|
||||
a temporary file will be created. If the file was created, the file
|
||||
will be deleted upon exiting the context manager (no exception is
|
||||
raised if the file was deleted prior to the context manager
|
||||
exiting).
|
||||
"""
|
||||
reader = _common.get_resource_reader(_common.get_package(package))
|
||||
return (
|
||||
_path_from_reader(reader, _common.normalize_path(resource))
|
||||
if reader else
|
||||
_common.as_file(
|
||||
_common.files(package).joinpath(_common.normalize_path(resource)))
|
||||
)
|
||||
|
||||
|
||||
def _path_from_reader(reader, resource):
|
||||
return _path_from_resource_path(reader, resource) or \
|
||||
_path_from_open_resource(reader, resource)
|
||||
|
||||
|
||||
def _path_from_resource_path(reader, resource):
|
||||
with suppress(FileNotFoundError):
|
||||
return Path(reader.resource_path(resource))
|
||||
|
||||
|
||||
def _path_from_open_resource(reader, resource):
|
||||
saved = io.BytesIO(reader.open_resource(resource).read())
|
||||
return _common._tempfile(saved.read, suffix=resource)
|
||||
|
||||
|
||||
def is_resource(package: Package, name: str) -> bool:
|
||||
"""True if `name` is a resource inside `package`.
|
||||
|
||||
Directories are *not* resources.
|
||||
"""
|
||||
package = _common.get_package(package)
|
||||
_common.normalize_path(name)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return reader.is_resource(name)
|
||||
package_contents = set(contents(package))
|
||||
if name not in package_contents:
|
||||
return False
|
||||
return (_common.from_package(package) / name).is_file()
|
||||
|
||||
|
||||
def contents(package: Package) -> Iterable[str]:
|
||||
"""Return an iterable of entries in `package`.
|
||||
|
||||
Note that not all entries are resources. Specifically, directories are
|
||||
not considered resources. Use `is_resource()` on each entry returned here
|
||||
to check if it is a resource or not.
|
||||
"""
|
||||
package = _common.get_package(package)
|
||||
reader = _common.get_resource_reader(package)
|
||||
if reader is not None:
|
||||
return _ensure_sequence(reader.contents())
|
||||
transversable = _common.from_package(package)
|
||||
if transversable.is_dir():
|
||||
return list(item.name for item in transversable.iterdir())
|
||||
return []
|
||||
|
||||
|
||||
@singledispatch
|
||||
def _ensure_sequence(iterable):
|
||||
return list(iterable)
|
||||
|
||||
|
||||
@_ensure_sequence.register(Sequence)
|
||||
def _(iterable):
|
||||
return iterable
|
142
vendor/importlib_resources/abc.py
vendored
Normal file
142
vendor/importlib_resources/abc.py
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import abc
|
||||
|
||||
from ._compat import ABC, FileNotFoundError, runtime_checkable, Protocol
|
||||
|
||||
# Use mypy's comment syntax for Python 2 compatibility
|
||||
try:
|
||||
from typing import BinaryIO, Iterable, Text
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class ResourceReader(ABC):
|
||||
"""Abstract base class for loaders to provide resource reading support."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def open_resource(self, resource):
|
||||
# type: (Text) -> BinaryIO
|
||||
"""Return an opened, file-like object for binary reading.
|
||||
|
||||
The 'resource' argument is expected to represent only a file name.
|
||||
If the resource cannot be found, FileNotFoundError is raised.
|
||||
"""
|
||||
# This deliberately raises FileNotFoundError instead of
|
||||
# NotImplementedError so that if this method is accidentally called,
|
||||
# it'll still do the right thing.
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def resource_path(self, resource):
|
||||
# type: (Text) -> Text
|
||||
"""Return the file system path to the specified resource.
|
||||
|
||||
The 'resource' argument is expected to represent only a file name.
|
||||
If the resource does not exist on the file system, raise
|
||||
FileNotFoundError.
|
||||
"""
|
||||
# This deliberately raises FileNotFoundError instead of
|
||||
# NotImplementedError so that if this method is accidentally called,
|
||||
# it'll still do the right thing.
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_resource(self, path):
|
||||
# type: (Text) -> bool
|
||||
"""Return True if the named 'path' is a resource.
|
||||
|
||||
Files are resources, directories are not.
|
||||
"""
|
||||
raise FileNotFoundError
|
||||
|
||||
@abc.abstractmethod
|
||||
def contents(self):
|
||||
# type: () -> Iterable[str]
|
||||
"""Return an iterable of entries in `package`."""
|
||||
raise FileNotFoundError
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Traversable(Protocol):
|
||||
"""
|
||||
An object with a subset of pathlib.Path methods suitable for
|
||||
traversing directories and opening files.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def iterdir(self):
|
||||
"""
|
||||
Yield Traversable objects in self
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def read_bytes(self):
|
||||
"""
|
||||
Read contents of self as bytes
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def read_text(self, encoding=None):
|
||||
"""
|
||||
Read contents of self as bytes
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_dir(self):
|
||||
"""
|
||||
Return True if self is a dir
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_file(self):
|
||||
"""
|
||||
Return True if self is a file
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def joinpath(self, child):
|
||||
"""
|
||||
Return Traversable child in self
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __truediv__(self, child):
|
||||
"""
|
||||
Return Traversable child in self
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def open(self, mode='r', *args, **kwargs):
|
||||
"""
|
||||
mode may be 'r' or 'rb' to open as text or binary. Return a handle
|
||||
suitable for reading (same as pathlib.Path.open).
|
||||
|
||||
When opening as text, accepts encoding parameters such as those
|
||||
accepted by io.TextIOWrapper.
|
||||
"""
|
||||
|
||||
@abc.abstractproperty
|
||||
def name(self):
|
||||
# type: () -> str
|
||||
"""
|
||||
The base name of this object without any parent references.
|
||||
"""
|
||||
|
||||
|
||||
class TraversableResources(ResourceReader):
|
||||
@abc.abstractmethod
|
||||
def files(self):
|
||||
"""Return a Traversable object for the loaded package."""
|
||||
|
||||
def open_resource(self, resource):
|
||||
return self.files().joinpath(resource).open('rb')
|
||||
|
||||
def resource_path(self, resource):
|
||||
raise FileNotFoundError(resource)
|
||||
|
||||
def is_resource(self, path):
|
||||
return self.files().joinpath(path).is_file()
|
||||
|
||||
def contents(self):
|
||||
return (item.name for item in self.files().iterdir())
|
0
vendor/importlib_resources/py.typed
vendored
Normal file
0
vendor/importlib_resources/py.typed
vendored
Normal file
123
vendor/importlib_resources/readers.py
vendored
Normal file
123
vendor/importlib_resources/readers.py
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
import os.path
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from . import abc
|
||||
|
||||
from ._compat import Path, ZipPath
|
||||
from ._compat import FileNotFoundError, NotADirectoryError
|
||||
|
||||
|
||||
class FileReader(abc.TraversableResources):
|
||||
def __init__(self, loader):
|
||||
self.path = Path(loader.path).parent
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
||||
|
||||
|
||||
class ZipReader(abc.TraversableResources):
|
||||
def __init__(self, loader, module):
|
||||
_, _, name = module.rpartition('.')
|
||||
self.prefix = loader.prefix.replace('\\', '/') + name + '/'
|
||||
self.archive = loader.archive
|
||||
|
||||
def open_resource(self, resource):
|
||||
try:
|
||||
return super().open_resource(resource)
|
||||
except KeyError as exc:
|
||||
raise FileNotFoundError(exc.args[0])
|
||||
|
||||
def is_resource(self, path):
|
||||
# workaround for `zipfile.Path.is_file` returning true
|
||||
# for non-existent paths.
|
||||
target = self.files().joinpath(path)
|
||||
return target.is_file() and target.exists()
|
||||
|
||||
def files(self):
|
||||
return ZipPath(self.archive, self.prefix)
|
||||
|
||||
|
||||
class MultiplexedPath(abc.Traversable):
|
||||
"""
|
||||
Given a series of Traversable objects, implement a merged
|
||||
version of the interface across all objects. Useful for
|
||||
namespace packages which may be multihomed at a single
|
||||
name.
|
||||
"""
|
||||
def __init__(self, *paths):
|
||||
paths = list(OrderedDict.fromkeys(paths)) # remove duplicates
|
||||
self._paths = list(map(Path, paths))
|
||||
if not self._paths:
|
||||
message = 'MultiplexedPath must contain at least one path'
|
||||
raise FileNotFoundError(message)
|
||||
if any(not path.is_dir() for path in self._paths):
|
||||
raise NotADirectoryError(
|
||||
'MultiplexedPath only supports directories')
|
||||
|
||||
def iterdir(self):
|
||||
visited = []
|
||||
for path in self._paths:
|
||||
for file in path.iterdir():
|
||||
if file.name in visited:
|
||||
continue
|
||||
visited.append(file.name)
|
||||
yield file
|
||||
|
||||
def read_bytes(self):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def read_text(self, *args, **kwargs):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def is_dir(self):
|
||||
return True
|
||||
|
||||
def is_file(self):
|
||||
return False
|
||||
|
||||
def joinpath(self, child):
|
||||
# first try to find child in current paths
|
||||
for file in self.iterdir():
|
||||
if file.name == child:
|
||||
return file
|
||||
# if it does not exist, construct it with the first path
|
||||
return self._paths[0] / child
|
||||
|
||||
__truediv__ = joinpath
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
raise FileNotFoundError('{} is not a file'.format(self))
|
||||
|
||||
def name(self):
|
||||
return os.path.basename(self._paths[0])
|
||||
|
||||
def __repr__(self):
|
||||
return 'MultiplexedPath({})'.format(
|
||||
', '.join("'{}'".format(path) for path in self._paths))
|
||||
|
||||
|
||||
class NamespaceReader(abc.TraversableResources):
|
||||
def __init__(self, namespace_path):
|
||||
if 'NamespacePath' not in str(namespace_path):
|
||||
raise ValueError('Invalid path')
|
||||
self.path = MultiplexedPath(*list(namespace_path))
|
||||
|
||||
def resource_path(self, resource):
|
||||
"""
|
||||
Return the file system path to prevent
|
||||
`resources.path()` from creating a temporary
|
||||
copy.
|
||||
"""
|
||||
return str(self.path.joinpath(resource))
|
||||
|
||||
def files(self):
|
||||
return self.path
|
0
vendor/importlib_resources/tests/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/__init__.py
vendored
Normal file
30
vendor/importlib_resources/tests/_compat.py
vendored
Normal file
30
vendor/importlib_resources/tests/_compat.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
try:
|
||||
from test.support import import_helper # type: ignore
|
||||
except ImportError:
|
||||
try:
|
||||
# Python 3.9 and earlier
|
||||
class import_helper: # type: ignore
|
||||
from test.support import modules_setup, modules_cleanup
|
||||
except ImportError:
|
||||
from . import py27compat
|
||||
|
||||
class import_helper: # type: ignore
|
||||
modules_setup = staticmethod(py27compat.modules_setup)
|
||||
modules_cleanup = staticmethod(py27compat.modules_cleanup)
|
||||
|
||||
|
||||
try:
|
||||
from os import fspath # type: ignore
|
||||
except ImportError:
|
||||
# Python 3.5
|
||||
fspath = str # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
# Python 3.10
|
||||
from test.support.os_helper import unlink
|
||||
except ImportError:
|
||||
from test.support import unlink as _unlink
|
||||
|
||||
def unlink(target):
|
||||
return _unlink(fspath(target))
|
0
vendor/importlib_resources/tests/data01/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data01/__init__.py
vendored
Normal file
BIN
vendor/importlib_resources/tests/data01/binary.file
vendored
Normal file
BIN
vendor/importlib_resources/tests/data01/binary.file
vendored
Normal file
Binary file not shown.
0
vendor/importlib_resources/tests/data01/subdirectory/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data01/subdirectory/__init__.py
vendored
Normal file
BIN
vendor/importlib_resources/tests/data01/subdirectory/binary.file
vendored
Normal file
BIN
vendor/importlib_resources/tests/data01/subdirectory/binary.file
vendored
Normal file
Binary file not shown.
BIN
vendor/importlib_resources/tests/data01/utf-16.file
vendored
Normal file
BIN
vendor/importlib_resources/tests/data01/utf-16.file
vendored
Normal file
Binary file not shown.
1
vendor/importlib_resources/tests/data01/utf-8.file
vendored
Normal file
1
vendor/importlib_resources/tests/data01/utf-8.file
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, UTF-8 world!
|
0
vendor/importlib_resources/tests/data02/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data02/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data02/one/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data02/one/__init__.py
vendored
Normal file
1
vendor/importlib_resources/tests/data02/one/resource1.txt
vendored
Normal file
1
vendor/importlib_resources/tests/data02/one/resource1.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
one resource
|
0
vendor/importlib_resources/tests/data02/two/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/data02/two/__init__.py
vendored
Normal file
1
vendor/importlib_resources/tests/data02/two/resource2.txt
vendored
Normal file
1
vendor/importlib_resources/tests/data02/two/resource2.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
two resource
|
BIN
vendor/importlib_resources/tests/namespacedata01/binary.file
vendored
Normal file
BIN
vendor/importlib_resources/tests/namespacedata01/binary.file
vendored
Normal file
Binary file not shown.
BIN
vendor/importlib_resources/tests/namespacedata01/utf-16.file
vendored
Normal file
BIN
vendor/importlib_resources/tests/namespacedata01/utf-16.file
vendored
Normal file
Binary file not shown.
1
vendor/importlib_resources/tests/namespacedata01/utf-8.file
vendored
Normal file
1
vendor/importlib_resources/tests/namespacedata01/utf-8.file
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, UTF-8 world!
|
23
vendor/importlib_resources/tests/py27compat.py
vendored
Normal file
23
vendor/importlib_resources/tests/py27compat.py
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import sys
|
||||
|
||||
|
||||
def modules_setup():
|
||||
return sys.modules.copy(),
|
||||
|
||||
|
||||
def modules_cleanup(oldmodules):
|
||||
# Encoders/decoders are registered permanently within the internal
|
||||
# codec cache. If we destroy the corresponding modules their
|
||||
# globals will be set to None which will trip up the cached functions.
|
||||
encodings = [(k, v) for k, v in sys.modules.items()
|
||||
if k.startswith('encodings.')]
|
||||
sys.modules.clear()
|
||||
sys.modules.update(encodings)
|
||||
# XXX: This kind of problem can affect more than just encodings.
|
||||
# In particular extension modules (such as _ssl) don't cope
|
||||
# with reloading properly. Really, test modules should be cleaning
|
||||
# out the test specific modules they know they added (ala test_runpy)
|
||||
# rather than relying on this function (as test_importhooks and test_pkg
|
||||
# do currently). Implicitly imported *real* modules should be left alone
|
||||
# (see issue 10556).
|
||||
sys.modules.update(oldmodules)
|
39
vendor/importlib_resources/tests/test_files.py
vendored
Normal file
39
vendor/importlib_resources/tests/test_files.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import typing
|
||||
import unittest
|
||||
|
||||
import importlib_resources as resources
|
||||
from importlib_resources.abc import Traversable
|
||||
from . import data01
|
||||
from . import util
|
||||
|
||||
|
||||
class FilesTests:
|
||||
def test_read_bytes(self):
|
||||
files = resources.files(self.data)
|
||||
actual = files.joinpath('utf-8.file').read_bytes()
|
||||
assert actual == b'Hello, UTF-8 world!\n'
|
||||
|
||||
def test_read_text(self):
|
||||
files = resources.files(self.data)
|
||||
actual = files.joinpath('utf-8.file').read_text()
|
||||
assert actual == 'Hello, UTF-8 world!\n'
|
||||
|
||||
@unittest.skipUnless(
|
||||
hasattr(typing, 'runtime_checkable'),
|
||||
"Only suitable when typing supports runtime_checkable",
|
||||
)
|
||||
def test_traversable(self):
|
||||
assert isinstance(resources.files(self.data), Traversable)
|
||||
|
||||
|
||||
class OpenDiskTests(FilesTests, unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.data = data01
|
||||
|
||||
|
||||
class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
84
vendor/importlib_resources/tests/test_open.py
vendored
Normal file
84
vendor/importlib_resources/tests/test_open.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import importlib_resources as resources
|
||||
from . import data01
|
||||
from . import util
|
||||
from .._compat import FileNotFoundError
|
||||
|
||||
|
||||
class CommonBinaryTests(util.CommonTests, unittest.TestCase):
|
||||
def execute(self, package, path):
|
||||
with resources.open_binary(package, path):
|
||||
pass
|
||||
|
||||
|
||||
class CommonTextTests(util.CommonTests, unittest.TestCase):
|
||||
def execute(self, package, path):
|
||||
with resources.open_text(package, path):
|
||||
pass
|
||||
|
||||
|
||||
class OpenTests:
|
||||
def test_open_binary(self):
|
||||
with resources.open_binary(self.data, 'utf-8.file') as fp:
|
||||
result = fp.read()
|
||||
self.assertEqual(result, b'Hello, UTF-8 world!\n')
|
||||
|
||||
def test_open_text_default_encoding(self):
|
||||
with resources.open_text(self.data, 'utf-8.file') as fp:
|
||||
result = fp.read()
|
||||
self.assertEqual(result, 'Hello, UTF-8 world!\n')
|
||||
|
||||
def test_open_text_given_encoding(self):
|
||||
with resources.open_text(
|
||||
self.data, 'utf-16.file', 'utf-16', 'strict') as fp:
|
||||
result = fp.read()
|
||||
self.assertEqual(result, 'Hello, UTF-16 world!\n')
|
||||
|
||||
def test_open_text_with_errors(self):
|
||||
# Raises UnicodeError without the 'errors' argument.
|
||||
with resources.open_text(
|
||||
self.data, 'utf-16.file', 'utf-8', 'strict') as fp:
|
||||
self.assertRaises(UnicodeError, fp.read)
|
||||
with resources.open_text(
|
||||
self.data, 'utf-16.file', 'utf-8', 'ignore') as fp:
|
||||
result = fp.read()
|
||||
self.assertEqual(
|
||||
result,
|
||||
'H\x00e\x00l\x00l\x00o\x00,\x00 '
|
||||
'\x00U\x00T\x00F\x00-\x001\x006\x00 '
|
||||
'\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00')
|
||||
|
||||
def test_open_binary_FileNotFoundError(self):
|
||||
self.assertRaises(
|
||||
FileNotFoundError,
|
||||
resources.open_binary, self.data, 'does-not-exist')
|
||||
|
||||
def test_open_text_FileNotFoundError(self):
|
||||
self.assertRaises(
|
||||
FileNotFoundError,
|
||||
resources.open_text, self.data, 'does-not-exist')
|
||||
|
||||
|
||||
class OpenDiskTests(OpenTests, unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.data = data01
|
||||
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.version_info[0] >= 3,
|
||||
'namespace packages not available on Python 2'
|
||||
)
|
||||
class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
|
||||
def setUp(self):
|
||||
from . import namespacedata01
|
||||
self.data = namespacedata01
|
||||
|
||||
|
||||
class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
51
vendor/importlib_resources/tests/test_path.py
vendored
Normal file
51
vendor/importlib_resources/tests/test_path.py
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
import unittest
|
||||
|
||||
import importlib_resources as resources
|
||||
from . import data01
|
||||
from . import util
|
||||
|
||||
|
||||
class CommonTests(util.CommonTests, unittest.TestCase):
|
||||
|
||||
def execute(self, package, path):
|
||||
with resources.path(package, path):
|
||||
pass
|
||||
|
||||
|
||||
class PathTests:
|
||||
|
||||
def test_reading(self):
|
||||
# Path should be readable.
|
||||
# Test also implicitly verifies the returned object is a pathlib.Path
|
||||
# instance.
|
||||
with resources.path(self.data, 'utf-8.file') as path:
|
||||
self.assertTrue(path.name.endswith("utf-8.file"), repr(path))
|
||||
# pathlib.Path.read_text() was introduced in Python 3.5.
|
||||
with path.open('r', encoding='utf-8') as file:
|
||||
text = file.read()
|
||||
self.assertEqual('Hello, UTF-8 world!\n', text)
|
||||
|
||||
|
||||
class PathDiskTests(PathTests, unittest.TestCase):
|
||||
data = data01
|
||||
|
||||
def test_natural_path(self):
|
||||
"""
|
||||
Guarantee the internal implementation detail that
|
||||
file-system-backed resources do not get the tempdir
|
||||
treatment.
|
||||
"""
|
||||
with resources.path(self.data, 'utf-8.file') as path:
|
||||
assert 'data' in str(path)
|
||||
|
||||
|
||||
class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase):
|
||||
def test_remove_in_context_manager(self):
|
||||
# It is not an error if the file that was temporarily stashed on the
|
||||
# file system is removed inside the `with` stanza.
|
||||
with resources.path(self.data, 'utf-8.file') as path:
|
||||
path.unlink()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
63
vendor/importlib_resources/tests/test_read.py
vendored
Normal file
63
vendor/importlib_resources/tests/test_read.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import unittest
|
||||
import importlib_resources as resources
|
||||
|
||||
from . import data01
|
||||
from . import util
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
class CommonBinaryTests(util.CommonTests, unittest.TestCase):
|
||||
def execute(self, package, path):
|
||||
resources.read_binary(package, path)
|
||||
|
||||
|
||||
class CommonTextTests(util.CommonTests, unittest.TestCase):
|
||||
def execute(self, package, path):
|
||||
resources.read_text(package, path)
|
||||
|
||||
|
||||
class ReadTests:
|
||||
def test_read_binary(self):
|
||||
result = resources.read_binary(self.data, 'binary.file')
|
||||
self.assertEqual(result, b'\0\1\2\3')
|
||||
|
||||
def test_read_text_default_encoding(self):
|
||||
result = resources.read_text(self.data, 'utf-8.file')
|
||||
self.assertEqual(result, 'Hello, UTF-8 world!\n')
|
||||
|
||||
def test_read_text_given_encoding(self):
|
||||
result = resources.read_text(
|
||||
self.data, 'utf-16.file', encoding='utf-16')
|
||||
self.assertEqual(result, 'Hello, UTF-16 world!\n')
|
||||
|
||||
def test_read_text_with_errors(self):
|
||||
# Raises UnicodeError without the 'errors' argument.
|
||||
self.assertRaises(
|
||||
UnicodeError, resources.read_text, self.data, 'utf-16.file')
|
||||
result = resources.read_text(self.data, 'utf-16.file', errors='ignore')
|
||||
self.assertEqual(
|
||||
result,
|
||||
'H\x00e\x00l\x00l\x00o\x00,\x00 '
|
||||
'\x00U\x00T\x00F\x00-\x001\x006\x00 '
|
||||
'\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00')
|
||||
|
||||
|
||||
class ReadDiskTests(ReadTests, unittest.TestCase):
|
||||
data = data01
|
||||
|
||||
|
||||
class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
|
||||
def test_read_submodule_resource(self):
|
||||
submodule = import_module('ziptestdata.subdirectory')
|
||||
result = resources.read_binary(
|
||||
submodule, 'binary.file')
|
||||
self.assertEqual(result, b'\0\1\2\3')
|
||||
|
||||
def test_read_submodule_resource_by_name(self):
|
||||
result = resources.read_binary(
|
||||
'ziptestdata.subdirectory', 'binary.file')
|
||||
self.assertEqual(result, b'\0\1\2\3')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
145
vendor/importlib_resources/tests/test_reader.py
vendored
Normal file
145
vendor/importlib_resources/tests/test_reader.py
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
import os.path
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from importlib import import_module
|
||||
from importlib_resources.readers import MultiplexedPath, NamespaceReader
|
||||
|
||||
from .._compat import FileNotFoundError, NotADirectoryError
|
||||
|
||||
|
||||
class MultiplexedPathTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.folder = os.path.abspath(
|
||||
os.path.join(__file__, '..', 'namespacedata01')
|
||||
)
|
||||
|
||||
def test_init_no_paths(self):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
MultiplexedPath()
|
||||
|
||||
def test_init_file(self):
|
||||
with self.assertRaises(NotADirectoryError):
|
||||
MultiplexedPath(os.path.join(self.folder, 'binary.file'))
|
||||
|
||||
def test_iterdir(self):
|
||||
contents = {
|
||||
path.name for path in MultiplexedPath(self.folder).iterdir()
|
||||
}
|
||||
try:
|
||||
contents.remove('__pycache__')
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
self.assertEqual(
|
||||
contents,
|
||||
{'binary.file', 'utf-16.file', 'utf-8.file'}
|
||||
)
|
||||
|
||||
def test_iterdir_duplicate(self):
|
||||
data01 = os.path.abspath(
|
||||
os.path.join(__file__, '..', 'data01')
|
||||
)
|
||||
contents = {
|
||||
path.name for path in
|
||||
MultiplexedPath(self.folder, data01).iterdir()
|
||||
}
|
||||
for remove in ('__pycache__', '__init__.pyc'):
|
||||
try:
|
||||
contents.remove(remove)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
self.assertEqual(contents, {
|
||||
'__init__.py',
|
||||
'binary.file',
|
||||
'subdirectory',
|
||||
'utf-16.file',
|
||||
'utf-8.file'
|
||||
})
|
||||
|
||||
def test_is_dir(self):
|
||||
self.assertEqual(MultiplexedPath(self.folder).is_dir(), True)
|
||||
|
||||
def test_is_file(self):
|
||||
self.assertEqual(MultiplexedPath(self.folder).is_file(), False)
|
||||
|
||||
def test_open_file(self):
|
||||
path = MultiplexedPath(self.folder)
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
path.read_bytes()
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
path.read_text()
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
path.open()
|
||||
|
||||
def test_join_path(self):
|
||||
print('test_join_path')
|
||||
prefix = os.path.abspath(os.path.join(__file__, '..'))
|
||||
data01 = os.path.join(prefix, 'data01')
|
||||
path = MultiplexedPath(self.folder, data01)
|
||||
self.assertEqual(
|
||||
str(path.joinpath('binary.file'))[len(prefix)+1:],
|
||||
os.path.join('namespacedata01', 'binary.file')
|
||||
)
|
||||
self.assertEqual(
|
||||
str(path.joinpath('subdirectory'))[len(prefix)+1:],
|
||||
os.path.join('data01', 'subdirectory')
|
||||
)
|
||||
self.assertEqual(
|
||||
str(path.joinpath('imaginary'))[len(prefix)+1:],
|
||||
os.path.join('namespacedata01', 'imaginary')
|
||||
)
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(
|
||||
repr(MultiplexedPath(self.folder)),
|
||||
"MultiplexedPath('{}')".format(self.folder)
|
||||
)
|
||||
|
||||
|
||||
class NamespaceReaderTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
|
||||
|
||||
def test_init_error(self):
|
||||
with self.assertRaises(ValueError):
|
||||
NamespaceReader(['path1', 'path2'])
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.version_info[0] >= 3,
|
||||
'namespace packages not available on Python 2'
|
||||
)
|
||||
def test_resource_path(self):
|
||||
namespacedata01 = import_module('namespacedata01')
|
||||
reader = NamespaceReader(
|
||||
namespacedata01.__spec__.submodule_search_locations
|
||||
)
|
||||
|
||||
root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
|
||||
self.assertEqual(reader.resource_path('binary.file'), os.path.join(
|
||||
root, 'binary.file'
|
||||
))
|
||||
self.assertEqual(reader.resource_path('imaginary'), os.path.join(
|
||||
root, 'imaginary'
|
||||
))
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.version_info[0] >= 3,
|
||||
'namespace packages not available on Python 2'
|
||||
)
|
||||
def test_files(self):
|
||||
namespacedata01 = import_module('namespacedata01')
|
||||
reader = NamespaceReader(
|
||||
namespacedata01.__spec__.submodule_search_locations
|
||||
)
|
||||
root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
|
||||
self.assertIsInstance(reader.files(), MultiplexedPath)
|
||||
self.assertEqual(
|
||||
repr(reader.files()),
|
||||
"MultiplexedPath('{}')".format(root)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
256
vendor/importlib_resources/tests/test_resource.py
vendored
Normal file
256
vendor/importlib_resources/tests/test_resource.py
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
import os.path
|
||||
import sys
|
||||
import unittest
|
||||
import importlib_resources as resources
|
||||
import uuid
|
||||
|
||||
from importlib_resources._compat import Path
|
||||
|
||||
from . import data01
|
||||
from . import zipdata01, zipdata02
|
||||
from . import util
|
||||
from importlib import import_module
|
||||
from ._compat import import_helper, unlink
|
||||
|
||||
|
||||
class ResourceTests:
|
||||
# Subclasses are expected to set the `data` attribute.
|
||||
|
||||
def test_is_resource_good_path(self):
|
||||
self.assertTrue(resources.is_resource(self.data, 'binary.file'))
|
||||
|
||||
def test_is_resource_missing(self):
|
||||
self.assertFalse(resources.is_resource(self.data, 'not-a-file'))
|
||||
|
||||
def test_is_resource_subresource_directory(self):
|
||||
# Directories are not resources.
|
||||
self.assertFalse(resources.is_resource(self.data, 'subdirectory'))
|
||||
|
||||
def test_contents(self):
|
||||
contents = set(resources.contents(self.data))
|
||||
# There may be cruft in the directory listing of the data directory.
|
||||
# Under Python 3 we could have a __pycache__ directory, and under
|
||||
# Python 2 we could have .pyc files. These are both artifacts of the
|
||||
# test suite importing these modules and writing these caches. They
|
||||
# aren't germane to this test, so just filter them out.
|
||||
contents.discard('__pycache__')
|
||||
contents.discard('__init__.pyc')
|
||||
contents.discard('__init__.pyo')
|
||||
self.assertEqual(contents, {
|
||||
'__init__.py',
|
||||
'subdirectory',
|
||||
'utf-8.file',
|
||||
'binary.file',
|
||||
'utf-16.file',
|
||||
})
|
||||
|
||||
|
||||
class ResourceDiskTests(ResourceTests, unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.data = data01
|
||||
|
||||
|
||||
class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3,), 'No ResourceReader in Python 2')
|
||||
class ResourceLoaderTests(unittest.TestCase):
|
||||
def test_resource_contents(self):
|
||||
package = util.create_package(
|
||||
file=data01, path=data01.__file__, contents=['A', 'B', 'C'])
|
||||
self.assertEqual(
|
||||
set(resources.contents(package)),
|
||||
{'A', 'B', 'C'})
|
||||
|
||||
def test_resource_is_resource(self):
|
||||
package = util.create_package(
|
||||
file=data01, path=data01.__file__,
|
||||
contents=['A', 'B', 'C', 'D/E', 'D/F'])
|
||||
self.assertTrue(resources.is_resource(package, 'B'))
|
||||
|
||||
def test_resource_directory_is_not_resource(self):
|
||||
package = util.create_package(
|
||||
file=data01, path=data01.__file__,
|
||||
contents=['A', 'B', 'C', 'D/E', 'D/F'])
|
||||
self.assertFalse(resources.is_resource(package, 'D'))
|
||||
|
||||
def test_resource_missing_is_not_resource(self):
|
||||
package = util.create_package(
|
||||
file=data01, path=data01.__file__,
|
||||
contents=['A', 'B', 'C', 'D/E', 'D/F'])
|
||||
self.assertFalse(resources.is_resource(package, 'Z'))
|
||||
|
||||
|
||||
class ResourceCornerCaseTests(unittest.TestCase):
|
||||
def test_package_has_no_reader_fallback(self):
|
||||
# Test odd ball packages which:
|
||||
# 1. Do not have a ResourceReader as a loader
|
||||
# 2. Are not on the file system
|
||||
# 3. Are not in a zip file
|
||||
module = util.create_package(
|
||||
file=data01, path=data01.__file__, contents=['A', 'B', 'C'])
|
||||
# Give the module a dummy loader.
|
||||
module.__loader__ = object()
|
||||
# Give the module a dummy origin.
|
||||
module.__file__ = '/path/which/shall/not/be/named'
|
||||
if sys.version_info >= (3,):
|
||||
module.__spec__.loader = module.__loader__
|
||||
module.__spec__.origin = module.__file__
|
||||
self.assertFalse(resources.is_resource(module, 'A'))
|
||||
|
||||
|
||||
class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
|
||||
ZIP_MODULE = zipdata01 # type: ignore
|
||||
|
||||
def test_is_submodule_resource(self):
|
||||
submodule = import_module('ziptestdata.subdirectory')
|
||||
self.assertTrue(
|
||||
resources.is_resource(submodule, 'binary.file'))
|
||||
|
||||
def test_read_submodule_resource_by_name(self):
|
||||
self.assertTrue(
|
||||
resources.is_resource('ziptestdata.subdirectory', 'binary.file'))
|
||||
|
||||
def test_submodule_contents(self):
|
||||
submodule = import_module('ziptestdata.subdirectory')
|
||||
self.assertEqual(
|
||||
set(resources.contents(submodule)),
|
||||
{'__init__.py', 'binary.file'})
|
||||
|
||||
def test_submodule_contents_by_name(self):
|
||||
self.assertEqual(
|
||||
set(resources.contents('ziptestdata.subdirectory')),
|
||||
{'__init__.py', 'binary.file'})
|
||||
|
||||
|
||||
class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
|
||||
ZIP_MODULE = zipdata02 # type: ignore
|
||||
|
||||
def test_unrelated_contents(self):
|
||||
"""
|
||||
Test thata zip with two unrelated subpackages return
|
||||
distinct resources. Ref python/importlib_resources#44.
|
||||
"""
|
||||
self.assertEqual(
|
||||
set(resources.contents('ziptestdata.one')),
|
||||
{'__init__.py', 'resource1.txt'})
|
||||
self.assertEqual(
|
||||
set(resources.contents('ziptestdata.two')),
|
||||
{'__init__.py', 'resource2.txt'})
|
||||
|
||||
|
||||
class DeletingZipsTest(unittest.TestCase):
|
||||
"""Having accessed resources in a zip file should not keep an open
|
||||
reference to the zip.
|
||||
"""
|
||||
ZIP_MODULE = zipdata01
|
||||
|
||||
def setUp(self):
|
||||
modules = import_helper.modules_setup()
|
||||
self.addCleanup(import_helper.modules_cleanup, *modules)
|
||||
|
||||
data_path = Path(self.ZIP_MODULE.__file__)
|
||||
data_dir = data_path.parent
|
||||
self.source_zip_path = data_dir / 'ziptestdata.zip'
|
||||
self.zip_path = Path('{}.zip'.format(uuid.uuid4())).absolute()
|
||||
self.zip_path.write_bytes(self.source_zip_path.read_bytes())
|
||||
sys.path.append(str(self.zip_path))
|
||||
self.data = import_module('ziptestdata')
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
sys.path.remove(str(self.zip_path))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
del sys.path_importer_cache[str(self.zip_path)]
|
||||
del sys.modules[self.data.__name__]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
unlink(self.zip_path)
|
||||
except OSError:
|
||||
# If the test fails, this will probably fail too
|
||||
pass
|
||||
|
||||
def test_contents_does_not_keep_open(self):
|
||||
c = resources.contents('ziptestdata')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
def test_is_resource_does_not_keep_open(self):
|
||||
c = resources.is_resource('ziptestdata', 'binary.file')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
def test_is_resource_failure_does_not_keep_open(self):
|
||||
c = resources.is_resource('ziptestdata', 'not-present')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
@unittest.skip("Desired but not supported.")
|
||||
def test_path_does_not_keep_open(self):
|
||||
c = resources.path('ziptestdata', 'binary.file')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
def test_entered_path_does_not_keep_open(self):
|
||||
# This is what certifi does on import to make its bundle
|
||||
# available for the process duration.
|
||||
c = resources.path('ziptestdata', 'binary.file').__enter__()
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
def test_read_binary_does_not_keep_open(self):
|
||||
c = resources.read_binary('ziptestdata', 'binary.file')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
def test_read_text_does_not_keep_open(self):
|
||||
c = resources.read_text('ziptestdata', 'utf-8.file', encoding='utf-8')
|
||||
self.zip_path.unlink()
|
||||
del c
|
||||
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.version_info[0] >= 3,
|
||||
'namespace packages not available on Python 2'
|
||||
)
|
||||
class ResourceFromNamespaceTest01(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
sys.path.append(os.path.abspath(os.path.join(__file__, '..')))
|
||||
|
||||
def test_is_submodule_resource(self):
|
||||
self.assertTrue(
|
||||
resources.is_resource(
|
||||
import_module('namespacedata01'), 'binary.file'))
|
||||
|
||||
def test_read_submodule_resource_by_name(self):
|
||||
self.assertTrue(
|
||||
resources.is_resource('namespacedata01', 'binary.file'))
|
||||
|
||||
def test_submodule_contents(self):
|
||||
contents = set(resources.contents(import_module('namespacedata01')))
|
||||
try:
|
||||
contents.remove('__pycache__')
|
||||
except KeyError:
|
||||
pass
|
||||
self.assertEqual(
|
||||
contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
|
||||
|
||||
def test_submodule_contents_by_name(self):
|
||||
contents = set(resources.contents('namespacedata01'))
|
||||
try:
|
||||
contents.remove('__pycache__')
|
||||
except KeyError:
|
||||
pass
|
||||
self.assertEqual(
|
||||
contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
190
vendor/importlib_resources/tests/util.py
vendored
Normal file
190
vendor/importlib_resources/tests/util.py
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
import abc
|
||||
import importlib
|
||||
import io
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
|
||||
from . import data01
|
||||
from . import zipdata01
|
||||
from .._compat import ABC, Path, PurePath, FileNotFoundError
|
||||
from ..abc import ResourceReader
|
||||
from ._compat import import_helper
|
||||
|
||||
|
||||
try:
|
||||
from importlib.machinery import ModuleSpec
|
||||
except ImportError:
|
||||
ModuleSpec = None # type: ignore
|
||||
|
||||
|
||||
def create_package(file, path, is_package=True, contents=()):
|
||||
class Reader(ResourceReader):
|
||||
def get_resource_reader(self, package):
|
||||
return self
|
||||
|
||||
def open_resource(self, path):
|
||||
self._path = path
|
||||
if isinstance(file, Exception):
|
||||
raise file
|
||||
else:
|
||||
return file
|
||||
|
||||
def resource_path(self, path_):
|
||||
self._path = path_
|
||||
if isinstance(path, Exception):
|
||||
raise path
|
||||
else:
|
||||
return path
|
||||
|
||||
def is_resource(self, path_):
|
||||
self._path = path_
|
||||
if isinstance(path, Exception):
|
||||
raise path
|
||||
for entry in contents:
|
||||
parts = entry.split('/')
|
||||
if len(parts) == 1 and parts[0] == path_:
|
||||
return True
|
||||
return False
|
||||
|
||||
def contents(self):
|
||||
if isinstance(path, Exception):
|
||||
raise path
|
||||
# There's no yield from in baseball, er, Python 2.
|
||||
for entry in contents:
|
||||
yield entry
|
||||
|
||||
name = 'testingpackage'
|
||||
# Unforunately importlib.util.module_from_spec() was not introduced until
|
||||
# Python 3.5.
|
||||
module = types.ModuleType(name)
|
||||
if ModuleSpec is None:
|
||||
# Python 2.
|
||||
module.__name__ = name
|
||||
module.__file__ = 'does-not-exist'
|
||||
if is_package:
|
||||
module.__path__ = []
|
||||
else:
|
||||
# Python 3.
|
||||
loader = Reader()
|
||||
spec = ModuleSpec(
|
||||
name, loader,
|
||||
origin='does-not-exist',
|
||||
is_package=is_package)
|
||||
module.__spec__ = spec
|
||||
module.__loader__ = loader
|
||||
return module
|
||||
|
||||
|
||||
class CommonTests(ABC):
|
||||
|
||||
@abc.abstractmethod
|
||||
def execute(self, package, path):
|
||||
raise NotImplementedError
|
||||
|
||||
def test_package_name(self):
|
||||
# Passing in the package name should succeed.
|
||||
self.execute(data01.__name__, 'utf-8.file')
|
||||
|
||||
def test_package_object(self):
|
||||
# Passing in the package itself should succeed.
|
||||
self.execute(data01, 'utf-8.file')
|
||||
|
||||
def test_string_path(self):
|
||||
# Passing in a string for the path should succeed.
|
||||
path = 'utf-8.file'
|
||||
self.execute(data01, path)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3, 6), 'requires os.PathLike support')
|
||||
def test_pathlib_path(self):
|
||||
# Passing in a pathlib.PurePath object for the path should succeed.
|
||||
path = PurePath('utf-8.file')
|
||||
self.execute(data01, path)
|
||||
|
||||
def test_absolute_path(self):
|
||||
# An absolute path is a ValueError.
|
||||
path = Path(__file__)
|
||||
full_path = path.parent/'utf-8.file'
|
||||
with self.assertRaises(ValueError):
|
||||
self.execute(data01, full_path)
|
||||
|
||||
def test_relative_path(self):
|
||||
# A reative path is a ValueError.
|
||||
with self.assertRaises(ValueError):
|
||||
self.execute(data01, '../data01/utf-8.file')
|
||||
|
||||
def test_importing_module_as_side_effect(self):
|
||||
# The anchor package can already be imported.
|
||||
del sys.modules[data01.__name__]
|
||||
self.execute(data01.__name__, 'utf-8.file')
|
||||
|
||||
def test_non_package_by_name(self):
|
||||
# The anchor package cannot be a module.
|
||||
with self.assertRaises(TypeError):
|
||||
self.execute(__name__, 'utf-8.file')
|
||||
|
||||
def test_non_package_by_package(self):
|
||||
# The anchor package cannot be a module.
|
||||
with self.assertRaises(TypeError):
|
||||
module = sys.modules['importlib_resources.tests.util']
|
||||
self.execute(module, 'utf-8.file')
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3,), 'No ResourceReader in Python 2')
|
||||
def test_resource_opener(self):
|
||||
bytes_data = io.BytesIO(b'Hello, world!')
|
||||
package = create_package(file=bytes_data, path=FileNotFoundError())
|
||||
self.execute(package, 'utf-8.file')
|
||||
self.assertEqual(package.__loader__._path, 'utf-8.file')
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3,), 'No ResourceReader in Python 2')
|
||||
def test_resource_path(self):
|
||||
bytes_data = io.BytesIO(b'Hello, world!')
|
||||
path = __file__
|
||||
package = create_package(file=bytes_data, path=path)
|
||||
self.execute(package, 'utf-8.file')
|
||||
self.assertEqual(package.__loader__._path, 'utf-8.file')
|
||||
|
||||
def test_useless_loader(self):
|
||||
package = create_package(file=FileNotFoundError(),
|
||||
path=FileNotFoundError())
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
self.execute(package, 'utf-8.file')
|
||||
|
||||
|
||||
class ZipSetupBase:
|
||||
ZIP_MODULE = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
data_path = Path(cls.ZIP_MODULE.__file__)
|
||||
data_dir = data_path.parent
|
||||
cls._zip_path = str(data_dir / 'ziptestdata.zip')
|
||||
sys.path.append(cls._zip_path)
|
||||
cls.data = importlib.import_module('ziptestdata')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
sys.path.remove(cls._zip_path)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
del sys.path_importer_cache[cls._zip_path]
|
||||
del sys.modules[cls.data.__name__]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
del cls.data
|
||||
del cls._zip_path
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def setUp(self):
|
||||
modules = import_helper.modules_setup()
|
||||
self.addCleanup(import_helper.modules_cleanup, *modules)
|
||||
|
||||
|
||||
class ZipSetup(ZipSetupBase):
|
||||
ZIP_MODULE = zipdata01 # type: ignore
|
0
vendor/importlib_resources/tests/zipdata01/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/zipdata01/__init__.py
vendored
Normal file
BIN
vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
vendored
Normal file
BIN
vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
vendored
Normal file
Binary file not shown.
0
vendor/importlib_resources/tests/zipdata02/__init__.py
vendored
Normal file
0
vendor/importlib_resources/tests/zipdata02/__init__.py
vendored
Normal file
BIN
vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
vendored
Normal file
BIN
vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
vendored
Normal file
Binary file not shown.
6
vendor/importlib_resources/trees.py
vendored
Normal file
6
vendor/importlib_resources/trees.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# for compatibility with 1.1, continue to expose as_file here.
|
||||
|
||||
from ._common import as_file
|
||||
|
||||
|
||||
__all__ = ['as_file']
|
Reference in New Issue
Block a user