mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	gh-121735: Fix module-adjacent references in zip files (#123037)
* gh-116608: Apply style and compatibility changes from importlib_metadata. * gh-121735: Ensure module-adjacent resources are loadable from a zipfile. * gh-121735: Allow all modules to be processed by the ZipReader. * Add blurb * Remove update-zips script, unneeded. * Remove unnecessary references to removed static fixtures. * Remove zipdata fixtures, unused.
This commit is contained in:
		
							parent
							
								
									3bd942f106
								
							
						
					
					
						commit
						ba687d9481
					
				
					 40 changed files with 225 additions and 263 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitattributes
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitattributes
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -27,8 +27,6 @@ Lib/test/cjkencodings/*                    noeol
 | 
				
			||||||
Lib/test/tokenizedata/coding20731.py       noeol
 | 
					Lib/test/tokenizedata/coding20731.py       noeol
 | 
				
			||||||
Lib/test/decimaltestdata/*.decTest         noeol
 | 
					Lib/test/decimaltestdata/*.decTest         noeol
 | 
				
			||||||
Lib/test/test_email/data/*.txt             noeol
 | 
					Lib/test/test_email/data/*.txt             noeol
 | 
				
			||||||
Lib/test/test_importlib/resources/data01/*           noeol
 | 
					 | 
				
			||||||
Lib/test/test_importlib/resources/namespacedata01/*  noeol
 | 
					 | 
				
			||||||
Lib/test/xmltestdata/*                     noeol
 | 
					Lib/test/xmltestdata/*                     noeol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Shell scripts should have LF even on Windows because of Cygwin
 | 
					# Shell scripts should have LF even on Windows because of Cygwin
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,8 +34,10 @@ def files(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ZipReader(abc.TraversableResources):
 | 
					class ZipReader(abc.TraversableResources):
 | 
				
			||||||
    def __init__(self, loader, module):
 | 
					    def __init__(self, loader, module):
 | 
				
			||||||
 | 
					        self.prefix = loader.prefix.replace('\\', '/')
 | 
				
			||||||
 | 
					        if loader.is_package(module):
 | 
				
			||||||
            _, _, name = module.rpartition('.')
 | 
					            _, _, name = module.rpartition('.')
 | 
				
			||||||
        self.prefix = loader.prefix.replace('\\', '/') + name + '/'
 | 
					            self.prefix += name + '/'
 | 
				
			||||||
        self.archive = loader.archive
 | 
					        self.archive = loader.archive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def open_resource(self, resource):
 | 
					    def open_resource(self, resource):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Hello, UTF-8 world!
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
one resource
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
a resource
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
two resource
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Hello, UTF-8 world!
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
from importlib import resources
 | 
					from importlib import resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import data01
 | 
					 | 
				
			||||||
from . import util
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,16 +18,17 @@ def test_contents(self):
 | 
				
			||||||
        assert self.expected <= contents
 | 
					        assert self.expected <= contents
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContentsDiskTests(ContentsTests, unittest.TestCase):
 | 
					class ContentsDiskTests(ContentsTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    pass
 | 
				
			||||||
        self.data = data01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
 | 
					class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
 | 
					class ContentsNamespaceTests(ContentsTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expected = {
 | 
					    expected = {
 | 
				
			||||||
        # no __init__ because of namespace design
 | 
					        # no __init__ because of namespace design
 | 
				
			||||||
        'binary.file',
 | 
					        'binary.file',
 | 
				
			||||||
| 
						 | 
					@ -36,8 +36,3 @@ class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
 | 
				
			||||||
        'utf-16.file',
 | 
					        'utf-16.file',
 | 
				
			||||||
        'utf-8.file',
 | 
					        'utf-8.file',
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setUp(self):
 | 
					 | 
				
			||||||
        from . import namespacedata01
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.data = namespacedata01
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,11 +6,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 util
 | 
					from . import util
 | 
				
			||||||
from . import _path
 | 
					 | 
				
			||||||
from test.support import os_helper
 | 
					 | 
				
			||||||
from test.support import import_helper
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@contextlib.contextmanager
 | 
					@contextlib.contextmanager
 | 
				
			||||||
| 
						 | 
					@ -48,70 +44,96 @@ def test_old_parameter(self):
 | 
				
			||||||
            resources.files(package=self.data)
 | 
					            resources.files(package=self.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenDiskTests(FilesTests, unittest.TestCase):
 | 
					class OpenDiskTests(FilesTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    pass
 | 
				
			||||||
        self.data = data01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
 | 
					class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenNamespaceTests(FilesTests, unittest.TestCase):
 | 
					class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
        from . import namespacedata01
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.data = namespacedata01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
 | 
					class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    ZIP_MODULE = 'namespacedata01'
 | 
					    ZIP_MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SiteDir:
 | 
					class DirectSpec:
 | 
				
			||||||
    def setUp(self):
 | 
					    """
 | 
				
			||||||
        self.fixtures = contextlib.ExitStack()
 | 
					    Override behavior of ModuleSetup to write a full spec directly.
 | 
				
			||||||
        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))
 | 
					    MODULE = 'unused'
 | 
				
			||||||
        self.fixtures.enter_context(import_helper.isolated_modules())
 | 
					
 | 
				
			||||||
 | 
					    def load_fixture(self, name):
 | 
				
			||||||
 | 
					        self.tree_on_path(self.spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ModulesFilesTests(SiteDir, unittest.TestCase):
 | 
					class ModulesFiles:
 | 
				
			||||||
    def test_module_resources(self):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        A module can have resources found adjacent to the module.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
    spec = {
 | 
					    spec = {
 | 
				
			||||||
        'mod.py': '',
 | 
					        'mod.py': '',
 | 
				
			||||||
        'res.txt': 'resources are the best',
 | 
					        'res.txt': 'resources are the best',
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        _path.build(spec, self.site_dir)
 | 
					
 | 
				
			||||||
 | 
					    def test_module_resources(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        A module can have resources found adjacent to the module.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        import mod
 | 
					        import mod
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
 | 
					        actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8')
 | 
				
			||||||
        assert actual == spec['res.txt']
 | 
					        assert actual == self.spec['res.txt']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImplicitContextFilesTests(SiteDir, unittest.TestCase):
 | 
					class ModuleFilesDiskTests(DirectSpec, util.DiskSetup, ModulesFiles, unittest.TestCase):
 | 
				
			||||||
    def test_implicit_files(self):
 | 
					    pass
 | 
				
			||||||
        """
 | 
					
 | 
				
			||||||
        Without any parameter, files() will infer the location as the caller.
 | 
					
 | 
				
			||||||
        """
 | 
					class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.TestCase):
 | 
				
			||||||
        spec = {
 | 
					    pass
 | 
				
			||||||
            'somepkg': {
 | 
					
 | 
				
			||||||
                '__init__.py': textwrap.dedent(
 | 
					
 | 
				
			||||||
 | 
					class ImplicitContextFiles:
 | 
				
			||||||
 | 
					    set_val = textwrap.dedent(
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        import importlib.resources as res
 | 
					        import importlib.resources as res
 | 
				
			||||||
        val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
 | 
					        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',
 | 
					            'res.txt': 'resources are the best',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        _path.build(spec, self.site_dir)
 | 
					
 | 
				
			||||||
 | 
					    def test_implicit_files_package(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Without any parameter, files() will infer the location as the caller.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        assert importlib.import_module('somepkg').val == 'resources are the best'
 | 
					        assert importlib.import_module('somepkg').val == 'resources are the best'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_implicit_files_submodule(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Without any parameter, files() will infer the location as the caller.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        assert importlib.import_module('somepkg.submod').val == 'resources are the best'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImplicitContextFilesDiskTests(
 | 
				
			||||||
 | 
					    DirectSpec, util.DiskSetup, ImplicitContextFiles, unittest.TestCase
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImplicitContextFilesZipTests(
 | 
				
			||||||
 | 
					    DirectSpec, util.ZipSetup, ImplicitContextFiles, unittest.TestCase
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,26 +1,38 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					import importlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from test.support import warnings_helper
 | 
					from test.support import warnings_helper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib import resources
 | 
					from importlib import resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Since the functional API forwards to Traversable, we only test
 | 
					# Since the functional API forwards to Traversable, we only test
 | 
				
			||||||
# filesystem resources here -- not zip files, namespace packages etc.
 | 
					# filesystem resources here -- not zip files, namespace packages etc.
 | 
				
			||||||
# We do test for two kinds of Anchor, though.
 | 
					# We do test for two kinds of Anchor, though.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StringAnchorMixin:
 | 
					class StringAnchorMixin:
 | 
				
			||||||
    anchor01 = 'test.test_importlib.resources.data01'
 | 
					    anchor01 = 'data01'
 | 
				
			||||||
    anchor02 = 'test.test_importlib.resources.data02'
 | 
					    anchor02 = 'data02'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ModuleAnchorMixin:
 | 
					class ModuleAnchorMixin:
 | 
				
			||||||
    from . import data01 as anchor01
 | 
					    @property
 | 
				
			||||||
    from . import data02 as anchor02
 | 
					    def anchor01(self):
 | 
				
			||||||
 | 
					        return importlib.import_module('data01')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def anchor02(self):
 | 
				
			||||||
 | 
					        return importlib.import_module('data02')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FunctionalAPIBase:
 | 
					class FunctionalAPIBase(util.DiskSetup):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super().setUp()
 | 
				
			||||||
 | 
					        self.load_fixture('data02')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _gen_resourcetxt_path_parts(self):
 | 
					    def _gen_resourcetxt_path_parts(self):
 | 
				
			||||||
        """Yield various names of a text file in anchor02, each in a subTest"""
 | 
					        """Yield various names of a text file in anchor02, each in a subTest"""
 | 
				
			||||||
        for path_parts in (
 | 
					        for path_parts in (
 | 
				
			||||||
| 
						 | 
					@ -228,16 +240,16 @@ def test_text_errors(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FunctionalAPITest_StringAnchor(
 | 
					class FunctionalAPITest_StringAnchor(
 | 
				
			||||||
    unittest.TestCase,
 | 
					 | 
				
			||||||
    FunctionalAPIBase,
 | 
					 | 
				
			||||||
    StringAnchorMixin,
 | 
					    StringAnchorMixin,
 | 
				
			||||||
 | 
					    FunctionalAPIBase,
 | 
				
			||||||
 | 
					    unittest.TestCase,
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FunctionalAPITest_ModuleAnchor(
 | 
					class FunctionalAPITest_ModuleAnchor(
 | 
				
			||||||
    unittest.TestCase,
 | 
					 | 
				
			||||||
    FunctionalAPIBase,
 | 
					 | 
				
			||||||
    ModuleAnchorMixin,
 | 
					    ModuleAnchorMixin,
 | 
				
			||||||
 | 
					    FunctionalAPIBase,
 | 
				
			||||||
 | 
					    unittest.TestCase,
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib import resources
 | 
					from importlib import resources
 | 
				
			||||||
from . import data01
 | 
					 | 
				
			||||||
from . import util
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,16 +64,12 @@ def test_open_text_FileNotFoundError(self):
 | 
				
			||||||
            target.open(encoding='utf-8')
 | 
					            target.open(encoding='utf-8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenDiskTests(OpenTests, unittest.TestCase):
 | 
					class OpenDiskTests(OpenTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    pass
 | 
				
			||||||
        self.data = data01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
 | 
					class OpenDiskNamespaceTests(OpenTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
        from . import namespacedata01
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.data = namespacedata01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
 | 
					class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
| 
						 | 
					@ -82,7 +77,7 @@ class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
 | 
					class OpenNamespaceZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    ZIP_MODULE = 'namespacedata01'
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,6 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib import resources
 | 
					from importlib import resources
 | 
				
			||||||
from . import data01
 | 
					 | 
				
			||||||
from . import util
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,9 +24,7 @@ def test_reading(self):
 | 
				
			||||||
            self.assertEqual('Hello, UTF-8 world!\n', path.read_text(encoding='utf-8'))
 | 
					            self.assertEqual('Hello, UTF-8 world!\n', path.read_text(encoding='utf-8'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PathDiskTests(PathTests, unittest.TestCase):
 | 
					class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    data = data01
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_natural_path(self):
 | 
					    def test_natural_path(self):
 | 
				
			||||||
        # Guarantee the internal implementation detail that
 | 
					        # Guarantee the internal implementation detail that
 | 
				
			||||||
        # file-system-backed resources do not get the tempdir
 | 
					        # file-system-backed resources do not get the tempdir
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib import import_module, resources
 | 
					from importlib import import_module, resources
 | 
				
			||||||
from . import data01
 | 
					
 | 
				
			||||||
from . import util
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,8 +51,8 @@ def test_read_text_with_errors(self):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReadDiskTests(ReadTests, unittest.TestCase):
 | 
					class ReadDiskTests(ReadTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    data = data01
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
 | 
					class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
| 
						 | 
					@ -68,15 +68,12 @@ def test_read_submodule_resource_by_name(self):
 | 
				
			||||||
        self.assertEqual(result, bytes(range(4, 8)))
 | 
					        self.assertEqual(result, bytes(range(4, 8)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReadNamespaceTests(ReadTests, unittest.TestCase):
 | 
					class ReadNamespaceTests(ReadTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
        from . import namespacedata01
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.data = namespacedata01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
 | 
					class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    ZIP_MODULE = 'namespacedata01'
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_submodule_resource(self):
 | 
					    def test_read_submodule_resource(self):
 | 
				
			||||||
        submodule = import_module('namespacedata01.subdirectory')
 | 
					        submodule = import_module('namespacedata01.subdirectory')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,21 @@
 | 
				
			||||||
import os.path
 | 
					import os.path
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib import import_module
 | 
					from importlib import import_module
 | 
				
			||||||
from importlib.readers import MultiplexedPath, NamespaceReader
 | 
					from importlib.readers import MultiplexedPath, NamespaceReader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MultiplexedPathTest(unittest.TestCase):
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					class MultiplexedPathTest(util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUpClass(cls):
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
        cls.folder = pathlib.Path(__file__).parent / 'namespacedata01'
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super().setUp()
 | 
				
			||||||
 | 
					        self.folder = pathlib.Path(self.data.__path__[0])
 | 
				
			||||||
 | 
					        self.data01 = pathlib.Path(self.load_fixture('data01').__file__).parent
 | 
				
			||||||
 | 
					        self.data02 = pathlib.Path(self.load_fixture('data02').__file__).parent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_init_no_paths(self):
 | 
					    def test_init_no_paths(self):
 | 
				
			||||||
        with self.assertRaises(FileNotFoundError):
 | 
					        with self.assertRaises(FileNotFoundError):
 | 
				
			||||||
| 
						 | 
					@ -31,9 +36,8 @@ def test_iterdir(self):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_iterdir_duplicate(self):
 | 
					    def test_iterdir_duplicate(self):
 | 
				
			||||||
        data01 = pathlib.Path(__file__).parent.joinpath('data01')
 | 
					 | 
				
			||||||
        contents = {
 | 
					        contents = {
 | 
				
			||||||
            path.name for path in MultiplexedPath(self.folder, data01).iterdir()
 | 
					            path.name for path in MultiplexedPath(self.folder, self.data01).iterdir()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        for remove in ('__pycache__', '__init__.pyc'):
 | 
					        for remove in ('__pycache__', '__init__.pyc'):
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
| 
						 | 
					@ -61,9 +65,8 @@ def test_open_file(self):
 | 
				
			||||||
            path.open()
 | 
					            path.open()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_join_path(self):
 | 
					    def test_join_path(self):
 | 
				
			||||||
        data01 = pathlib.Path(__file__).parent.joinpath('data01')
 | 
					        prefix = str(self.folder.parent)
 | 
				
			||||||
        prefix = str(data01.parent)
 | 
					        path = MultiplexedPath(self.folder, self.data01)
 | 
				
			||||||
        path = MultiplexedPath(self.folder, data01)
 | 
					 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            str(path.joinpath('binary.file'))[len(prefix) + 1 :],
 | 
					            str(path.joinpath('binary.file'))[len(prefix) + 1 :],
 | 
				
			||||||
            os.path.join('namespacedata01', 'binary.file'),
 | 
					            os.path.join('namespacedata01', 'binary.file'),
 | 
				
			||||||
| 
						 | 
					@ -83,10 +86,8 @@ def test_join_path_compound(self):
 | 
				
			||||||
        assert not path.joinpath('imaginary/foo.py').exists()
 | 
					        assert not path.joinpath('imaginary/foo.py').exists()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_join_path_common_subdir(self):
 | 
					    def test_join_path_common_subdir(self):
 | 
				
			||||||
        data01 = pathlib.Path(__file__).parent.joinpath('data01')
 | 
					        prefix = str(self.data02.parent)
 | 
				
			||||||
        data02 = pathlib.Path(__file__).parent.joinpath('data02')
 | 
					        path = MultiplexedPath(self.data01, self.data02)
 | 
				
			||||||
        prefix = str(data01.parent)
 | 
					 | 
				
			||||||
        path = MultiplexedPath(data01, data02)
 | 
					 | 
				
			||||||
        self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath)
 | 
					        self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath)
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :],
 | 
					            str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :],
 | 
				
			||||||
| 
						 | 
					@ -106,16 +107,8 @@ def test_name(self):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NamespaceReaderTest(unittest.TestCase):
 | 
					class NamespaceReaderTest(util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    site_dir = str(pathlib.Path(__file__).parent)
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def setUpClass(cls):
 | 
					 | 
				
			||||||
        sys.path.append(cls.site_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def tearDownClass(cls):
 | 
					 | 
				
			||||||
        sys.path.remove(cls.site_dir)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_init_error(self):
 | 
					    def test_init_error(self):
 | 
				
			||||||
        with self.assertRaises(ValueError):
 | 
					        with self.assertRaises(ValueError):
 | 
				
			||||||
| 
						 | 
					@ -125,7 +118,7 @@ def test_resource_path(self):
 | 
				
			||||||
        namespacedata01 = import_module('namespacedata01')
 | 
					        namespacedata01 = import_module('namespacedata01')
 | 
				
			||||||
        reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
 | 
					        reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
 | 
					        root = self.data.__path__[0]
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            reader.resource_path('binary.file'), os.path.join(root, 'binary.file')
 | 
					            reader.resource_path('binary.file'), os.path.join(root, 'binary.file')
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -134,9 +127,8 @@ def test_resource_path(self):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_files(self):
 | 
					    def test_files(self):
 | 
				
			||||||
        namespacedata01 = import_module('namespacedata01')
 | 
					        reader = NamespaceReader(self.data.__spec__.submodule_search_locations)
 | 
				
			||||||
        reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
 | 
					        root = self.data.__path__[0]
 | 
				
			||||||
        root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
 | 
					 | 
				
			||||||
        self.assertIsInstance(reader.files(), MultiplexedPath)
 | 
					        self.assertIsInstance(reader.files(), MultiplexedPath)
 | 
				
			||||||
        self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')")
 | 
					        self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,5 @@
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
import pathlib
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import data01
 | 
					 | 
				
			||||||
from . import util
 | 
					from . import util
 | 
				
			||||||
from importlib import resources, import_module
 | 
					from importlib import resources, import_module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,9 +21,8 @@ def test_is_dir(self):
 | 
				
			||||||
        self.assertTrue(target.is_dir())
 | 
					        self.assertTrue(target.is_dir())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceDiskTests(ResourceTests, unittest.TestCase):
 | 
					class ResourceDiskTests(ResourceTests, util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    pass
 | 
				
			||||||
        self.data = data01
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
 | 
					class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
| 
						 | 
					@ -37,33 +33,39 @@ def names(traversable):
 | 
				
			||||||
    return {item.name for item in traversable.iterdir()}
 | 
					    return {item.name for item in traversable.iterdir()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceLoaderTests(unittest.TestCase):
 | 
					class ResourceLoaderTests(util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def test_resource_contents(self):
 | 
					    def test_resource_contents(self):
 | 
				
			||||||
        package = util.create_package(
 | 
					        package = util.create_package(
 | 
				
			||||||
            file=data01, path=data01.__file__, contents=['A', 'B', 'C']
 | 
					            file=self.data, path=self.data.__file__, contents=['A', 'B', 'C']
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
 | 
					        self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_is_file(self):
 | 
					    def test_is_file(self):
 | 
				
			||||||
        package = util.create_package(
 | 
					        package = util.create_package(
 | 
				
			||||||
            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
 | 
					            file=self.data,
 | 
				
			||||||
 | 
					            path=self.data.__file__,
 | 
				
			||||||
 | 
					            contents=['A', 'B', 'C', 'D/E', 'D/F'],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(resources.files(package).joinpath('B').is_file())
 | 
					        self.assertTrue(resources.files(package).joinpath('B').is_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_is_dir(self):
 | 
					    def test_is_dir(self):
 | 
				
			||||||
        package = util.create_package(
 | 
					        package = util.create_package(
 | 
				
			||||||
            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
 | 
					            file=self.data,
 | 
				
			||||||
 | 
					            path=self.data.__file__,
 | 
				
			||||||
 | 
					            contents=['A', 'B', 'C', 'D/E', 'D/F'],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(resources.files(package).joinpath('D').is_dir())
 | 
					        self.assertTrue(resources.files(package).joinpath('D').is_dir())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_resource_missing(self):
 | 
					    def test_resource_missing(self):
 | 
				
			||||||
        package = util.create_package(
 | 
					        package = util.create_package(
 | 
				
			||||||
            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
 | 
					            file=self.data,
 | 
				
			||||||
 | 
					            path=self.data.__file__,
 | 
				
			||||||
 | 
					            contents=['A', 'B', 'C', 'D/E', 'D/F'],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertFalse(resources.files(package).joinpath('Z').is_file())
 | 
					        self.assertFalse(resources.files(package).joinpath('Z').is_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceCornerCaseTests(unittest.TestCase):
 | 
					class ResourceCornerCaseTests(util.DiskSetup, unittest.TestCase):
 | 
				
			||||||
    def test_package_has_no_reader_fallback(self):
 | 
					    def test_package_has_no_reader_fallback(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Test odd ball packages which:
 | 
					        Test odd ball packages which:
 | 
				
			||||||
| 
						 | 
					@ -72,7 +74,7 @@ def test_package_has_no_reader_fallback(self):
 | 
				
			||||||
        # 3. Are not in a zip file
 | 
					        # 3. Are not in a zip file
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        module = util.create_package(
 | 
					        module = util.create_package(
 | 
				
			||||||
            file=data01, path=data01.__file__, contents=['A', 'B', 'C']
 | 
					            file=self.data, path=self.data.__file__, contents=['A', 'B', 'C']
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        # Give the module a dummy loader.
 | 
					        # Give the module a dummy loader.
 | 
				
			||||||
        module.__loader__ = object()
 | 
					        module.__loader__ = object()
 | 
				
			||||||
| 
						 | 
					@ -83,9 +85,7 @@ def test_package_has_no_reader_fallback(self):
 | 
				
			||||||
        self.assertFalse(resources.files(module).joinpath('A').is_file())
 | 
					        self.assertFalse(resources.files(module).joinpath('A').is_file())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
 | 
					class ResourceFromZipsTest01(util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    ZIP_MODULE = 'data01'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_is_submodule_resource(self):
 | 
					    def test_is_submodule_resource(self):
 | 
				
			||||||
        submodule = import_module('data01.subdirectory')
 | 
					        submodule = import_module('data01.subdirectory')
 | 
				
			||||||
        self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
 | 
					        self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
 | 
				
			||||||
| 
						 | 
					@ -116,8 +116,8 @@ def test_as_file_directory(self):
 | 
				
			||||||
        assert not data.parent.exists()
 | 
					        assert not data.parent.exists()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
 | 
					class ResourceFromZipsTest02(util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    ZIP_MODULE = 'data02'
 | 
					    MODULE = 'data02'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_unrelated_contents(self):
 | 
					    def test_unrelated_contents(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -134,7 +134,7 @@ def test_unrelated_contents(self):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeletingZipsTest(util.ZipSetupBase, unittest.TestCase):
 | 
					class DeletingZipsTest(util.ZipSetup, unittest.TestCase):
 | 
				
			||||||
    """Having accessed resources in a zip file should not keep an open
 | 
					    """Having accessed resources in a zip file should not keep an open
 | 
				
			||||||
    reference to the zip.
 | 
					    reference to the zip.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -216,24 +216,20 @@ def test_submodule_sub_contents_by_name(self):
 | 
				
			||||||
        self.assertEqual(contents, {'binary.file'})
 | 
					        self.assertEqual(contents, {'binary.file'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ResourceFromNamespaceDiskTests(ResourceFromNamespaceTests, unittest.TestCase):
 | 
					class ResourceFromNamespaceDiskTests(
 | 
				
			||||||
    site_dir = str(pathlib.Path(__file__).parent)
 | 
					    util.DiskSetup,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def setUpClass(cls):
 | 
					 | 
				
			||||||
        sys.path.append(cls.site_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def tearDownClass(cls):
 | 
					 | 
				
			||||||
        sys.path.remove(cls.site_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ResourceFromNamespaceZipTests(
 | 
					 | 
				
			||||||
    util.ZipSetupBase,
 | 
					 | 
				
			||||||
    ResourceFromNamespaceTests,
 | 
					    ResourceFromNamespaceTests,
 | 
				
			||||||
    unittest.TestCase,
 | 
					    unittest.TestCase,
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    ZIP_MODULE = 'namespacedata01'
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ResourceFromNamespaceZipTests(
 | 
				
			||||||
 | 
					    util.ZipSetup,
 | 
				
			||||||
 | 
					    ResourceFromNamespaceTests,
 | 
				
			||||||
 | 
					    unittest.TestCase,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    MODULE = 'namespacedata01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,53 +0,0 @@
 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
Generate the zip test data files.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Run to build the tests/zipdataNN/ziptestdata.zip files from
 | 
					 | 
				
			||||||
files in tests/dataNN.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Replaces the file with the working copy, but does commit anything
 | 
					 | 
				
			||||||
to the source repo.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import contextlib
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import pathlib
 | 
					 | 
				
			||||||
import zipfile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    >>> from unittest import mock
 | 
					 | 
				
			||||||
    >>> monkeypatch = getfixture('monkeypatch')
 | 
					 | 
				
			||||||
    >>> monkeypatch.setattr(zipfile, 'ZipFile', mock.MagicMock())
 | 
					 | 
				
			||||||
    >>> print(); main()  # print workaround for bpo-32509
 | 
					 | 
				
			||||||
    <BLANKLINE>
 | 
					 | 
				
			||||||
    ...data01... -> ziptestdata/...
 | 
					 | 
				
			||||||
    ...
 | 
					 | 
				
			||||||
    ...data02... -> ziptestdata/...
 | 
					 | 
				
			||||||
    ...
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    suffixes = '01', '02'
 | 
					 | 
				
			||||||
    tuple(map(generate, suffixes))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def generate(suffix):
 | 
					 | 
				
			||||||
    root = pathlib.Path(__file__).parent.relative_to(os.getcwd())
 | 
					 | 
				
			||||||
    zfpath = root / f'zipdata{suffix}/ziptestdata.zip'
 | 
					 | 
				
			||||||
    with zipfile.ZipFile(zfpath, 'w') as zf:
 | 
					 | 
				
			||||||
        for src, rel in walk(root / f'data{suffix}'):
 | 
					 | 
				
			||||||
            dst = 'ziptestdata' / pathlib.PurePosixPath(rel.as_posix())
 | 
					 | 
				
			||||||
            print(src, '->', dst)
 | 
					 | 
				
			||||||
            zf.write(src, dst)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def walk(datapath):
 | 
					 | 
				
			||||||
    for dirpath, dirnames, filenames in os.walk(datapath):
 | 
					 | 
				
			||||||
        with contextlib.suppress(ValueError):
 | 
					 | 
				
			||||||
            dirnames.remove('__pycache__')
 | 
					 | 
				
			||||||
        for filename in filenames:
 | 
					 | 
				
			||||||
            res = pathlib.Path(dirpath) / filename
 | 
					 | 
				
			||||||
            rel = res.relative_to(datapath)
 | 
					 | 
				
			||||||
            yield res, rel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__name__ == '__main__' and main()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,10 @@
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
import contextlib
 | 
					import contextlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import data01
 | 
					 | 
				
			||||||
from importlib.resources.abc import ResourceReader
 | 
					from importlib.resources.abc import ResourceReader
 | 
				
			||||||
from test.support import import_helper, os_helper
 | 
					from test.support import import_helper, os_helper
 | 
				
			||||||
from . import zip as zip_
 | 
					from . import zip as zip_
 | 
				
			||||||
 | 
					from . import _path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib.machinery import ModuleSpec
 | 
					from importlib.machinery import ModuleSpec
 | 
				
			||||||
| 
						 | 
					@ -68,7 +68,7 @@ def create_package(file=None, path=None, is_package=True, contents=()):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CommonTests(metaclass=abc.ABCMeta):
 | 
					class CommonTestsBase(metaclass=abc.ABCMeta):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Tests shared by test_open, test_path, and test_read.
 | 
					    Tests shared by test_open, test_path, and test_read.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -84,34 +84,34 @@ def test_package_name(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Passing in the package name should succeed.
 | 
					        Passing in the package name should succeed.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.execute(data01.__name__, 'utf-8.file')
 | 
					        self.execute(self.data.__name__, 'utf-8.file')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_package_object(self):
 | 
					    def test_package_object(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Passing in the package itself should succeed.
 | 
					        Passing in the package itself should succeed.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.execute(data01, 'utf-8.file')
 | 
					        self.execute(self.data, 'utf-8.file')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_string_path(self):
 | 
					    def test_string_path(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Passing in a string for the path should succeed.
 | 
					        Passing in a string for the path should succeed.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        path = 'utf-8.file'
 | 
					        path = 'utf-8.file'
 | 
				
			||||||
        self.execute(data01, path)
 | 
					        self.execute(self.data, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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 = pathlib.PurePath('utf-8.file')
 | 
					        path = pathlib.PurePath('utf-8.file')
 | 
				
			||||||
        self.execute(data01, path)
 | 
					        self.execute(self.data, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_importing_module_as_side_effect(self):
 | 
					    def test_importing_module_as_side_effect(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        The anchor package can already be imported.
 | 
					        The anchor package can already be imported.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        del sys.modules[data01.__name__]
 | 
					        del sys.modules[self.data.__name__]
 | 
				
			||||||
        self.execute(data01.__name__, 'utf-8.file')
 | 
					        self.execute(self.data.__name__, 'utf-8.file')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_missing_path(self):
 | 
					    def test_missing_path(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -141,24 +141,66 @@ def test_useless_loader(self):
 | 
				
			||||||
            self.execute(package, 'utf-8.file')
 | 
					            self.execute(package, 'utf-8.file')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ZipSetupBase:
 | 
					fixtures = dict(
 | 
				
			||||||
    ZIP_MODULE = 'data01'
 | 
					    data01={
 | 
				
			||||||
 | 
					        '__init__.py': '',
 | 
				
			||||||
 | 
					        'binary.file': bytes(range(4)),
 | 
				
			||||||
 | 
					        'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'),
 | 
				
			||||||
 | 
					        'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'),
 | 
				
			||||||
 | 
					        'subdirectory': {
 | 
				
			||||||
 | 
					            '__init__.py': '',
 | 
				
			||||||
 | 
					            'binary.file': bytes(range(4, 8)),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    data02={
 | 
				
			||||||
 | 
					        '__init__.py': '',
 | 
				
			||||||
 | 
					        'one': {'__init__.py': '', 'resource1.txt': 'one resource'},
 | 
				
			||||||
 | 
					        'two': {'__init__.py': '', 'resource2.txt': 'two resource'},
 | 
				
			||||||
 | 
					        'subdirectory': {'subsubdir': {'resource.txt': 'a resource'}},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    namespacedata01={
 | 
				
			||||||
 | 
					        'binary.file': bytes(range(4)),
 | 
				
			||||||
 | 
					        'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'),
 | 
				
			||||||
 | 
					        'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'),
 | 
				
			||||||
 | 
					        'subdirectory': {
 | 
				
			||||||
 | 
					            'binary.file': bytes(range(12, 16)),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ModuleSetup:
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.fixtures = contextlib.ExitStack()
 | 
					        self.fixtures = contextlib.ExitStack()
 | 
				
			||||||
        self.addCleanup(self.fixtures.close)
 | 
					        self.addCleanup(self.fixtures.close)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.fixtures.enter_context(import_helper.isolated_modules())
 | 
					        self.fixtures.enter_context(import_helper.isolated_modules())
 | 
				
			||||||
 | 
					        self.data = self.load_fixture(self.MODULE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_fixture(self, module):
 | 
				
			||||||
 | 
					        self.tree_on_path({module: fixtures[module]})
 | 
				
			||||||
 | 
					        return importlib.import_module(module)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ZipSetup(ModuleSetup):
 | 
				
			||||||
 | 
					    MODULE = 'data01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tree_on_path(self, spec):
 | 
				
			||||||
        temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
 | 
					        temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
 | 
				
			||||||
        modules = pathlib.Path(temp_dir) / 'zipped modules.zip'
 | 
					        modules = pathlib.Path(temp_dir) / 'zipped modules.zip'
 | 
				
			||||||
        src_path = pathlib.Path(__file__).parent.joinpath(self.ZIP_MODULE)
 | 
					 | 
				
			||||||
        self.fixtures.enter_context(
 | 
					        self.fixtures.enter_context(
 | 
				
			||||||
            import_helper.DirsOnSysPath(str(zip_.make_zip_file(src_path, modules)))
 | 
					            import_helper.DirsOnSysPath(str(zip_.make_zip_file(spec, modules)))
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.data = importlib.import_module(self.ZIP_MODULE)
 | 
					
 | 
				
			||||||
 | 
					class DiskSetup(ModuleSetup):
 | 
				
			||||||
 | 
					    MODULE = 'data01'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tree_on_path(self, spec):
 | 
				
			||||||
 | 
					        temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
 | 
				
			||||||
 | 
					        _path.build(spec, pathlib.Path(temp_dir))
 | 
				
			||||||
 | 
					        self.fixtures.enter_context(import_helper.DirsOnSysPath(temp_dir))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ZipSetup(ZipSetupBase):
 | 
					class CommonTests(DiskSetup, CommonTestsBase):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,29 +2,23 @@
 | 
				
			||||||
Generate zip test data files.
 | 
					Generate zip test data files.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import contextlib
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import pathlib
 | 
					 | 
				
			||||||
import zipfile
 | 
					import zipfile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def make_zip_file(src, dst):
 | 
					def make_zip_file(tree, dst):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Zip the files in src into a new zipfile at dst.
 | 
					    Zip the files in tree into a new zipfile at dst.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    with zipfile.ZipFile(dst, 'w') as zf:
 | 
					    with zipfile.ZipFile(dst, 'w') as zf:
 | 
				
			||||||
        for src_path, rel in walk(src):
 | 
					        for name, contents in walk(tree):
 | 
				
			||||||
            dst_name = src.name / pathlib.PurePosixPath(rel.as_posix())
 | 
					            zf.writestr(name, contents)
 | 
				
			||||||
            zf.write(src_path, dst_name)
 | 
					 | 
				
			||||||
        zipfile._path.CompleteDirs.inject(zf)
 | 
					        zipfile._path.CompleteDirs.inject(zf)
 | 
				
			||||||
    return dst
 | 
					    return dst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def walk(datapath):
 | 
					def walk(tree, prefix=''):
 | 
				
			||||||
    for dirpath, dirnames, filenames in os.walk(datapath):
 | 
					    for name, contents in tree.items():
 | 
				
			||||||
        with contextlib.suppress(ValueError):
 | 
					        if isinstance(contents, dict):
 | 
				
			||||||
            dirnames.remove('__pycache__')
 | 
					            yield from walk(contents, prefix=f'{prefix}{name}/')
 | 
				
			||||||
        for filename in filenames:
 | 
					        else:
 | 
				
			||||||
            res = pathlib.Path(dirpath) / filename
 | 
					            yield f'{prefix}{name}', contents
 | 
				
			||||||
            rel = res.relative_to(datapath)
 | 
					 | 
				
			||||||
            yield res, rel
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -256,17 +256,9 @@ def load_module(self, fullname):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_resource_reader(self, fullname):
 | 
					    def get_resource_reader(self, fullname):
 | 
				
			||||||
        """Return the ResourceReader for a package in a zip file.
 | 
					        """Return the ResourceReader for a module 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
 | 
					 | 
				
			||||||
        from importlib.readers import ZipReader
 | 
					        from importlib.readers import ZipReader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ZipReader(self, fullname)
 | 
					        return ZipReader(self, fullname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2489,21 +2489,6 @@ TESTSUBDIRS=	idlelib/idle_test \
 | 
				
			||||||
		test/test_importlib/namespace_pkgs/project3/parent/child \
 | 
							test/test_importlib/namespace_pkgs/project3/parent/child \
 | 
				
			||||||
		test/test_importlib/partial \
 | 
							test/test_importlib/partial \
 | 
				
			||||||
		test/test_importlib/resources \
 | 
							test/test_importlib/resources \
 | 
				
			||||||
		test/test_importlib/resources/data01 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data01/subdirectory \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data02 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data02/one \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data02/subdirectory \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data02/subdirectory/subsubdir \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data02/two \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data03 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data03/namespace \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data03/namespace/portion1 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/data03/namespace/portion2 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/namespacedata01 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/namespacedata01/subdirectory \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/zipdata01 \
 | 
					 | 
				
			||||||
		test/test_importlib/resources/zipdata02 \
 | 
					 | 
				
			||||||
		test/test_importlib/source \
 | 
							test/test_importlib/source \
 | 
				
			||||||
		test/test_inspect \
 | 
							test/test_inspect \
 | 
				
			||||||
		test/test_interpreters \
 | 
							test/test_interpreters \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)``).
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue