From b7daf21bff66a52a87d15f289d20a31ba56796ea Mon Sep 17 00:00:00 2001 From: Stefan Zetzsche Date: Thu, 26 Feb 2026 15:44:00 +0000 Subject: [PATCH 1/5] gh-145260: Fix importlib.resources.simple ResourceContainer and ResourceHandle ResourceContainer and ResourceHandle could not be instantiated because Traversable now requires name as an abstract property. Additionally, ResourceContainer.iterdir() accessed self.reader.resources without calling it, and ResourceHandle.joinpath had a signature mismatch. - Add name property to ResourceContainer - Fix self.reader.resources -> self.reader.resources() call - Add iterdir() method to ResourceHandle - Fix ResourceHandle.joinpath to accept *descendants --- Lib/importlib/resources/simple.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/importlib/resources/simple.py b/Lib/importlib/resources/simple.py index 2e75299b13aabf..8deb34fc1756d1 100644 --- a/Lib/importlib/resources/simple.py +++ b/Lib/importlib/resources/simple.py @@ -55,6 +55,10 @@ class ResourceContainer(Traversable): def __init__(self, reader: SimpleReader): self.reader = reader + @property + def name(self): + return self.reader.name + def is_dir(self): return True @@ -62,7 +66,7 @@ def is_file(self): return False def iterdir(self): - files = (ResourceHandle(self, name) for name in self.reader.resources) + files = (ResourceHandle(self, name) for name in self.reader.resources()) dirs = map(ResourceContainer, self.reader.children()) return itertools.chain(files, dirs) @@ -85,13 +89,18 @@ def is_file(self): def is_dir(self): return False + def iterdir(self): + return iter([]) + def open(self, mode='r', *args, **kwargs): stream = self.parent.reader.open_binary(self.name) if 'b' not in mode: stream = io.TextIOWrapper(stream, *args, **kwargs) return stream - def joinpath(self, name): + def joinpath(self, *descendants): + if not descendants: + return self raise RuntimeError("Cannot traverse into a resource") From e4f2d29a40a1e9ba110db9285db5bbb4f865ef4a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:46:51 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst diff --git a/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst new file mode 100644 index 00000000000000..1c641e73979be8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst @@ -0,0 +1,5 @@ +Fix :class:`~importlib.resources.simple.ResourceContainer` and +:class:`~importlib.resources.simple.ResourceHandle` being uninstantiable due +to missing :attr:`name` property required by :class:`~importlib.resources.abc.Traversable`. +Also fix ``ResourceContainer.iterdir()`` not calling ``resources()`` and +``ResourceHandle.joinpath`` signature mismatch with base class. From 9d64bd3121ca6c012513314a46418859d02d8b26 Mon Sep 17 00:00:00 2001 From: Stefan Zetzsche Date: Thu, 26 Feb 2026 15:57:58 +0000 Subject: [PATCH 3/5] Fix NEWS entry: use plain markup for undocumented classes --- .../2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst index 1c641e73979be8..46f9acbfaa7054 100644 --- a/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst +++ b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst @@ -1,5 +1,5 @@ -Fix :class:`~importlib.resources.simple.ResourceContainer` and -:class:`~importlib.resources.simple.ResourceHandle` being uninstantiable due -to missing :attr:`name` property required by :class:`~importlib.resources.abc.Traversable`. -Also fix ``ResourceContainer.iterdir()`` not calling ``resources()`` and +Fix ``ResourceContainer`` and ``ResourceHandle`` in +:mod:`importlib.resources.simple` being uninstantiable due to missing ``name`` +property required by :class:`~importlib.resources.abc.Traversable`. Also fix +``ResourceContainer.iterdir()`` not calling ``resources()`` and ``ResourceHandle.joinpath`` signature mismatch with base class. From b3b1772208c026ff7b35a273a28c79be45b61cd3 Mon Sep 17 00:00:00 2001 From: Stefan Zetzsche Date: Thu, 26 Feb 2026 16:04:05 +0000 Subject: [PATCH 4/5] Fix NEWS entry: use plain markup for undocumented module --- .../next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst index 46f9acbfaa7054..3675cfe9af23ad 100644 --- a/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst +++ b/Misc/NEWS.d/next/Library/2026-02-26-15-46-50.gh-issue-145260.wmT5hp.rst @@ -1,5 +1,5 @@ Fix ``ResourceContainer`` and ``ResourceHandle`` in -:mod:`importlib.resources.simple` being uninstantiable due to missing ``name`` +``importlib.resources.simple`` being uninstantiable due to missing ``name`` property required by :class:`~importlib.resources.abc.Traversable`. Also fix ``ResourceContainer.iterdir()`` not calling ``resources()`` and ``ResourceHandle.joinpath`` signature mismatch with base class. From d95ab356aaa7d2c350dc0179fdf48d47fd74996a Mon Sep 17 00:00:00 2001 From: Stefan Zetzsche Date: Fri, 27 Feb 2026 13:27:17 +0000 Subject: [PATCH 5/5] test: Add tests for importlib.resources.simple module Add tests for ResourceContainer and ResourceHandle classes to verify: - ResourceContainer instantiation with name property - ResourceContainer.iterdir() calling resources() method - ResourceHandle.joinpath() accepting *descendants parameter - ResourceHandle.iterdir() returning empty iterator - ResourceHandle name as property instead of instance variable Also fix ResourceHandle.name to be a property to satisfy Traversable abstract base class requirements. --- Lib/importlib/resources/simple.py | 6 +- .../test_importlib/resources/test_reader.py | 64 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Lib/importlib/resources/simple.py b/Lib/importlib/resources/simple.py index 8deb34fc1756d1..12a540d5ff253f 100644 --- a/Lib/importlib/resources/simple.py +++ b/Lib/importlib/resources/simple.py @@ -81,7 +81,11 @@ class ResourceHandle(Traversable): def __init__(self, parent: ResourceContainer, name: str): self.parent = parent - self.name = name # type: ignore[misc] + self._name = name + + @property + def name(self): + return self._name def is_file(self): return True diff --git a/Lib/test/test_importlib/resources/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py index ed5693ab416798..7fd9cd2212ccd7 100644 --- a/Lib/test/test_importlib/resources/test_reader.py +++ b/Lib/test/test_importlib/resources/test_reader.py @@ -1,9 +1,11 @@ +import io import os.path import pathlib import unittest from importlib import import_module from importlib.readers import MultiplexedPath, NamespaceReader +from importlib.resources.simple import ResourceContainer, ResourceHandle, SimpleReader from . import util @@ -133,5 +135,67 @@ def test_files(self): self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')") +class SimpleReaderTest(unittest.TestCase): + def test_resource_container_instantiation(self): + class R(SimpleReader): + @property + def package(self): + return 'x' + + def children(self): + return [] + + def resources(self): + return [] + + def open_binary(self, r): + return io.BytesIO(b'') + + container = ResourceContainer(R()) + self.assertEqual(container.name, 'x') + self.assertTrue(container.is_dir()) + + def test_resource_container_iterdir(self): + class R(SimpleReader): + @property + def package(self): + return 'test' + + def children(self): + return [] + + def resources(self): + return ['file.txt'] + + def open_binary(self, r): + return io.BytesIO(b'data') + + container = ResourceContainer(R()) + items = list(container.iterdir()) + self.assertEqual(len(items), 1) + self.assertIsInstance(items[0], ResourceHandle) + + def test_resource_handle_joinpath(self): + class R(SimpleReader): + @property + def package(self): + return 'test' + + def children(self): + return [] + + def resources(self): + return [] + + def open_binary(self, r): + return io.BytesIO(b'') + + container = ResourceContainer(R()) + handle = ResourceHandle(container, 'file.txt') + self.assertIs(handle.joinpath(), handle) + with self.assertRaises(RuntimeError): + handle.joinpath('subpath') + + if __name__ == '__main__': unittest.main()