17db96d56Sopenharmony_ciimport re 27db96d56Sopenharmony_ciimport json 37db96d56Sopenharmony_ciimport pickle 47db96d56Sopenharmony_ciimport unittest 57db96d56Sopenharmony_ciimport warnings 67db96d56Sopenharmony_ciimport importlib.metadata 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_citry: 97db96d56Sopenharmony_ci import pyfakefs.fake_filesystem_unittest as ffs 107db96d56Sopenharmony_ciexcept ImportError: 117db96d56Sopenharmony_ci from .stubs import fake_filesystem_unittest as ffs 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_cifrom . import fixtures 147db96d56Sopenharmony_cifrom importlib.metadata import ( 157db96d56Sopenharmony_ci Distribution, 167db96d56Sopenharmony_ci EntryPoint, 177db96d56Sopenharmony_ci PackageNotFoundError, 187db96d56Sopenharmony_ci _unique, 197db96d56Sopenharmony_ci distributions, 207db96d56Sopenharmony_ci entry_points, 217db96d56Sopenharmony_ci metadata, 227db96d56Sopenharmony_ci packages_distributions, 237db96d56Sopenharmony_ci version, 247db96d56Sopenharmony_ci) 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ciclass BasicTests(fixtures.DistInfoPkg, unittest.TestCase): 287db96d56Sopenharmony_ci version_pattern = r'\d+\.\d+(\.\d)?' 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci def test_retrieves_version_of_self(self): 317db96d56Sopenharmony_ci dist = Distribution.from_name('distinfo-pkg') 327db96d56Sopenharmony_ci assert isinstance(dist.version, str) 337db96d56Sopenharmony_ci assert re.match(self.version_pattern, dist.version) 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci def test_for_name_does_not_exist(self): 367db96d56Sopenharmony_ci with self.assertRaises(PackageNotFoundError): 377db96d56Sopenharmony_ci Distribution.from_name('does-not-exist') 387db96d56Sopenharmony_ci 397db96d56Sopenharmony_ci def test_package_not_found_mentions_metadata(self): 407db96d56Sopenharmony_ci """ 417db96d56Sopenharmony_ci When a package is not found, that could indicate that the 427db96d56Sopenharmony_ci packgae is not installed or that it is installed without 437db96d56Sopenharmony_ci metadata. Ensure the exception mentions metadata to help 447db96d56Sopenharmony_ci guide users toward the cause. See #124. 457db96d56Sopenharmony_ci """ 467db96d56Sopenharmony_ci with self.assertRaises(PackageNotFoundError) as ctx: 477db96d56Sopenharmony_ci Distribution.from_name('does-not-exist') 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci assert "metadata" in str(ctx.exception) 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ci def test_new_style_classes(self): 527db96d56Sopenharmony_ci self.assertIsInstance(Distribution, type) 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci @fixtures.parameterize( 557db96d56Sopenharmony_ci dict(name=None), 567db96d56Sopenharmony_ci dict(name=''), 577db96d56Sopenharmony_ci ) 587db96d56Sopenharmony_ci def test_invalid_inputs_to_from_name(self, name): 597db96d56Sopenharmony_ci with self.assertRaises(Exception): 607db96d56Sopenharmony_ci Distribution.from_name(name) 617db96d56Sopenharmony_ci 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ciclass ImportTests(fixtures.DistInfoPkg, unittest.TestCase): 647db96d56Sopenharmony_ci def test_import_nonexistent_module(self): 657db96d56Sopenharmony_ci # Ensure that the MetadataPathFinder does not crash an import of a 667db96d56Sopenharmony_ci # non-existent module. 677db96d56Sopenharmony_ci with self.assertRaises(ImportError): 687db96d56Sopenharmony_ci importlib.import_module('does_not_exist') 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci def test_resolve(self): 717db96d56Sopenharmony_ci ep = entry_points(group='entries')['main'] 727db96d56Sopenharmony_ci self.assertEqual(ep.load().__name__, "main") 737db96d56Sopenharmony_ci 747db96d56Sopenharmony_ci def test_entrypoint_with_colon_in_name(self): 757db96d56Sopenharmony_ci ep = entry_points(group='entries')['ns:sub'] 767db96d56Sopenharmony_ci self.assertEqual(ep.value, 'mod:main') 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci def test_resolve_without_attr(self): 797db96d56Sopenharmony_ci ep = EntryPoint( 807db96d56Sopenharmony_ci name='ep', 817db96d56Sopenharmony_ci value='importlib.metadata', 827db96d56Sopenharmony_ci group='grp', 837db96d56Sopenharmony_ci ) 847db96d56Sopenharmony_ci assert ep.load() is importlib.metadata 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ciclass NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): 887db96d56Sopenharmony_ci @staticmethod 897db96d56Sopenharmony_ci def make_pkg(name): 907db96d56Sopenharmony_ci """ 917db96d56Sopenharmony_ci Create minimal metadata for a dist-info package with 927db96d56Sopenharmony_ci the indicated name on the file system. 937db96d56Sopenharmony_ci """ 947db96d56Sopenharmony_ci return { 957db96d56Sopenharmony_ci f'{name}.dist-info': { 967db96d56Sopenharmony_ci 'METADATA': 'VERSION: 1.0\n', 977db96d56Sopenharmony_ci }, 987db96d56Sopenharmony_ci } 997db96d56Sopenharmony_ci 1007db96d56Sopenharmony_ci def test_dashes_in_dist_name_found_as_underscores(self): 1017db96d56Sopenharmony_ci """ 1027db96d56Sopenharmony_ci For a package with a dash in the name, the dist-info metadata 1037db96d56Sopenharmony_ci uses underscores in the name. Ensure the metadata loads. 1047db96d56Sopenharmony_ci """ 1057db96d56Sopenharmony_ci fixtures.build_files(self.make_pkg('my_pkg'), self.site_dir) 1067db96d56Sopenharmony_ci assert version('my-pkg') == '1.0' 1077db96d56Sopenharmony_ci 1087db96d56Sopenharmony_ci def test_dist_name_found_as_any_case(self): 1097db96d56Sopenharmony_ci """ 1107db96d56Sopenharmony_ci Ensure the metadata loads when queried with any case. 1117db96d56Sopenharmony_ci """ 1127db96d56Sopenharmony_ci pkg_name = 'CherryPy' 1137db96d56Sopenharmony_ci fixtures.build_files(self.make_pkg(pkg_name), self.site_dir) 1147db96d56Sopenharmony_ci assert version(pkg_name) == '1.0' 1157db96d56Sopenharmony_ci assert version(pkg_name.lower()) == '1.0' 1167db96d56Sopenharmony_ci assert version(pkg_name.upper()) == '1.0' 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci def test_unique_distributions(self): 1197db96d56Sopenharmony_ci """ 1207db96d56Sopenharmony_ci Two distributions varying only by non-normalized name on 1217db96d56Sopenharmony_ci the file system should resolve as the same. 1227db96d56Sopenharmony_ci """ 1237db96d56Sopenharmony_ci fixtures.build_files(self.make_pkg('abc'), self.site_dir) 1247db96d56Sopenharmony_ci before = list(_unique(distributions())) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci alt_site_dir = self.fixtures.enter_context(fixtures.tempdir()) 1277db96d56Sopenharmony_ci self.fixtures.enter_context(self.add_sys_path(alt_site_dir)) 1287db96d56Sopenharmony_ci fixtures.build_files(self.make_pkg('ABC'), alt_site_dir) 1297db96d56Sopenharmony_ci after = list(_unique(distributions())) 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci assert len(after) == len(before) 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ciclass NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): 1357db96d56Sopenharmony_ci @staticmethod 1367db96d56Sopenharmony_ci def pkg_with_non_ascii_description(site_dir): 1377db96d56Sopenharmony_ci """ 1387db96d56Sopenharmony_ci Create minimal metadata for a package with non-ASCII in 1397db96d56Sopenharmony_ci the description. 1407db96d56Sopenharmony_ci """ 1417db96d56Sopenharmony_ci contents = { 1427db96d56Sopenharmony_ci 'portend.dist-info': { 1437db96d56Sopenharmony_ci 'METADATA': 'Description: pôrˈtend', 1447db96d56Sopenharmony_ci }, 1457db96d56Sopenharmony_ci } 1467db96d56Sopenharmony_ci fixtures.build_files(contents, site_dir) 1477db96d56Sopenharmony_ci return 'portend' 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci @staticmethod 1507db96d56Sopenharmony_ci def pkg_with_non_ascii_description_egg_info(site_dir): 1517db96d56Sopenharmony_ci """ 1527db96d56Sopenharmony_ci Create minimal metadata for an egg-info package with 1537db96d56Sopenharmony_ci non-ASCII in the description. 1547db96d56Sopenharmony_ci """ 1557db96d56Sopenharmony_ci contents = { 1567db96d56Sopenharmony_ci 'portend.dist-info': { 1577db96d56Sopenharmony_ci 'METADATA': """ 1587db96d56Sopenharmony_ci Name: portend 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci pôrˈtend""", 1617db96d56Sopenharmony_ci }, 1627db96d56Sopenharmony_ci } 1637db96d56Sopenharmony_ci fixtures.build_files(contents, site_dir) 1647db96d56Sopenharmony_ci return 'portend' 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci def test_metadata_loads(self): 1677db96d56Sopenharmony_ci pkg_name = self.pkg_with_non_ascii_description(self.site_dir) 1687db96d56Sopenharmony_ci meta = metadata(pkg_name) 1697db96d56Sopenharmony_ci assert meta['Description'] == 'pôrˈtend' 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci def test_metadata_loads_egg_info(self): 1727db96d56Sopenharmony_ci pkg_name = self.pkg_with_non_ascii_description_egg_info(self.site_dir) 1737db96d56Sopenharmony_ci meta = metadata(pkg_name) 1747db96d56Sopenharmony_ci assert meta['Description'] == 'pôrˈtend' 1757db96d56Sopenharmony_ci 1767db96d56Sopenharmony_ci 1777db96d56Sopenharmony_ciclass DiscoveryTests(fixtures.EggInfoPkg, fixtures.DistInfoPkg, unittest.TestCase): 1787db96d56Sopenharmony_ci def test_package_discovery(self): 1797db96d56Sopenharmony_ci dists = list(distributions()) 1807db96d56Sopenharmony_ci assert all(isinstance(dist, Distribution) for dist in dists) 1817db96d56Sopenharmony_ci assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists) 1827db96d56Sopenharmony_ci assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) 1837db96d56Sopenharmony_ci 1847db96d56Sopenharmony_ci def test_invalid_usage(self): 1857db96d56Sopenharmony_ci with self.assertRaises(ValueError): 1867db96d56Sopenharmony_ci list(distributions(context='something', name='else')) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci 1897db96d56Sopenharmony_ciclass DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): 1907db96d56Sopenharmony_ci def test_egg_info(self): 1917db96d56Sopenharmony_ci # make an `EGG-INFO` directory that's unrelated 1927db96d56Sopenharmony_ci self.site_dir.joinpath('EGG-INFO').mkdir() 1937db96d56Sopenharmony_ci # used to crash with `IsADirectoryError` 1947db96d56Sopenharmony_ci with self.assertRaises(PackageNotFoundError): 1957db96d56Sopenharmony_ci version('unknown-package') 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ci def test_egg(self): 1987db96d56Sopenharmony_ci egg = self.site_dir.joinpath('foo-3.6.egg') 1997db96d56Sopenharmony_ci egg.mkdir() 2007db96d56Sopenharmony_ci with self.add_sys_path(egg): 2017db96d56Sopenharmony_ci with self.assertRaises(PackageNotFoundError): 2027db96d56Sopenharmony_ci version('foo') 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ciclass MissingSysPath(fixtures.OnSysPath, unittest.TestCase): 2067db96d56Sopenharmony_ci site_dir = '/does-not-exist' 2077db96d56Sopenharmony_ci 2087db96d56Sopenharmony_ci def test_discovery(self): 2097db96d56Sopenharmony_ci """ 2107db96d56Sopenharmony_ci Discovering distributions should succeed even if 2117db96d56Sopenharmony_ci there is an invalid path on sys.path. 2127db96d56Sopenharmony_ci """ 2137db96d56Sopenharmony_ci importlib.metadata.distributions() 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ciclass InaccessibleSysPath(fixtures.OnSysPath, ffs.TestCase): 2177db96d56Sopenharmony_ci site_dir = '/access-denied' 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci def setUp(self): 2207db96d56Sopenharmony_ci super().setUp() 2217db96d56Sopenharmony_ci self.setUpPyfakefs() 2227db96d56Sopenharmony_ci self.fs.create_dir(self.site_dir, perm_bits=000) 2237db96d56Sopenharmony_ci 2247db96d56Sopenharmony_ci def test_discovery(self): 2257db96d56Sopenharmony_ci """ 2267db96d56Sopenharmony_ci Discovering distributions should succeed even if 2277db96d56Sopenharmony_ci there is an invalid path on sys.path. 2287db96d56Sopenharmony_ci """ 2297db96d56Sopenharmony_ci list(importlib.metadata.distributions()) 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_ciclass TestEntryPoints(unittest.TestCase): 2337db96d56Sopenharmony_ci def __init__(self, *args): 2347db96d56Sopenharmony_ci super().__init__(*args) 2357db96d56Sopenharmony_ci self.ep = importlib.metadata.EntryPoint( 2367db96d56Sopenharmony_ci name='name', value='value', group='group' 2377db96d56Sopenharmony_ci ) 2387db96d56Sopenharmony_ci 2397db96d56Sopenharmony_ci def test_entry_point_pickleable(self): 2407db96d56Sopenharmony_ci revived = pickle.loads(pickle.dumps(self.ep)) 2417db96d56Sopenharmony_ci assert revived == self.ep 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def test_positional_args(self): 2447db96d56Sopenharmony_ci """ 2457db96d56Sopenharmony_ci Capture legacy (namedtuple) construction, discouraged. 2467db96d56Sopenharmony_ci """ 2477db96d56Sopenharmony_ci EntryPoint('name', 'value', 'group') 2487db96d56Sopenharmony_ci 2497db96d56Sopenharmony_ci def test_immutable(self): 2507db96d56Sopenharmony_ci """EntryPoints should be immutable""" 2517db96d56Sopenharmony_ci with self.assertRaises(AttributeError): 2527db96d56Sopenharmony_ci self.ep.name = 'badactor' 2537db96d56Sopenharmony_ci 2547db96d56Sopenharmony_ci def test_repr(self): 2557db96d56Sopenharmony_ci assert 'EntryPoint' in repr(self.ep) 2567db96d56Sopenharmony_ci assert 'name=' in repr(self.ep) 2577db96d56Sopenharmony_ci assert "'name'" in repr(self.ep) 2587db96d56Sopenharmony_ci 2597db96d56Sopenharmony_ci def test_hashable(self): 2607db96d56Sopenharmony_ci """EntryPoints should be hashable""" 2617db96d56Sopenharmony_ci hash(self.ep) 2627db96d56Sopenharmony_ci 2637db96d56Sopenharmony_ci def test_json_dump(self): 2647db96d56Sopenharmony_ci """ 2657db96d56Sopenharmony_ci json should not expect to be able to dump an EntryPoint 2667db96d56Sopenharmony_ci """ 2677db96d56Sopenharmony_ci with self.assertRaises(Exception): 2687db96d56Sopenharmony_ci with warnings.catch_warnings(record=True): 2697db96d56Sopenharmony_ci json.dumps(self.ep) 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ci def test_module(self): 2727db96d56Sopenharmony_ci assert self.ep.module == 'value' 2737db96d56Sopenharmony_ci 2747db96d56Sopenharmony_ci def test_attr(self): 2757db96d56Sopenharmony_ci assert self.ep.attr is None 2767db96d56Sopenharmony_ci 2777db96d56Sopenharmony_ci def test_sortable(self): 2787db96d56Sopenharmony_ci """ 2797db96d56Sopenharmony_ci EntryPoint objects are sortable, but result is undefined. 2807db96d56Sopenharmony_ci """ 2817db96d56Sopenharmony_ci sorted( 2827db96d56Sopenharmony_ci [ 2837db96d56Sopenharmony_ci EntryPoint(name='b', value='val', group='group'), 2847db96d56Sopenharmony_ci EntryPoint(name='a', value='val', group='group'), 2857db96d56Sopenharmony_ci ] 2867db96d56Sopenharmony_ci ) 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci 2897db96d56Sopenharmony_ciclass FileSystem( 2907db96d56Sopenharmony_ci fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase 2917db96d56Sopenharmony_ci): 2927db96d56Sopenharmony_ci def test_unicode_dir_on_sys_path(self): 2937db96d56Sopenharmony_ci """ 2947db96d56Sopenharmony_ci Ensure a Unicode subdirectory of a directory on sys.path 2957db96d56Sopenharmony_ci does not crash. 2967db96d56Sopenharmony_ci """ 2977db96d56Sopenharmony_ci fixtures.build_files( 2987db96d56Sopenharmony_ci {self.unicode_filename(): {}}, 2997db96d56Sopenharmony_ci prefix=self.site_dir, 3007db96d56Sopenharmony_ci ) 3017db96d56Sopenharmony_ci list(distributions()) 3027db96d56Sopenharmony_ci 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ciclass PackagesDistributionsPrebuiltTest(fixtures.ZipFixtures, unittest.TestCase): 3057db96d56Sopenharmony_ci def test_packages_distributions_example(self): 3067db96d56Sopenharmony_ci self._fixture_on_path('example-21.12-py3-none-any.whl') 3077db96d56Sopenharmony_ci assert packages_distributions()['example'] == ['example'] 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci def test_packages_distributions_example2(self): 3107db96d56Sopenharmony_ci """ 3117db96d56Sopenharmony_ci Test packages_distributions on a wheel built 3127db96d56Sopenharmony_ci by trampolim. 3137db96d56Sopenharmony_ci """ 3147db96d56Sopenharmony_ci self._fixture_on_path('example2-1.0.0-py3-none-any.whl') 3157db96d56Sopenharmony_ci assert packages_distributions()['example2'] == ['example2'] 3167db96d56Sopenharmony_ci 3177db96d56Sopenharmony_ci 3187db96d56Sopenharmony_ciclass PackagesDistributionsTest( 3197db96d56Sopenharmony_ci fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase 3207db96d56Sopenharmony_ci): 3217db96d56Sopenharmony_ci def test_packages_distributions_neither_toplevel_nor_files(self): 3227db96d56Sopenharmony_ci """ 3237db96d56Sopenharmony_ci Test a package built without 'top-level.txt' or a file list. 3247db96d56Sopenharmony_ci """ 3257db96d56Sopenharmony_ci fixtures.build_files( 3267db96d56Sopenharmony_ci { 3277db96d56Sopenharmony_ci 'trim_example-1.0.0.dist-info': { 3287db96d56Sopenharmony_ci 'METADATA': """ 3297db96d56Sopenharmony_ci Name: trim_example 3307db96d56Sopenharmony_ci Version: 1.0.0 3317db96d56Sopenharmony_ci """, 3327db96d56Sopenharmony_ci } 3337db96d56Sopenharmony_ci }, 3347db96d56Sopenharmony_ci prefix=self.site_dir, 3357db96d56Sopenharmony_ci ) 3367db96d56Sopenharmony_ci packages_distributions() 337