mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	 667294d5b2
			
		
	
	
		667294d5b2
		
			
		
	
	
	
	
		
			
			* Apply changes from importlib_metadata 7.1.0 * Include the data sources in the makefile (even though they're not needed)
		
			
				
	
	
		
			380 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			380 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import os
 | |
| import sys
 | |
| import copy
 | |
| import json
 | |
| import shutil
 | |
| import pathlib
 | |
| import tempfile
 | |
| import textwrap
 | |
| import functools
 | |
| import contextlib
 | |
| 
 | |
| from test.support import import_helper
 | |
| from test.support import os_helper
 | |
| from test.support import requires_zlib
 | |
| 
 | |
| from . import _path
 | |
| from ._path import FilesSpec
 | |
| 
 | |
| 
 | |
| try:
 | |
|     from importlib import resources  # type: ignore
 | |
| 
 | |
|     getattr(resources, 'files')
 | |
|     getattr(resources, 'as_file')
 | |
| except (ImportError, AttributeError):
 | |
|     import importlib_resources as resources  # type: ignore
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def tempdir():
 | |
|     tmpdir = tempfile.mkdtemp()
 | |
|     try:
 | |
|         yield pathlib.Path(tmpdir)
 | |
|     finally:
 | |
|         shutil.rmtree(tmpdir)
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def save_cwd():
 | |
|     orig = os.getcwd()
 | |
|     try:
 | |
|         yield
 | |
|     finally:
 | |
|         os.chdir(orig)
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def tempdir_as_cwd():
 | |
|     with tempdir() as tmp:
 | |
|         with save_cwd():
 | |
|             os.chdir(str(tmp))
 | |
|             yield tmp
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def install_finder(finder):
 | |
|     sys.meta_path.append(finder)
 | |
|     try:
 | |
|         yield
 | |
|     finally:
 | |
|         sys.meta_path.remove(finder)
 | |
| 
 | |
| 
 | |
| class Fixtures:
 | |
|     def setUp(self):
 | |
|         self.fixtures = contextlib.ExitStack()
 | |
|         self.addCleanup(self.fixtures.close)
 | |
| 
 | |
| 
 | |
| class SiteDir(Fixtures):
 | |
|     def setUp(self):
 | |
|         super().setUp()
 | |
|         self.site_dir = self.fixtures.enter_context(tempdir())
 | |
| 
 | |
| 
 | |
| class OnSysPath(Fixtures):
 | |
|     @staticmethod
 | |
|     @contextlib.contextmanager
 | |
|     def add_sys_path(dir):
 | |
|         sys.path[:0] = [str(dir)]
 | |
|         try:
 | |
|             yield
 | |
|         finally:
 | |
|             sys.path.remove(str(dir))
 | |
| 
 | |
|     def setUp(self):
 | |
|         super().setUp()
 | |
|         self.fixtures.enter_context(self.add_sys_path(self.site_dir))
 | |
|         self.fixtures.enter_context(import_helper.isolated_modules())
 | |
| 
 | |
| 
 | |
| class SiteBuilder(SiteDir):
 | |
|     def setUp(self):
 | |
|         super().setUp()
 | |
|         for cls in self.__class__.mro():
 | |
|             with contextlib.suppress(AttributeError):
 | |
|                 build_files(cls.files, prefix=self.site_dir)
 | |
| 
 | |
| 
 | |
| class DistInfoPkg(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "distinfo_pkg-1.0.0.dist-info": {
 | |
|             "METADATA": """
 | |
|                 Name: distinfo-pkg
 | |
|                 Author: Steven Ma
 | |
|                 Version: 1.0.0
 | |
|                 Requires-Dist: wheel >= 1.0
 | |
|                 Requires-Dist: pytest; extra == 'test'
 | |
|                 Keywords: sample package
 | |
| 
 | |
|                 Once upon a time
 | |
|                 There was a distinfo pkg
 | |
|                 """,
 | |
|             "RECORD": "mod.py,sha256=abc,20\n",
 | |
|             "entry_points.txt": """
 | |
|                 [entries]
 | |
|                 main = mod:main
 | |
|                 ns:sub = mod:main
 | |
|             """,
 | |
|         },
 | |
|         "mod.py": """
 | |
|             def main():
 | |
|                 print("hello world")
 | |
|             """,
 | |
|     }
 | |
| 
 | |
|     def make_uppercase(self):
 | |
|         """
 | |
|         Rewrite metadata with everything uppercase.
 | |
|         """
 | |
|         shutil.rmtree(self.site_dir / "distinfo_pkg-1.0.0.dist-info")
 | |
|         files = copy.deepcopy(DistInfoPkg.files)
 | |
|         info = files["distinfo_pkg-1.0.0.dist-info"]
 | |
|         info["METADATA"] = info["METADATA"].upper()
 | |
|         build_files(files, self.site_dir)
 | |
| 
 | |
| 
 | |
| class DistInfoPkgEditable(DistInfoPkg):
 | |
|     """
 | |
|     Package with a PEP 660 direct_url.json.
 | |
|     """
 | |
| 
 | |
|     some_hash = '524127ce937f7cb65665130c695abd18ca386f60bb29687efb976faa1596fdcc'
 | |
|     files: FilesSpec = {
 | |
|         'distinfo_pkg-1.0.0.dist-info': {
 | |
|             'direct_url.json': json.dumps({
 | |
|                 "archive_info": {
 | |
|                     "hash": f"sha256={some_hash}",
 | |
|                     "hashes": {"sha256": f"{some_hash}"},
 | |
|                 },
 | |
|                 "url": "file:///path/to/distinfo_pkg-1.0.0.editable-py3-none-any.whl",
 | |
|             })
 | |
|         },
 | |
|     }
 | |
| 
 | |
| 
 | |
| class DistInfoPkgWithDot(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "pkg_dot-1.0.0.dist-info": {
 | |
|             "METADATA": """
 | |
|                 Name: pkg.dot
 | |
|                 Version: 1.0.0
 | |
|                 """,
 | |
|         },
 | |
|     }
 | |
| 
 | |
| 
 | |
| class DistInfoPkgWithDotLegacy(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "pkg.dot-1.0.0.dist-info": {
 | |
|             "METADATA": """
 | |
|                 Name: pkg.dot
 | |
|                 Version: 1.0.0
 | |
|                 """,
 | |
|         },
 | |
|         "pkg.lot.egg-info": {
 | |
|             "METADATA": """
 | |
|                 Name: pkg.lot
 | |
|                 Version: 1.0.0
 | |
|                 """,
 | |
|         },
 | |
|     }
 | |
| 
 | |
| 
 | |
| class DistInfoPkgOffPath(SiteBuilder):
 | |
|     files = DistInfoPkg.files
 | |
| 
 | |
| 
 | |
| class EggInfoPkg(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "egginfo_pkg.egg-info": {
 | |
|             "PKG-INFO": """
 | |
|                 Name: egginfo-pkg
 | |
|                 Author: Steven Ma
 | |
|                 License: Unknown
 | |
|                 Version: 1.0.0
 | |
|                 Classifier: Intended Audience :: Developers
 | |
|                 Classifier: Topic :: Software Development :: Libraries
 | |
|                 Keywords: sample package
 | |
|                 Description: Once upon a time
 | |
|                         There was an egginfo package
 | |
|                 """,
 | |
|             "SOURCES.txt": """
 | |
|                 mod.py
 | |
|                 egginfo_pkg.egg-info/top_level.txt
 | |
|             """,
 | |
|             "entry_points.txt": """
 | |
|                 [entries]
 | |
|                 main = mod:main
 | |
|             """,
 | |
|             "requires.txt": """
 | |
|                 wheel >= 1.0; python_version >= "2.7"
 | |
|                 [test]
 | |
|                 pytest
 | |
|             """,
 | |
|             "top_level.txt": "mod\n",
 | |
|         },
 | |
|         "mod.py": """
 | |
|             def main():
 | |
|                 print("hello world")
 | |
|             """,
 | |
|     }
 | |
| 
 | |
| 
 | |
| class EggInfoPkgPipInstalledNoToplevel(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "egg_with_module_pkg.egg-info": {
 | |
|             "PKG-INFO": "Name: egg_with_module-pkg",
 | |
|             # SOURCES.txt is made from the source archive, and contains files
 | |
|             # (setup.py) that are not present after installation.
 | |
|             "SOURCES.txt": """
 | |
|                 egg_with_module.py
 | |
|                 setup.py
 | |
|                 egg_with_module_pkg.egg-info/PKG-INFO
 | |
|                 egg_with_module_pkg.egg-info/SOURCES.txt
 | |
|                 egg_with_module_pkg.egg-info/top_level.txt
 | |
|             """,
 | |
|             # installed-files.txt is written by pip, and is a strictly more
 | |
|             # accurate source than SOURCES.txt as to the installed contents of
 | |
|             # the package.
 | |
|             "installed-files.txt": """
 | |
|                 ../egg_with_module.py
 | |
|                 PKG-INFO
 | |
|                 SOURCES.txt
 | |
|                 top_level.txt
 | |
|             """,
 | |
|             # missing top_level.txt (to trigger fallback to installed-files.txt)
 | |
|         },
 | |
|         "egg_with_module.py": """
 | |
|             def main():
 | |
|                 print("hello world")
 | |
|             """,
 | |
|     }
 | |
| 
 | |
| 
 | |
| class EggInfoPkgPipInstalledNoModules(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "egg_with_no_modules_pkg.egg-info": {
 | |
|             "PKG-INFO": "Name: egg_with_no_modules-pkg",
 | |
|             # SOURCES.txt is made from the source archive, and contains files
 | |
|             # (setup.py) that are not present after installation.
 | |
|             "SOURCES.txt": """
 | |
|                 setup.py
 | |
|                 egg_with_no_modules_pkg.egg-info/PKG-INFO
 | |
|                 egg_with_no_modules_pkg.egg-info/SOURCES.txt
 | |
|                 egg_with_no_modules_pkg.egg-info/top_level.txt
 | |
|             """,
 | |
|             # installed-files.txt is written by pip, and is a strictly more
 | |
|             # accurate source than SOURCES.txt as to the installed contents of
 | |
|             # the package.
 | |
|             "installed-files.txt": """
 | |
|                 PKG-INFO
 | |
|                 SOURCES.txt
 | |
|                 top_level.txt
 | |
|             """,
 | |
|             # top_level.txt correctly reflects that no modules are installed
 | |
|             "top_level.txt": b"\n",
 | |
|         },
 | |
|     }
 | |
| 
 | |
| 
 | |
| class EggInfoPkgSourcesFallback(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "sources_fallback_pkg.egg-info": {
 | |
|             "PKG-INFO": "Name: sources_fallback-pkg",
 | |
|             # SOURCES.txt is made from the source archive, and contains files
 | |
|             # (setup.py) that are not present after installation.
 | |
|             "SOURCES.txt": """
 | |
|                 sources_fallback.py
 | |
|                 setup.py
 | |
|                 sources_fallback_pkg.egg-info/PKG-INFO
 | |
|                 sources_fallback_pkg.egg-info/SOURCES.txt
 | |
|             """,
 | |
|             # missing installed-files.txt (i.e. not installed by pip) and
 | |
|             # missing top_level.txt (to trigger fallback to SOURCES.txt)
 | |
|         },
 | |
|         "sources_fallback.py": """
 | |
|             def main():
 | |
|                 print("hello world")
 | |
|             """,
 | |
|     }
 | |
| 
 | |
| 
 | |
| class EggInfoFile(OnSysPath, SiteBuilder):
 | |
|     files: FilesSpec = {
 | |
|         "egginfo_file.egg-info": """
 | |
|             Metadata-Version: 1.0
 | |
|             Name: egginfo_file
 | |
|             Version: 0.1
 | |
|             Summary: An example package
 | |
|             Home-page: www.example.com
 | |
|             Author: Eric Haffa-Vee
 | |
|             Author-email: eric@example.coms
 | |
|             License: UNKNOWN
 | |
|             Description: UNKNOWN
 | |
|             Platform: UNKNOWN
 | |
|             """,
 | |
|     }
 | |
| 
 | |
| 
 | |
| # dedent all text strings before writing
 | |
| orig = _path.create.registry[str]
 | |
| _path.create.register(str, lambda content, path: orig(DALS(content), path))
 | |
| 
 | |
| 
 | |
| build_files = _path.build
 | |
| 
 | |
| 
 | |
| def build_record(file_defs):
 | |
|     return ''.join(f'{name},,\n' for name in record_names(file_defs))
 | |
| 
 | |
| 
 | |
| def record_names(file_defs):
 | |
|     recording = _path.Recording()
 | |
|     _path.build(file_defs, recording)
 | |
|     return recording.record
 | |
| 
 | |
| 
 | |
| class FileBuilder:
 | |
|     def unicode_filename(self):
 | |
|         return os_helper.FS_NONASCII or self.skip(
 | |
|             "File system does not support non-ascii."
 | |
|         )
 | |
| 
 | |
| 
 | |
| def DALS(str):
 | |
|     "Dedent and left-strip"
 | |
|     return textwrap.dedent(str).lstrip()
 | |
| 
 | |
| 
 | |
| @requires_zlib()
 | |
| class ZipFixtures:
 | |
|     root = 'test.test_importlib.metadata.data'
 | |
| 
 | |
|     def _fixture_on_path(self, filename):
 | |
|         pkg_file = resources.files(self.root).joinpath(filename)
 | |
|         file = self.resources.enter_context(resources.as_file(pkg_file))
 | |
|         assert file.name.startswith('example'), file.name
 | |
|         sys.path.insert(0, str(file))
 | |
|         self.resources.callback(sys.path.pop, 0)
 | |
| 
 | |
|     def setUp(self):
 | |
|         # Add self.zip_name to the front of sys.path.
 | |
|         self.resources = contextlib.ExitStack()
 | |
|         self.addCleanup(self.resources.close)
 | |
| 
 | |
| 
 | |
| def parameterize(*args_set):
 | |
|     """Run test method with a series of parameters."""
 | |
| 
 | |
|     def wrapper(func):
 | |
|         @functools.wraps(func)
 | |
|         def _inner(self):
 | |
|             for args in args_set:
 | |
|                 with self.subTest(**args):
 | |
|                     func(self, **args)
 | |
| 
 | |
|         return _inner
 | |
| 
 | |
|     return wrapper
 |