| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | import re | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  | import pickle | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | import warnings | 
					
						
							| 
									
										
										
										
											2024-03-20 23:01:24 -04:00
										 |  |  |  | import importlib | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | import importlib.metadata | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | import contextlib | 
					
						
							| 
									
										
										
										
											2024-01-16 16:10:03 +01:00
										 |  |  |  | from test.support import os_helper | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  |  | try: | 
					
						
							|  |  |  |  |     import pyfakefs.fake_filesystem_unittest as ffs | 
					
						
							|  |  |  |  | except ImportError: | 
					
						
							| 
									
										
										
										
											2022-01-22 21:38:26 -05:00
										 |  |  |  |     from .stubs import fake_filesystem_unittest as ffs | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-22 21:38:26 -05:00
										 |  |  |  | from . import fixtures | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | from ._context import suppress | 
					
						
							| 
									
										
										
										
											2023-12-21 15:04:05 -05:00
										 |  |  |  | from ._path import Symlink | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | from importlib.metadata import ( | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |     Distribution, | 
					
						
							|  |  |  |  |     EntryPoint, | 
					
						
							|  |  |  |  |     PackageNotFoundError, | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |     _unique, | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |     distributions, | 
					
						
							|  |  |  |  |     entry_points, | 
					
						
							|  |  |  |  |     metadata, | 
					
						
							| 
									
										
										
										
											2021-12-16 15:49:42 -05:00
										 |  |  |  |     packages_distributions, | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |     version, | 
					
						
							|  |  |  |  | ) | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | @contextlib.contextmanager | 
					
						
							|  |  |  |  | def suppress_known_deprecation(): | 
					
						
							|  |  |  |  |     with warnings.catch_warnings(record=True) as ctx: | 
					
						
							|  |  |  |  |         warnings.simplefilter('default', category=DeprecationWarning) | 
					
						
							|  |  |  |  |         yield ctx | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): | 
					
						
							|  |  |  |  |     version_pattern = r'\d+\.\d+(\.\d)?' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_retrieves_version_of_self(self): | 
					
						
							|  |  |  |  |         dist = Distribution.from_name('distinfo-pkg') | 
					
						
							|  |  |  |  |         assert isinstance(dist.version, str) | 
					
						
							|  |  |  |  |         assert re.match(self.version_pattern, dist.version) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_for_name_does_not_exist(self): | 
					
						
							|  |  |  |  |         with self.assertRaises(PackageNotFoundError): | 
					
						
							|  |  |  |  |             Distribution.from_name('does-not-exist') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 14:14:21 -07:00
										 |  |  |  |     def test_package_not_found_mentions_metadata(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """
 | 
					
						
							|  |  |  |  |         When a package is not found, that could indicate that the | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  |         package is not installed or that it is installed without | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         metadata. Ensure the exception mentions metadata to help | 
					
						
							|  |  |  |  |         guide users toward the cause. See #124. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-10-19 14:14:21 -07:00
										 |  |  |  |         with self.assertRaises(PackageNotFoundError) as ctx: | 
					
						
							|  |  |  |  |             Distribution.from_name('does-not-exist') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         assert "metadata" in str(ctx.exception) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  |     # expected to fail until ABC is enforced | 
					
						
							|  |  |  |  |     @suppress(AssertionError) | 
					
						
							|  |  |  |  |     @suppress_known_deprecation() | 
					
						
							|  |  |  |  |     def test_abc_enforced(self): | 
					
						
							|  |  |  |  |         with self.assertRaises(TypeError): | 
					
						
							|  |  |  |  |             type('DistributionSubclass', (Distribution,), {})() | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |     @fixtures.parameterize( | 
					
						
							|  |  |  |  |         dict(name=None), | 
					
						
							|  |  |  |  |         dict(name=''), | 
					
						
							|  |  |  |  |     ) | 
					
						
							|  |  |  |  |     def test_invalid_inputs_to_from_name(self, name): | 
					
						
							| 
									
										
										
										
											2023-12-21 15:04:05 -05:00
										 |  |  |  |         with self.assertRaises(Exception): | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |             Distribution.from_name(name) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class ImportTests(fixtures.DistInfoPkg, unittest.TestCase): | 
					
						
							|  |  |  |  |     def test_import_nonexistent_module(self): | 
					
						
							|  |  |  |  |         # Ensure that the MetadataPathFinder does not crash an import of a | 
					
						
							| 
									
										
										
										
											2019-08-31 06:21:19 +10:00
										 |  |  |  |         # non-existent module. | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         with self.assertRaises(ImportError): | 
					
						
							|  |  |  |  |             importlib.import_module('does_not_exist') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_resolve(self): | 
					
						
							| 
									
										
										
										
											2021-03-13 11:31:45 -05:00
										 |  |  |  |         ep = entry_points(group='entries')['main'] | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         self.assertEqual(ep.load().__name__, "main") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-28 14:59:24 -04:00
										 |  |  |  |     def test_entrypoint_with_colon_in_name(self): | 
					
						
							| 
									
										
										
										
											2021-03-13 11:31:45 -05:00
										 |  |  |  |         ep = entry_points(group='entries')['ns:sub'] | 
					
						
							| 
									
										
										
										
											2019-07-28 14:59:24 -04:00
										 |  |  |  |         self.assertEqual(ep.value, 'mod:main') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |     def test_resolve_without_attr(self): | 
					
						
							|  |  |  |  |         ep = EntryPoint( | 
					
						
							|  |  |  |  |             name='ep', | 
					
						
							|  |  |  |  |             value='importlib.metadata', | 
					
						
							|  |  |  |  |             group='grp', | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         assert ep.load() is importlib.metadata | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  | class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |     def make_pkg(name): | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         Create minimal metadata for a dist-info package with | 
					
						
							|  |  |  |  |         the indicated name on the file system. | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         return { | 
					
						
							|  |  |  |  |             f'{name}.dist-info': { | 
					
						
							|  |  |  |  |                 'METADATA': 'VERSION: 1.0\n', | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_dashes_in_dist_name_found_as_underscores(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """
 | 
					
						
							|  |  |  |  |         For a package with a dash in the name, the dist-info metadata | 
					
						
							|  |  |  |  |         uses underscores in the name. Ensure the metadata loads. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         fixtures.build_files(self.make_pkg('my_pkg'), self.site_dir) | 
					
						
							|  |  |  |  |         assert version('my-pkg') == '1.0' | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_dist_name_found_as_any_case(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Ensure the metadata loads when queried with any case. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         pkg_name = 'CherryPy' | 
					
						
							|  |  |  |  |         fixtures.build_files(self.make_pkg(pkg_name), self.site_dir) | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         assert version(pkg_name) == '1.0' | 
					
						
							|  |  |  |  |         assert version(pkg_name.lower()) == '1.0' | 
					
						
							|  |  |  |  |         assert version(pkg_name.upper()) == '1.0' | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |     def test_unique_distributions(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Two distributions varying only by non-normalized name on | 
					
						
							|  |  |  |  |         the file system should resolve as the same. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         fixtures.build_files(self.make_pkg('abc'), self.site_dir) | 
					
						
							|  |  |  |  |         before = list(_unique(distributions())) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 15:00:39 -04:00
										 |  |  |  |         alt_site_dir = self.fixtures.enter_context(fixtures.tmp_path()) | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         self.fixtures.enter_context(self.add_sys_path(alt_site_dir)) | 
					
						
							|  |  |  |  |         fixtures.build_files(self.make_pkg('ABC'), alt_site_dir) | 
					
						
							|  |  |  |  |         after = list(_unique(distributions())) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         assert len(after) == len(before) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): | 
					
						
							|  |  |  |  |     @staticmethod | 
					
						
							|  |  |  |  |     def pkg_with_non_ascii_description(site_dir): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Create minimal metadata for a package with non-ASCII in | 
					
						
							|  |  |  |  |         the description. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         contents = { | 
					
						
							|  |  |  |  |             'portend.dist-info': { | 
					
						
							|  |  |  |  |                 'METADATA': 'Description: pôrˈtend', | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         fixtures.build_files(contents, site_dir) | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         return 'portend' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @staticmethod | 
					
						
							|  |  |  |  |     def pkg_with_non_ascii_description_egg_info(site_dir): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Create minimal metadata for an egg-info package with | 
					
						
							|  |  |  |  |         non-ASCII in the description. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |         contents = { | 
					
						
							|  |  |  |  |             'portend.dist-info': { | 
					
						
							|  |  |  |  |                 'METADATA': """
 | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |                 Name: portend | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-25 21:04:28 -04:00
										 |  |  |  |                 pôrˈtend""",
 | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         fixtures.build_files(contents, site_dir) | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |         return 'portend' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_metadata_loads(self): | 
					
						
							|  |  |  |  |         pkg_name = self.pkg_with_non_ascii_description(self.site_dir) | 
					
						
							|  |  |  |  |         meta = metadata(pkg_name) | 
					
						
							|  |  |  |  |         assert meta['Description'] == 'pôrˈtend' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_metadata_loads_egg_info(self): | 
					
						
							|  |  |  |  |         pkg_name = self.pkg_with_non_ascii_description_egg_info(self.site_dir) | 
					
						
							|  |  |  |  |         meta = metadata(pkg_name) | 
					
						
							| 
									
										
										
										
											2021-05-02 17:03:40 -04:00
										 |  |  |  |         assert meta['Description'] == 'pôrˈtend' | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | class DiscoveryTests( | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkg, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgPipInstalledNoToplevel, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgPipInstalledNoModules, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgSourcesFallback, | 
					
						
							|  |  |  |  |     fixtures.DistInfoPkg, | 
					
						
							|  |  |  |  |     unittest.TestCase, | 
					
						
							|  |  |  |  | ): | 
					
						
							| 
									
										
										
										
											2019-05-24 19:59:01 -04:00
										 |  |  |  |     def test_package_discovery(self): | 
					
						
							|  |  |  |  |         dists = list(distributions()) | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |         assert all(isinstance(dist, Distribution) for dist in dists) | 
					
						
							|  |  |  |  |         assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists) | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  |         assert any(dist.metadata['Name'] == 'egg_with_module-pkg' for dist in dists) | 
					
						
							|  |  |  |  |         assert any(dist.metadata['Name'] == 'egg_with_no_modules-pkg' for dist in dists) | 
					
						
							|  |  |  |  |         assert any(dist.metadata['Name'] == 'sources_fallback-pkg' for dist in dists) | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |         assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) | 
					
						
							| 
									
										
										
										
											2019-05-29 17:13:12 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 14:53:31 +01:00
										 |  |  |  |     def test_invalid_usage(self): | 
					
						
							|  |  |  |  |         with self.assertRaises(ValueError): | 
					
						
							|  |  |  |  |             list(distributions(context='something', name='else')) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-21 15:04:05 -05:00
										 |  |  |  |     def test_interleaved_discovery(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Ensure interleaved searches are safe. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         When the search is cached, it is possible for searches to be | 
					
						
							|  |  |  |  |         interleaved, so make sure those use-cases are safe. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         Ref #293 | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         dists = distributions() | 
					
						
							|  |  |  |  |         next(dists) | 
					
						
							|  |  |  |  |         version('egginfo-pkg') | 
					
						
							|  |  |  |  |         next(dists) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-29 17:13:12 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2019-07-28 14:59:24 -04:00
										 |  |  |  |     def test_egg_info(self): | 
					
						
							| 
									
										
										
										
											2019-05-29 17:13:12 -07:00
										 |  |  |  |         # make an `EGG-INFO` directory that's unrelated | 
					
						
							|  |  |  |  |         self.site_dir.joinpath('EGG-INFO').mkdir() | 
					
						
							|  |  |  |  |         # used to crash with `IsADirectoryError` | 
					
						
							| 
									
										
										
										
											2019-07-28 14:59:24 -04:00
										 |  |  |  |         with self.assertRaises(PackageNotFoundError): | 
					
						
							|  |  |  |  |             version('unknown-package') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_egg(self): | 
					
						
							|  |  |  |  |         egg = self.site_dir.joinpath('foo-3.6.egg') | 
					
						
							|  |  |  |  |         egg.mkdir() | 
					
						
							|  |  |  |  |         with self.add_sys_path(egg): | 
					
						
							|  |  |  |  |             with self.assertRaises(PackageNotFoundError): | 
					
						
							|  |  |  |  |                 version('foo') | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  |  | class MissingSysPath(fixtures.OnSysPath, unittest.TestCase): | 
					
						
							|  |  |  |  |     site_dir = '/does-not-exist' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_discovery(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Discovering distributions should succeed even if | 
					
						
							|  |  |  |  |         there is an invalid path on sys.path. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         importlib.metadata.distributions() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class InaccessibleSysPath(fixtures.OnSysPath, ffs.TestCase): | 
					
						
							|  |  |  |  |     site_dir = '/access-denied' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2021-12-16 15:49:42 -05:00
										 |  |  |  |         super().setUp() | 
					
						
							| 
									
										
										
										
											2020-02-11 21:58:47 -05:00
										 |  |  |  |         self.setUpPyfakefs() | 
					
						
							|  |  |  |  |         self.fs.create_dir(self.site_dir, perm_bits=000) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_discovery(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Discovering distributions should succeed even if | 
					
						
							|  |  |  |  |         there is an invalid path on sys.path. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         list(importlib.metadata.distributions()) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  | class TestEntryPoints(unittest.TestCase): | 
					
						
							|  |  |  |  |     def __init__(self, *args): | 
					
						
							| 
									
										
										
										
											2021-12-16 15:49:42 -05:00
										 |  |  |  |         super().__init__(*args) | 
					
						
							|  |  |  |  |         self.ep = importlib.metadata.EntryPoint( | 
					
						
							|  |  |  |  |             name='name', value='value', group='group' | 
					
						
							|  |  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_entry_point_pickleable(self): | 
					
						
							|  |  |  |  |         revived = pickle.loads(pickle.dumps(self.ep)) | 
					
						
							|  |  |  |  |         assert revived == self.ep | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-16 15:49:42 -05:00
										 |  |  |  |     def test_positional_args(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Capture legacy (namedtuple) construction, discouraged. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         EntryPoint('name', 'value', 'group') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  |     def test_immutable(self): | 
					
						
							|  |  |  |  |         """EntryPoints should be immutable""" | 
					
						
							|  |  |  |  |         with self.assertRaises(AttributeError): | 
					
						
							|  |  |  |  |             self.ep.name = 'badactor' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_repr(self): | 
					
						
							|  |  |  |  |         assert 'EntryPoint' in repr(self.ep) | 
					
						
							|  |  |  |  |         assert 'name=' in repr(self.ep) | 
					
						
							|  |  |  |  |         assert "'name'" in repr(self.ep) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_hashable(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """EntryPoints should be hashable""" | 
					
						
							| 
									
										
										
										
											2019-12-10 20:05:10 -05:00
										 |  |  |  |         hash(self.ep) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  |  |     def test_module(self): | 
					
						
							|  |  |  |  |         assert self.ep.module == 'value' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_attr(self): | 
					
						
							|  |  |  |  |         assert self.ep.attr is None | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |     def test_sortable(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """
 | 
					
						
							|  |  |  |  |         EntryPoint objects are sortable, but result is undefined. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-03-20 23:01:24 -04:00
										 |  |  |  |         sorted([ | 
					
						
							|  |  |  |  |             EntryPoint(name='b', value='val', group='group'), | 
					
						
							|  |  |  |  |             EntryPoint(name='a', value='val', group='group'), | 
					
						
							|  |  |  |  |         ]) | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-07 10:57:45 -04:00
										 |  |  |  | class FileSystem( | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |     fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase | 
					
						
							|  |  |  |  | ): | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  |  |     def test_unicode_dir_on_sys_path(self): | 
					
						
							| 
									
										
										
										
											2022-04-17 11:10:26 -04:00
										 |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Ensure a Unicode subdirectory of a directory on sys.path | 
					
						
							|  |  |  |  |         does not crash. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-06-07 10:57:45 -04:00
										 |  |  |  |         fixtures.build_files( | 
					
						
							|  |  |  |  |             {self.unicode_filename(): {}}, | 
					
						
							|  |  |  |  |             prefix=self.site_dir, | 
					
						
							| 
									
										
										
										
											2020-12-31 12:56:43 -05:00
										 |  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-06-05 16:34:16 -04:00
										 |  |  |  |         list(distributions()) | 
					
						
							| 
									
										
										
										
											2021-12-16 15:49:42 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class PackagesDistributionsPrebuiltTest(fixtures.ZipFixtures, unittest.TestCase): | 
					
						
							|  |  |  |  |     def test_packages_distributions_example(self): | 
					
						
							|  |  |  |  |         self._fixture_on_path('example-21.12-py3-none-any.whl') | 
					
						
							|  |  |  |  |         assert packages_distributions()['example'] == ['example'] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_packages_distributions_example2(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Test packages_distributions on a wheel built | 
					
						
							|  |  |  |  |         by trampolim. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         self._fixture_on_path('example2-1.0.0-py3-none-any.whl') | 
					
						
							|  |  |  |  |         assert packages_distributions()['example2'] == ['example2'] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class PackagesDistributionsTest( | 
					
						
							|  |  |  |  |     fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase | 
					
						
							|  |  |  |  | ): | 
					
						
							|  |  |  |  |     def test_packages_distributions_neither_toplevel_nor_files(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Test a package built without 'top-level.txt' or a file list. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         fixtures.build_files( | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 'trim_example-1.0.0.dist-info': { | 
					
						
							|  |  |  |  |                     'METADATA': """
 | 
					
						
							|  |  |  |  |                 Name: trim_example | 
					
						
							|  |  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |  |                 """,
 | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |             prefix=self.site_dir, | 
					
						
							|  |  |  |  |         ) | 
					
						
							|  |  |  |  |         packages_distributions() | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     def test_packages_distributions_all_module_types(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Test top-level modules detected on a package without 'top-level.txt'. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         suffixes = importlib.machinery.all_suffixes() | 
					
						
							|  |  |  |  |         metadata = dict( | 
					
						
							|  |  |  |  |             METADATA="""
 | 
					
						
							|  |  |  |  |                 Name: all_distributions | 
					
						
							|  |  |  |  |                 Version: 1.0.0 | 
					
						
							|  |  |  |  |                 """,
 | 
					
						
							|  |  |  |  |         ) | 
					
						
							|  |  |  |  |         files = { | 
					
						
							|  |  |  |  |             'all_distributions-1.0.0.dist-info': metadata, | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |         for i, suffix in enumerate(suffixes): | 
					
						
							| 
									
										
										
										
											2024-03-20 23:01:24 -04:00
										 |  |  |  |             files.update({ | 
					
						
							|  |  |  |  |                 f'importable-name {i}{suffix}': '', | 
					
						
							|  |  |  |  |                 f'in_namespace_{i}': { | 
					
						
							|  |  |  |  |                     f'mod{suffix}': '', | 
					
						
							|  |  |  |  |                 }, | 
					
						
							|  |  |  |  |                 f'in_package_{i}': { | 
					
						
							|  |  |  |  |                     '__init__.py': '', | 
					
						
							|  |  |  |  |                     f'mod{suffix}': '', | 
					
						
							|  |  |  |  |                 }, | 
					
						
							|  |  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  |         metadata.update(RECORD=fixtures.build_record(files)) | 
					
						
							|  |  |  |  |         fixtures.build_files(files, prefix=self.site_dir) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         distributions = packages_distributions() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         for i in range(len(suffixes)): | 
					
						
							|  |  |  |  |             assert distributions[f'importable-name {i}'] == ['all_distributions'] | 
					
						
							|  |  |  |  |             assert distributions[f'in_namespace_{i}'] == ['all_distributions'] | 
					
						
							|  |  |  |  |             assert distributions[f'in_package_{i}'] == ['all_distributions'] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         assert not any(name.endswith('.dist-info') for name in distributions) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 16:10:03 +01:00
										 |  |  |  |     @os_helper.skip_unless_symlink | 
					
						
							| 
									
										
										
										
											2023-12-21 15:04:05 -05:00
										 |  |  |  |     def test_packages_distributions_symlinked_top_level(self) -> None: | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Distribution is resolvable from a simple top-level symlink in RECORD. | 
					
						
							|  |  |  |  |         See #452. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         files: fixtures.FilesSpec = { | 
					
						
							|  |  |  |  |             "symlinked_pkg-1.0.0.dist-info": { | 
					
						
							|  |  |  |  |                 "METADATA": """
 | 
					
						
							|  |  |  |  |                     Name: symlinked-pkg | 
					
						
							|  |  |  |  |                     Version: 1.0.0 | 
					
						
							|  |  |  |  |                     """,
 | 
					
						
							|  |  |  |  |                 "RECORD": "symlinked,,\n", | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |             ".symlink.target": {}, | 
					
						
							|  |  |  |  |             "symlinked": Symlink(".symlink.target"), | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         fixtures.build_files(files, self.site_dir) | 
					
						
							|  |  |  |  |         assert packages_distributions()['symlinked'] == ['symlinked-pkg'] | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-20 22:12:48 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class PackagesDistributionsEggTest( | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkg, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgPipInstalledNoToplevel, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgPipInstalledNoModules, | 
					
						
							|  |  |  |  |     fixtures.EggInfoPkgSourcesFallback, | 
					
						
							|  |  |  |  |     unittest.TestCase, | 
					
						
							|  |  |  |  | ): | 
					
						
							|  |  |  |  |     def test_packages_distributions_on_eggs(self): | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         Test old-style egg packages with a variation of 'top_level.txt', | 
					
						
							|  |  |  |  |         'SOURCES.txt', and 'installed-files.txt', available. | 
					
						
							|  |  |  |  |         """
 | 
					
						
							|  |  |  |  |         distributions = packages_distributions() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         def import_names_from_package(package_name): | 
					
						
							|  |  |  |  |             return { | 
					
						
							|  |  |  |  |                 import_name | 
					
						
							|  |  |  |  |                 for import_name, package_names in distributions.items() | 
					
						
							|  |  |  |  |                 if package_name in package_names | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # egginfo-pkg declares one import ('mod') via top_level.txt | 
					
						
							|  |  |  |  |         assert import_names_from_package('egginfo-pkg') == {'mod'} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # egg_with_module-pkg has one import ('egg_with_module') inferred from | 
					
						
							|  |  |  |  |         # installed-files.txt (top_level.txt is missing) | 
					
						
							|  |  |  |  |         assert import_names_from_package('egg_with_module-pkg') == {'egg_with_module'} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # egg_with_no_modules-pkg should not be associated with any import names | 
					
						
							|  |  |  |  |         # (top_level.txt is empty, and installed-files.txt has no .py files) | 
					
						
							|  |  |  |  |         assert import_names_from_package('egg_with_no_modules-pkg') == set() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         # sources_fallback-pkg has one import ('sources_fallback') inferred from | 
					
						
							|  |  |  |  |         # SOURCES.txt (top_level.txt and installed-files.txt is missing) | 
					
						
							|  |  |  |  |         assert import_names_from_package('sources_fallback-pkg') == {'sources_fallback'} | 
					
						
							| 
									
										
										
										
											2023-12-21 15:04:05 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class EditableDistributionTest(fixtures.DistInfoPkgEditable, unittest.TestCase): | 
					
						
							|  |  |  |  |     def test_origin(self): | 
					
						
							|  |  |  |  |         dist = Distribution.from_name('distinfo-pkg') | 
					
						
							|  |  |  |  |         assert dist.origin.url.endswith('.whl') | 
					
						
							|  |  |  |  |         assert dist.origin.archive_info.hashes.sha256 |