mirror of https://github.com/python/cpython
gh-97930: Apply changes from importlib_resources 5.10. (GH-100598)
This commit is contained in:
parent
ba1342ce99
commit
447d061bc7
|
@ -14,12 +14,13 @@ This module leverages Python's import system to provide access to *resources*
|
||||||
within *packages*.
|
within *packages*.
|
||||||
|
|
||||||
"Resources" are file-like resources associated with a module or package in
|
"Resources" are file-like resources associated with a module or package in
|
||||||
Python. The resources may be contained directly in a package or within a
|
Python. The resources may be contained directly in a package, within a
|
||||||
subdirectory contained in that package. Resources may be text or binary. As a
|
subdirectory contained in that package, or adjacent to modules outside a
|
||||||
result, Python module sources (.py) of a package and compilation artifacts
|
package. Resources may be text or binary. As a result, Python module sources
|
||||||
(pycache) are technically de-facto resources of that package. In practice,
|
(.py) of a package and compilation artifacts (pycache) are technically
|
||||||
however, resources are primarily those non-Python artifacts exposed
|
de-facto resources of that package. In practice, however, resources are
|
||||||
specifically by the package author.
|
primarily those non-Python artifacts exposed specifically by the package
|
||||||
|
author.
|
||||||
|
|
||||||
Resources can be opened or read in either binary or text mode.
|
Resources can be opened or read in either binary or text mode.
|
||||||
|
|
||||||
|
@ -49,27 +50,35 @@ for example, a package and its resources can be imported from a zip file using
|
||||||
``get_resource_reader(fullname)`` method as specified by
|
``get_resource_reader(fullname)`` method as specified by
|
||||||
:class:`importlib.resources.abc.ResourceReader`.
|
:class:`importlib.resources.abc.ResourceReader`.
|
||||||
|
|
||||||
.. data:: Package
|
.. data:: Anchor
|
||||||
|
|
||||||
Whenever a function accepts a ``Package`` argument, you can pass in
|
Represents an anchor for resources, either a :class:`module object
|
||||||
either a :class:`module object <types.ModuleType>` or a module name
|
<types.ModuleType>` or a module name as a string. Defined as
|
||||||
as a string. You can only pass module objects whose
|
``Union[str, ModuleType]``.
|
||||||
``__spec__.submodule_search_locations`` is not ``None``.
|
|
||||||
|
|
||||||
The ``Package`` type is defined as ``Union[str, ModuleType]``.
|
.. function:: files(anchor: Optional[Anchor] = None)
|
||||||
|
|
||||||
.. function:: files(package)
|
|
||||||
|
|
||||||
Returns a :class:`~importlib.resources.abc.Traversable` object
|
Returns a :class:`~importlib.resources.abc.Traversable` object
|
||||||
representing the resource container for the package (think directory)
|
representing the resource container (think directory) and its resources
|
||||||
and its resources (think files). A Traversable may contain other
|
(think files). A Traversable may contain other containers (think
|
||||||
containers (think subdirectories).
|
subdirectories).
|
||||||
|
|
||||||
*package* is either a name or a module object which conforms to the
|
*anchor* is an optional :data:`Anchor`. If the anchor is a
|
||||||
:data:`Package` requirements.
|
package, resources are resolved from that package. If a module,
|
||||||
|
resources are resolved adjacent to that module (in the same package
|
||||||
|
or the package root). If the anchor is omitted, the caller's module
|
||||||
|
is used.
|
||||||
|
|
||||||
.. versionadded:: 3.9
|
.. versionadded:: 3.9
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12
|
||||||
|
"package" parameter was renamed to "anchor". "anchor" can now
|
||||||
|
be a non-package module and if omitted will default to the caller's
|
||||||
|
module. "package" is still accepted for compatibility but will raise
|
||||||
|
a DeprecationWarning. Consider passing the anchor positionally or
|
||||||
|
using ``importlib_resources >= 5.10`` for a compatible interface
|
||||||
|
on older Pythons.
|
||||||
|
|
||||||
.. function:: as_file(traversable)
|
.. function:: as_file(traversable)
|
||||||
|
|
||||||
Given a :class:`~importlib.resources.abc.Traversable` object representing
|
Given a :class:`~importlib.resources.abc.Traversable` object representing
|
||||||
|
@ -86,6 +95,7 @@ for example, a package and its resources can be imported from a zip file using
|
||||||
|
|
||||||
.. versionadded:: 3.9
|
.. versionadded:: 3.9
|
||||||
|
|
||||||
|
|
||||||
Deprecated functions
|
Deprecated functions
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -94,6 +104,18 @@ scheduled for removal in a future version of Python.
|
||||||
The main drawback of these functions is that they do not support
|
The main drawback of these functions is that they do not support
|
||||||
directories: they assume all resources are located directly within a *package*.
|
directories: they assume all resources are located directly within a *package*.
|
||||||
|
|
||||||
|
.. data:: Package
|
||||||
|
|
||||||
|
Whenever a function accepts a ``Package`` argument, you can pass in
|
||||||
|
either a :class:`module object <types.ModuleType>` or a module name
|
||||||
|
as a string. You can only pass module objects whose
|
||||||
|
``__spec__.submodule_search_locations`` is not ``None``.
|
||||||
|
|
||||||
|
The ``Package`` type is defined as ``Union[str, ModuleType]``.
|
||||||
|
|
||||||
|
.. deprecated:: 3.12
|
||||||
|
|
||||||
|
|
||||||
.. data:: Resource
|
.. data:: Resource
|
||||||
|
|
||||||
For *resource* arguments of the functions below, you can pass in
|
For *resource* arguments of the functions below, you can pass in
|
||||||
|
@ -102,6 +124,7 @@ directories: they assume all resources are located directly within a *package*.
|
||||||
|
|
||||||
The ``Resource`` type is defined as ``Union[str, os.PathLike]``.
|
The ``Resource`` type is defined as ``Union[str, os.PathLike]``.
|
||||||
|
|
||||||
|
|
||||||
.. function:: open_binary(package, resource)
|
.. function:: open_binary(package, resource)
|
||||||
|
|
||||||
Open for binary reading the *resource* within *package*.
|
Open for binary reading the *resource* within *package*.
|
||||||
|
|
|
@ -5,25 +5,58 @@ import functools
|
||||||
import contextlib
|
import contextlib
|
||||||
import types
|
import types
|
||||||
import importlib
|
import importlib
|
||||||
|
import inspect
|
||||||
|
import warnings
|
||||||
|
import itertools
|
||||||
|
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional, cast
|
||||||
from .abc import ResourceReader, Traversable
|
from .abc import ResourceReader, Traversable
|
||||||
|
|
||||||
from ._adapters import wrap_spec
|
from ._adapters import wrap_spec
|
||||||
|
|
||||||
Package = Union[types.ModuleType, str]
|
Package = Union[types.ModuleType, str]
|
||||||
|
Anchor = Package
|
||||||
|
|
||||||
|
|
||||||
def files(package):
|
def package_to_anchor(func):
|
||||||
# type: (Package) -> Traversable
|
|
||||||
"""
|
"""
|
||||||
Get a Traversable resource from a package
|
Replace 'package' parameter as 'anchor' and warn about the change.
|
||||||
|
|
||||||
|
Other errors should fall through.
|
||||||
|
|
||||||
|
>>> files('a', 'b')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
TypeError: files() takes from 0 to 1 positional arguments but 2 were given
|
||||||
"""
|
"""
|
||||||
return from_package(get_package(package))
|
undefined = object()
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(anchor=undefined, package=undefined):
|
||||||
|
if package is not undefined:
|
||||||
|
if anchor is not undefined:
|
||||||
|
return func(anchor, package)
|
||||||
|
warnings.warn(
|
||||||
|
"First parameter to files is renamed to 'anchor'",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return func(package)
|
||||||
|
elif anchor is undefined:
|
||||||
|
return func()
|
||||||
|
return func(anchor)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def get_resource_reader(package):
|
@package_to_anchor
|
||||||
# type: (types.ModuleType) -> Optional[ResourceReader]
|
def files(anchor: Optional[Anchor] = None) -> Traversable:
|
||||||
|
"""
|
||||||
|
Get a Traversable resource for an anchor.
|
||||||
|
"""
|
||||||
|
return from_package(resolve(anchor))
|
||||||
|
|
||||||
|
|
||||||
|
def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
|
||||||
"""
|
"""
|
||||||
Return the package's loader if it's a ResourceReader.
|
Return the package's loader if it's a ResourceReader.
|
||||||
"""
|
"""
|
||||||
|
@ -39,24 +72,39 @@ def get_resource_reader(package):
|
||||||
return reader(spec.name) # type: ignore
|
return reader(spec.name) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def resolve(cand):
|
@functools.singledispatch
|
||||||
# type: (Package) -> types.ModuleType
|
def resolve(cand: Optional[Anchor]) -> types.ModuleType:
|
||||||
return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
|
return cast(types.ModuleType, cand)
|
||||||
|
|
||||||
|
|
||||||
def get_package(package):
|
@resolve.register
|
||||||
# type: (Package) -> types.ModuleType
|
def _(cand: str) -> types.ModuleType:
|
||||||
"""Take a package name or module object and return the module.
|
return importlib.import_module(cand)
|
||||||
|
|
||||||
Raise an exception if the resolved module is not a package.
|
|
||||||
|
@resolve.register
|
||||||
|
def _(cand: None) -> types.ModuleType:
|
||||||
|
return resolve(_infer_caller().f_globals['__name__'])
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_caller():
|
||||||
"""
|
"""
|
||||||
resolved = resolve(package)
|
Walk the stack and find the frame of the first caller not in this module.
|
||||||
if wrap_spec(resolved).submodule_search_locations is None:
|
"""
|
||||||
raise TypeError(f'{package!r} is not a package')
|
|
||||||
return resolved
|
def is_this_file(frame_info):
|
||||||
|
return frame_info.filename == __file__
|
||||||
|
|
||||||
|
def is_wrapper(frame_info):
|
||||||
|
return frame_info.function == 'wrapper'
|
||||||
|
|
||||||
|
not_this_file = itertools.filterfalse(is_this_file, inspect.stack())
|
||||||
|
# also exclude 'wrapper' due to singledispatch in the call stack
|
||||||
|
callers = itertools.filterfalse(is_wrapper, not_this_file)
|
||||||
|
return next(callers).frame
|
||||||
|
|
||||||
|
|
||||||
def from_package(package):
|
def from_package(package: types.ModuleType):
|
||||||
"""
|
"""
|
||||||
Return a Traversable object for the given package.
|
Return a Traversable object for the given package.
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ def deprecated(func):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def normalize_path(path):
|
def normalize_path(path: Any) -> str:
|
||||||
# type: (Any) -> str
|
|
||||||
"""Normalize a path by ensuring it is a string.
|
"""Normalize a path by ensuring it is a string.
|
||||||
|
|
||||||
If the resulting string contains path separators, an exception is raised.
|
If the resulting string contains path separators, an exception is raised.
|
||||||
|
|
|
@ -142,7 +142,8 @@ class Traversable(Protocol):
|
||||||
accepted by io.TextIOWrapper.
|
accepted by io.TextIOWrapper.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""
|
"""
|
||||||
The base name of this object without any parent references.
|
The base name of this object without any parent references.
|
||||||
|
|
|
@ -16,31 +16,28 @@ class SimpleReader(abc.ABC):
|
||||||
provider.
|
provider.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractproperty
|
@property
|
||||||
def package(self):
|
@abc.abstractmethod
|
||||||
# type: () -> str
|
def package(self) -> str:
|
||||||
"""
|
"""
|
||||||
The name of the package for which this reader loads resources.
|
The name of the package for which this reader loads resources.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def children(self):
|
def children(self) -> List['SimpleReader']:
|
||||||
# type: () -> List['SimpleReader']
|
|
||||||
"""
|
"""
|
||||||
Obtain an iterable of SimpleReader for available
|
Obtain an iterable of SimpleReader for available
|
||||||
child containers (e.g. directories).
|
child containers (e.g. directories).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def resources(self):
|
def resources(self) -> List[str]:
|
||||||
# type: () -> List[str]
|
|
||||||
"""
|
"""
|
||||||
Obtain available named resources for this virtual package.
|
Obtain available named resources for this virtual package.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def open_binary(self, resource):
|
def open_binary(self, resource: str) -> BinaryIO:
|
||||||
# type: (str) -> BinaryIO
|
|
||||||
"""
|
"""
|
||||||
Obtain a File-like for a named resource.
|
Obtain a File-like for a named resource.
|
||||||
"""
|
"""
|
||||||
|
@ -50,13 +47,35 @@ class SimpleReader(abc.ABC):
|
||||||
return self.package.split('.')[-1]
|
return self.package.split('.')[-1]
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceContainer(Traversable):
|
||||||
|
"""
|
||||||
|
Traversable container for a package's resources via its reader.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, reader: SimpleReader):
|
||||||
|
self.reader = reader
|
||||||
|
|
||||||
|
def is_dir(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_file(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def iterdir(self):
|
||||||
|
files = (ResourceHandle(self, name) for name in self.reader.resources)
|
||||||
|
dirs = map(ResourceContainer, self.reader.children())
|
||||||
|
return itertools.chain(files, dirs)
|
||||||
|
|
||||||
|
def open(self, *args, **kwargs):
|
||||||
|
raise IsADirectoryError()
|
||||||
|
|
||||||
|
|
||||||
class ResourceHandle(Traversable):
|
class ResourceHandle(Traversable):
|
||||||
"""
|
"""
|
||||||
Handle to a named resource in a ResourceReader.
|
Handle to a named resource in a ResourceReader.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent, name):
|
def __init__(self, parent: ResourceContainer, name: str):
|
||||||
# type: (ResourceContainer, str) -> None
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.name = name # type: ignore
|
self.name = name # type: ignore
|
||||||
|
|
||||||
|
@ -76,30 +95,6 @@ class ResourceHandle(Traversable):
|
||||||
raise RuntimeError("Cannot traverse into a resource")
|
raise RuntimeError("Cannot traverse into a resource")
|
||||||
|
|
||||||
|
|
||||||
class ResourceContainer(Traversable):
|
|
||||||
"""
|
|
||||||
Traversable container for a package's resources via its reader.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, reader):
|
|
||||||
# type: (SimpleReader) -> None
|
|
||||||
self.reader = reader
|
|
||||||
|
|
||||||
def is_dir(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_file(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def iterdir(self):
|
|
||||||
files = (ResourceHandle(self, name) for name in self.reader.resources)
|
|
||||||
dirs = map(ResourceContainer, self.reader.children())
|
|
||||||
return itertools.chain(files, dirs)
|
|
||||||
|
|
||||||
def open(self, *args, **kwargs):
|
|
||||||
raise IsADirectoryError()
|
|
||||||
|
|
||||||
|
|
||||||
class TraversableReader(TraversableResources, SimpleReader):
|
class TraversableReader(TraversableResources, SimpleReader):
|
||||||
"""
|
"""
|
||||||
A TraversableResources based on SimpleReader. Resource providers
|
A TraversableResources based on SimpleReader. Resource providers
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import pathlib
|
||||||
|
import functools
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
# from jaraco.path 3.4
|
||||||
|
|
||||||
|
|
||||||
|
def build(spec, prefix=pathlib.Path()):
|
||||||
|
"""
|
||||||
|
Build a set of files/directories, as described by the spec.
|
||||||
|
|
||||||
|
Each key represents a pathname, and the value represents
|
||||||
|
the content. Content may be a nested directory.
|
||||||
|
|
||||||
|
>>> spec = {
|
||||||
|
... 'README.txt': "A README file",
|
||||||
|
... "foo": {
|
||||||
|
... "__init__.py": "",
|
||||||
|
... "bar": {
|
||||||
|
... "__init__.py": "",
|
||||||
|
... },
|
||||||
|
... "baz.py": "# Some code",
|
||||||
|
... }
|
||||||
|
... }
|
||||||
|
>>> tmpdir = getfixture('tmpdir')
|
||||||
|
>>> build(spec, tmpdir)
|
||||||
|
"""
|
||||||
|
for name, contents in spec.items():
|
||||||
|
create(contents, pathlib.Path(prefix) / name)
|
||||||
|
|
||||||
|
|
||||||
|
@functools.singledispatch
|
||||||
|
def create(content, path):
|
||||||
|
path.mkdir(exist_ok=True)
|
||||||
|
build(content, prefix=path) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@create.register
|
||||||
|
def _(content: bytes, path):
|
||||||
|
path.write_bytes(content)
|
||||||
|
|
||||||
|
|
||||||
|
@create.register
|
||||||
|
def _(content: str, path):
|
||||||
|
path.write_text(content)
|
||||||
|
|
||||||
|
|
||||||
|
# end from jaraco.path
|
||||||
|
####
|
|
@ -1,10 +1,24 @@
|
||||||
import typing
|
import typing
|
||||||
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
import importlib
|
||||||
|
import contextlib
|
||||||
|
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
from importlib.resources.abc import Traversable
|
from importlib.resources.abc import Traversable
|
||||||
from . import data01
|
from . import data01
|
||||||
from . import util
|
from . import util
|
||||||
|
from . import _path
|
||||||
|
from test.support import os_helper
|
||||||
|
from test.support import import_helper
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def suppress_known_deprecation():
|
||||||
|
with warnings.catch_warnings(record=True) as ctx:
|
||||||
|
warnings.simplefilter('default', category=DeprecationWarning)
|
||||||
|
yield ctx
|
||||||
|
|
||||||
|
|
||||||
class FilesTests:
|
class FilesTests:
|
||||||
|
@ -25,6 +39,14 @@ class FilesTests:
|
||||||
def test_traversable(self):
|
def test_traversable(self):
|
||||||
assert isinstance(resources.files(self.data), Traversable)
|
assert isinstance(resources.files(self.data), Traversable)
|
||||||
|
|
||||||
|
def test_old_parameter(self):
|
||||||
|
"""
|
||||||
|
Files used to take a 'package' parameter. Make sure anyone
|
||||||
|
passing by name is still supported.
|
||||||
|
"""
|
||||||
|
with suppress_known_deprecation():
|
||||||
|
resources.files(package=self.data)
|
||||||
|
|
||||||
|
|
||||||
class OpenDiskTests(FilesTests, unittest.TestCase):
|
class OpenDiskTests(FilesTests, unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -42,5 +64,50 @@ class OpenNamespaceTests(FilesTests, unittest.TestCase):
|
||||||
self.data = namespacedata01
|
self.data = namespacedata01
|
||||||
|
|
||||||
|
|
||||||
|
class SiteDir:
|
||||||
|
def setUp(self):
|
||||||
|
self.fixtures = contextlib.ExitStack()
|
||||||
|
self.addCleanup(self.fixtures.close)
|
||||||
|
self.site_dir = self.fixtures.enter_context(os_helper.temp_dir())
|
||||||
|
self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir))
|
||||||
|
self.fixtures.enter_context(import_helper.CleanImport())
|
||||||
|
|
||||||
|
|
||||||
|
class ModulesFilesTests(SiteDir, unittest.TestCase):
|
||||||
|
def test_module_resources(self):
|
||||||
|
"""
|
||||||
|
A module can have resources found adjacent to the module.
|
||||||
|
"""
|
||||||
|
spec = {
|
||||||
|
'mod.py': '',
|
||||||
|
'res.txt': 'resources are the best',
|
||||||
|
}
|
||||||
|
_path.build(spec, self.site_dir)
|
||||||
|
import mod
|
||||||
|
|
||||||
|
actual = resources.files(mod).joinpath('res.txt').read_text()
|
||||||
|
assert actual == spec['res.txt']
|
||||||
|
|
||||||
|
|
||||||
|
class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
|
||||||
|
def test_implicit_files(self):
|
||||||
|
"""
|
||||||
|
Without any parameter, files() will infer the location as the caller.
|
||||||
|
"""
|
||||||
|
spec = {
|
||||||
|
'somepkg': {
|
||||||
|
'__init__.py': textwrap.dedent(
|
||||||
|
"""
|
||||||
|
import importlib.resources as res
|
||||||
|
val = res.files().joinpath('res.txt').read_text()
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
'res.txt': 'resources are the best',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_path.build(spec, self.site_dir)
|
||||||
|
assert importlib.import_module('somepkg').val == 'resources are the best'
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -3,7 +3,7 @@ import importlib
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
from pathlib import Path, PurePath
|
import pathlib
|
||||||
|
|
||||||
from . import data01
|
from . import data01
|
||||||
from . import zipdata01
|
from . import zipdata01
|
||||||
|
@ -94,7 +94,7 @@ class CommonTests(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
def test_pathlib_path(self):
|
def test_pathlib_path(self):
|
||||||
# Passing in a pathlib.PurePath object for the path should succeed.
|
# Passing in a pathlib.PurePath object for the path should succeed.
|
||||||
path = PurePath('utf-8.file')
|
path = pathlib.PurePath('utf-8.file')
|
||||||
self.execute(data01, path)
|
self.execute(data01, path)
|
||||||
|
|
||||||
def test_importing_module_as_side_effect(self):
|
def test_importing_module_as_side_effect(self):
|
||||||
|
@ -102,17 +102,6 @@ class CommonTests(metaclass=abc.ABCMeta):
|
||||||
del sys.modules[data01.__name__]
|
del sys.modules[data01.__name__]
|
||||||
self.execute(data01.__name__, 'utf-8.file')
|
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['test.test_importlib.resources.util']
|
|
||||||
self.execute(module, 'utf-8.file')
|
|
||||||
|
|
||||||
def test_missing_path(self):
|
def test_missing_path(self):
|
||||||
# Attempting to open or read or request the path for a
|
# Attempting to open or read or request the path for a
|
||||||
# non-existent path should succeed if open_resource
|
# non-existent path should succeed if open_resource
|
||||||
|
@ -144,7 +133,7 @@ class ZipSetupBase:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
data_path = Path(cls.ZIP_MODULE.__file__)
|
data_path = pathlib.Path(cls.ZIP_MODULE.__file__)
|
||||||
data_dir = data_path.parent
|
data_dir = data_path.parent
|
||||||
cls._zip_path = str(data_dir / 'ziptestdata.zip')
|
cls._zip_path = str(data_dir / 'ziptestdata.zip')
|
||||||
sys.path.append(cls._zip_path)
|
sys.path.append(cls._zip_path)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
``importlib.resources.files`` now accepts a module as an anchor instead of
|
||||||
|
only accepting packages. If a module is passed, resources are resolved
|
||||||
|
adjacent to that module (in the same package or at the package root). The
|
||||||
|
parameter was renamed from ``package`` to ``anchor`` with a compatibility
|
||||||
|
shim for those passing by keyword. Additionally, the new ``anchor``
|
||||||
|
parameter is now optional and will default to the caller's module.
|
Loading…
Reference in New Issue