diff --git a/Lib/importlib/resources/readers.py b/Lib/importlib/resources/readers.py index c3cdf769cbecb0..8e2d8e8250bd7e 100644 --- a/Lib/importlib/resources/readers.py +++ b/Lib/importlib/resources/readers.py @@ -31,8 +31,10 @@ def files(self): class ZipReader(abc.TraversableResources): def __init__(self, loader, module): - _, _, name = module.rpartition('.') - self.prefix = loader.prefix.replace('\\', '/') + name + '/' + self.prefix = loader.prefix.replace('\\', '/') + if loader.is_package(module): + _, _, name = module.rpartition('.') + self.prefix += name + '/' self.archive = loader.archive def open_resource(self, resource): diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index 26c8b04e44c3b9..038aac0a3fe773 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -108,6 +108,42 @@ def test_implicit_files(self): _path.build(spec, self.site_dir) assert importlib.import_module('somepkg').val == 'resources are the best' + def test_implicit_files_zip_submodule(self): + """ + Special test for gh-121735 for Python 3.12. + """ + import os + import zipfile + + def create_zip_from_directory(source_dir, zip_filename): + with zipfile.ZipFile(zip_filename, 'w') as zipf: + for root, _, files in os.walk(source_dir): + for file in files: + file_path = os.path.join(root, file) + # Ensure files are at the root + arcname = os.path.relpath(file_path, source_dir) + zipf.write(file_path, arcname) + + set_val = textwrap.dedent( + """ + import importlib.resources as res + val = res.files().joinpath('res.txt').read_text(encoding='utf-8') + """ + ) + spec = { + 'somepkg': { + '__init__.py': set_val, + 'submod.py': set_val, + 'res.txt': 'resources are the best', + }, + } + build_dir = self.fixtures.enter_context(os_helper.temp_dir()) + _path.build(spec, build_dir) + zip_file = os.path.join(self.site_dir, 'thepkg.zip') + create_zip_from_directory(build_dir, zip_file) + self.fixtures.enter_context(import_helper.DirsOnSysPath(zip_file)) + assert importlib.import_module('somepkg.submod').val == 'resources are the best' + if __name__ == '__main__': unittest.main() diff --git a/Lib/zipimport.py b/Lib/zipimport.py index a7333a4c4906de..7669abd6c472e6 100644 --- a/Lib/zipimport.py +++ b/Lib/zipimport.py @@ -254,17 +254,9 @@ def load_module(self, fullname): def get_resource_reader(self, fullname): - """Return the ResourceReader for a package in a zip file. - - If 'fullname' is a package within the zip file, return the - 'ResourceReader' object for the package. Otherwise return None. - """ - try: - if not self.is_package(fullname): - return None - except ZipImportError: - return None + """Return the ResourceReader for a module in a zip file.""" from importlib.readers import ZipReader + return ZipReader(self, fullname) diff --git a/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst b/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst new file mode 100644 index 00000000000000..e10b2e760bc063 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-15-09-45-34.gh-issue-121735._1q0qf.rst @@ -0,0 +1,3 @@ +When working with zip archives, importlib.resources now properly honors +module-adjacent references (e.g. ``files(pkg.mod)`` and not just +``files(pkg)``).
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: