gh-121190: Emit a better error message from `importlib.resources.files()` when module spec is `None`" by jaraco · Pull Request #148460 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions Lib/importlib/resources/_common.py
10 changes: 3 additions & 7 deletions Lib/importlib/resources/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@
import itertools
import os
import pathlib
from collections.abc import Iterable, Iterator
from typing import (
Any,
BinaryIO,
Iterable,
Iterator,
Literal,
NoReturn,
Optional,
Protocol,
Text,
TextIO,
Union,
overload,
runtime_checkable,
)

StrPath = Union[str, os.PathLike[str]]
StrPath = str | os.PathLike[str]

__all__ = ["ResourceReader", "Traversable", "TraversableResources"]

Expand Down Expand Up @@ -151,9 +149,7 @@ def open(self, mode: Literal['r'] = 'r', *args: Any, **kwargs: Any) -> TextIO: .
def open(self, mode: Literal['rb'], *args: Any, **kwargs: Any) -> BinaryIO: ...

@abc.abstractmethod
def open(
self, mode: str = 'r', *args: Any, **kwargs: Any
) -> Union[TextIO, BinaryIO]:
def open(self, mode: str = 'r', *args: Any, **kwargs: Any) -> TextIO | BinaryIO:
"""
mode may be 'r' or 'rb' to open as text or binary. Return a handle
suitable for reading (same as pathlib.Path.open).
Expand Down
6 changes: 3 additions & 3 deletions Lib/importlib/resources/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import abc
import io
import itertools
from typing import BinaryIO, List
from typing import BinaryIO

from .abc import Traversable, TraversableResources

Expand All @@ -24,14 +24,14 @@ def package(self) -> str:
"""

@abc.abstractmethod
def children(self) -> List['SimpleReader']:
def children(self) -> list['SimpleReader']:
"""
Obtain an iterable of SimpleReader for available
child containers (e.g. directories).
"""

@abc.abstractmethod
def resources(self) -> List[str]:
def resources(self) -> list[str]:
"""
Obtain available named resources for this virtual package.
"""
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_importlib/resources/_path.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import functools
import pathlib
from typing import Dict, Protocol, Union, runtime_checkable
from typing import Protocol, Union, runtime_checkable

####
# from jaraco.path 3.7.1
Expand All @@ -12,7 +12,7 @@ class Symlink(str):
"""


FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']]
FilesSpec = dict[str, Union[str, bytes, Symlink, 'FilesSpec']]


@runtime_checkable
Expand All @@ -28,13 +28,13 @@ def write_bytes(self, content): ... # pragma: no cover
def symlink_to(self, target): ... # pragma: no cover


def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker:
def _ensure_tree_maker(obj: str | TreeMaker) -> TreeMaker:
return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore[return-value]


def build(
spec: FilesSpec,
prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore[assignment]
prefix: str | TreeMaker = pathlib.Path(), # type: ignore[assignment]
):
"""
Build a set of files/directories, as described by the spec.
Expand Down Expand Up @@ -66,7 +66,7 @@ def build(


@functools.singledispatch
def create(content: Union[str, bytes, FilesSpec], path):
def create(content: str | bytes | FilesSpec, path):
path.mkdir(exist_ok=True)
build(content, prefix=path) # type: ignore[arg-type]

Expand Down
49 changes: 22 additions & 27 deletions Lib/test/test_importlib/resources/test_compatibilty_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,46 @@ def files(self):
return resources.files(self.package)

def test_spec_path_iter(self):
self.assertEqual(
sorted(path.name for path in self.files.iterdir()),
['a', 'b', 'c'],
)
assert sorted(path.name for path in self.files.iterdir()) == ['a', 'b', 'c']

def test_child_path_iter(self):
self.assertEqual(list((self.files / 'a').iterdir()), [])
assert list((self.files / 'a').iterdir()) == []

def test_orphan_path_iter(self):
self.assertEqual(list((self.files / 'a' / 'a').iterdir()), [])
self.assertEqual(list((self.files / 'a' / 'a' / 'a').iterdir()), [])
assert list((self.files / 'a' / 'a').iterdir()) == []
assert list((self.files / 'a' / 'a' / 'a').iterdir()) == []

def test_spec_path_is(self):
self.assertFalse(self.files.is_file())
self.assertFalse(self.files.is_dir())
assert not self.files.is_file()
assert not self.files.is_dir()

def test_child_path_is(self):
self.assertTrue((self.files / 'a').is_file())
self.assertFalse((self.files / 'a').is_dir())
assert (self.files / 'a').is_file()
assert not (self.files / 'a').is_dir()

def test_orphan_path_is(self):
self.assertFalse((self.files / 'a' / 'a').is_file())
self.assertFalse((self.files / 'a' / 'a').is_dir())
self.assertFalse((self.files / 'a' / 'a' / 'a').is_file())
self.assertFalse((self.files / 'a' / 'a' / 'a').is_dir())
assert not (self.files / 'a' / 'a').is_file()
assert not (self.files / 'a' / 'a').is_dir()
assert not (self.files / 'a' / 'a' / 'a').is_file()
assert not (self.files / 'a' / 'a' / 'a').is_dir()

def test_spec_path_name(self):
self.assertEqual(self.files.name, 'testingpackage')
assert self.files.name == 'testingpackage'

def test_child_path_name(self):
self.assertEqual((self.files / 'a').name, 'a')
assert (self.files / 'a').name == 'a'

def test_orphan_path_name(self):
self.assertEqual((self.files / 'a' / 'b').name, 'b')
self.assertEqual((self.files / 'a' / 'b' / 'c').name, 'c')
assert (self.files / 'a' / 'b').name == 'b'
assert (self.files / 'a' / 'b' / 'c').name == 'c'

def test_spec_path_open(self):
self.assertEqual(self.files.read_bytes(), b'Hello, world!')
self.assertEqual(self.files.read_text(encoding='utf-8'), 'Hello, world!')
assert self.files.read_bytes() == b'Hello, world!'
assert self.files.read_text(encoding='utf-8') == 'Hello, world!'

def test_child_path_open(self):
self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!')
self.assertEqual(
(self.files / 'a').read_text(encoding='utf-8'), 'Hello, world!'
)
assert (self.files / 'a').read_bytes() == b'Hello, world!'
assert (self.files / 'a').read_text(encoding='utf-8') == 'Hello, world!'

def test_orphan_path_open(self):
with self.assertRaises(FileNotFoundError):
Expand All @@ -86,7 +81,7 @@ def test_orphan_path_invalid(self):

def test_wrap_spec(self):
spec = wrap_spec(self.package)
self.assertIsInstance(spec.loader.get_resource_reader(None), CompatibilityFiles)
assert isinstance(spec.loader.get_resource_reader(None), CompatibilityFiles)


class CompatibilityFilesNoReaderTests(unittest.TestCase):
Expand All @@ -99,4 +94,4 @@ def files(self):
return resources.files(self.package)

def test_spec_path_joinpath(self):
self.assertIsInstance(self.files / 'a', CompatibilityFiles.OrphanPath)
assert isinstance(self.files / 'a', CompatibilityFiles.OrphanPath)
2 changes: 1 addition & 1 deletion Lib/test/test_importlib/resources/test_files.py
Loading
Loading