2019-05-24 20:59:01 -03:00
|
|
|
|
import re
|
2019-12-10 21:05:10 -04:00
|
|
|
|
import pickle
|
2019-05-24 20:59:01 -03:00
|
|
|
|
import unittest
|
2023-04-20 23:12:48 -03:00
|
|
|
|
import warnings
|
2024-03-21 00:01:24 -03:00
|
|
|
|
import importlib
|
2019-05-24 20:59:01 -03:00
|
|
|
|
import importlib.metadata
|
2023-04-20 23:12:48 -03:00
|
|
|
|
import contextlib
|
2024-01-16 11:10:03 -04:00
|
|
|
|
from test.support import os_helper
|
2019-05-24 20:59:01 -03:00
|
|
|
|
|
2020-02-11 22:58:47 -04:00
|
|
|
|
try:
|
|
|
|
|
import pyfakefs.fake_filesystem_unittest as ffs
|
|
|
|
|
except ImportError:
|
2022-01-22 22:38:26 -04:00
|
|
|
|
from .stubs import fake_filesystem_unittest as ffs
|
2020-02-11 22:58:47 -04:00
|
|
|
|
|
2022-01-22 22:38:26 -04:00
|
|
|
|
from . import fixtures
|
2023-04-20 23:12:48 -03:00
|
|
|
|
from ._context import suppress
|
2023-12-21 16:04:05 -04:00
|
|
|
|
from ._path import Symlink
|
2019-05-24 20:59:01 -03:00
|
|
|
|
from importlib.metadata import (
|
2020-12-31 13:56:43 -04:00
|
|
|
|
Distribution,
|
|
|
|
|
EntryPoint,
|
|
|
|
|
PackageNotFoundError,
|
2022-06-25 22:04:28 -03:00
|
|
|
|
_unique,
|
2020-12-31 13:56:43 -04:00
|
|
|
|
distributions,
|
|
|
|
|
entry_points,
|
|
|
|
|
metadata,
|
2021-12-16 16:49:42 -04:00
|
|
|
|
packages_distributions,
|
2020-12-31 13:56:43 -04:00
|
|
|
|
version,
|
|
|
|
|
)
|
2019-05-24 20:59:01 -03:00
|
|
|
|
|
|
|
|
|
|
2023-04-20 23:12:48 -03: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 20:59:01 -03: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 18:14:21 -03:00
|
|
|
|
def test_package_not_found_mentions_metadata(self):
|
2022-04-17 12:10:26 -03:00
|
|
|
|
"""
|
|
|
|
|
When a package is not found, that could indicate that the
|
2023-04-20 23:12:48 -03:00
|
|
|
|
package is not installed or that it is installed without
|
2022-04-17 12:10:26 -03:00
|
|
|
|
metadata. Ensure the exception mentions metadata to help
|
|
|
|
|
guide users toward the cause. See #124.
|
|
|
|
|
"""
|
2020-10-19 18:14:21 -03:00
|
|
|
|
with self.assertRaises(PackageNotFoundError) as ctx:
|
|
|
|
|
Distribution.from_name('does-not-exist')
|
|
|
|
|
|
|
|
|
|
assert "metadata" in str(ctx.exception)
|
|
|
|
|
|
2023-04-20 23:12:48 -03: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 20:59:01 -03:00
|
|
|
|
|
2022-06-25 22:04:28 -03:00
|
|
|
|
@fixtures.parameterize(
|
|
|
|
|
dict(name=None),
|
|
|
|
|
dict(name=''),
|
|
|
|
|
)
|
|
|
|
|
def test_invalid_inputs_to_from_name(self, name):
|
2023-12-21 16:04:05 -04:00
|
|
|
|
with self.assertRaises(Exception):
|
2022-06-25 22:04:28 -03:00
|
|
|
|
Distribution.from_name(name)
|
|
|
|
|
|
2019-05-24 20:59:01 -03: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-30 17:21:19 -03:00
|
|
|
|
# non-existent module.
|
2019-05-24 20:59:01 -03:00
|
|
|
|
with self.assertRaises(ImportError):
|
|
|
|
|
importlib.import_module('does_not_exist')
|
|
|
|
|
|
|
|
|
|
def test_resolve(self):
|
2021-03-13 12:31:45 -04:00
|
|
|
|
ep = entry_points(group='entries')['main']
|
2019-05-24 20:59:01 -03:00
|
|
|
|
self.assertEqual(ep.load().__name__, "main")
|
|
|
|
|
|
2019-07-28 15:59:24 -03:00
|
|
|
|
def test_entrypoint_with_colon_in_name(self):
|
2021-03-13 12:31:45 -04:00
|
|
|
|
ep = entry_points(group='entries')['ns:sub']
|
2019-07-28 15:59:24 -03:00
|
|
|
|
self.assertEqual(ep.value, 'mod:main')
|
|
|
|
|
|
2019-05-24 20:59:01 -03:00
|
|
|
|
def test_resolve_without_attr(self):
|
|
|
|
|
ep = EntryPoint(
|
|
|
|
|
name='ep',
|
|
|
|
|
value='importlib.metadata',
|
|
|
|
|
group='grp',
|
2020-12-31 13:56:43 -04:00
|
|
|
|
)
|
2019-05-24 20:59:01 -03:00
|
|
|
|
assert ep.load() is importlib.metadata
|
|
|
|
|
|
|
|
|
|
|
2020-12-31 13:56:43 -04:00
|
|
|
|
class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
2019-05-24 20:59:01 -03:00
|
|
|
|
@staticmethod
|
2022-06-25 22:04:28 -03:00
|
|
|
|
def make_pkg(name):
|
2019-05-24 20:59:01 -03:00
|
|
|
|
"""
|
2022-06-25 22:04:28 -03:00
|
|
|
|
Create minimal metadata for a dist-info package with
|
|
|
|
|
the indicated name on the file system.
|
2019-05-24 20:59:01 -03:00
|
|
|
|
"""
|
2022-06-25 22:04:28 -03:00
|
|
|
|
return {
|
|
|
|
|
f'{name}.dist-info': {
|
|
|
|
|
'METADATA': 'VERSION: 1.0\n',
|
|
|
|
|
},
|
|
|
|
|
}
|
2019-05-24 20:59:01 -03:00
|
|
|
|
|
|
|
|
|
def test_dashes_in_dist_name_found_as_underscores(self):
|
2022-04-17 12:10:26 -03: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 22:04:28 -03:00
|
|
|
|
fixtures.build_files(self.make_pkg('my_pkg'), self.site_dir)
|
|
|
|
|
assert version('my-pkg') == '1.0'
|
2019-05-24 20:59:01 -03:00
|
|
|
|
|
|
|
|
|
def test_dist_name_found_as_any_case(self):
|
2022-04-17 12:10:26 -03:00
|
|
|
|
"""
|
|
|
|
|
Ensure the metadata loads when queried with any case.
|
|
|
|
|
"""
|
2022-06-25 22:04:28 -03:00
|
|
|
|
pkg_name = 'CherryPy'
|
|
|
|
|
fixtures.build_files(self.make_pkg(pkg_name), self.site_dir)
|
2019-05-24 20:59:01 -03:00
|
|
|
|
assert version(pkg_name) == '1.0'
|
|
|
|
|
assert version(pkg_name.lower()) == '1.0'
|
|
|
|
|
assert version(pkg_name.upper()) == '1.0'
|
|
|
|
|
|
2022-06-25 22:04:28 -03: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()))
|
|
|
|
|
|
|
|
|
|
alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
|
|
|
|
|
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 20:59:01 -03: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 22:04:28 -03:00
|
|
|
|
contents = {
|
|
|
|
|
'portend.dist-info': {
|
|
|
|
|
'METADATA': 'Description: pôrˈtend',
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
fixtures.build_files(contents, site_dir)
|
2019-05-24 20:59:01 -03: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 22:04:28 -03:00
|
|
|
|
contents = {
|
|
|
|
|
'portend.dist-info': {
|
|
|
|
|
'METADATA': """
|
2019-05-24 20:59:01 -03:00
|
|
|
|
Name: portend
|
|
|
|
|
|
2022-06-25 22:04:28 -03:00
|
|
|
|
pôrˈtend""",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
fixtures.build_files(contents, site_dir)
|
2019-05-24 20:59:01 -03: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 18:03:40 -03:00
|
|
|
|
assert meta['Description'] == 'pôrˈtend'
|
2019-05-24 20:59:01 -03:00
|
|
|
|
|
|
|
|
|
|
2023-04-20 23:12:48 -03:00
|
|
|
|
class DiscoveryTests(
|
|
|
|
|
fixtures.EggInfoPkg,
|
|
|
|
|
fixtures.EggInfoPkgPipInstalledNoToplevel,
|
|
|
|
|
fixtures.EggInfoPkgPipInstalledNoModules,
|
|
|
|
|
fixtures.EggInfoPkgSourcesFallback,
|
|
|
|
|
fixtures.DistInfoPkg,
|
|
|
|
|
unittest.TestCase,
|
|
|
|
|
):
|
2019-05-24 20:59:01 -03:00
|
|
|
|
def test_package_discovery(self):
|
|
|
|
|
dists = list(distributions())
|
2020-12-31 13:56:43 -04:00
|
|
|
|
assert all(isinstance(dist, Distribution) for dist in dists)
|
|
|
|
|
assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists)
|
2023-04-20 23:12:48 -03: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 13:56:43 -04:00
|
|
|
|
assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists)
|
2019-05-29 21:13:12 -03:00
|
|
|
|
|
2019-09-10 10:53:31 -03:00
|
|
|
|
def test_invalid_usage(self):
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
|
list(distributions(context='something', name='else'))
|
|
|
|
|
|
2023-12-21 16:04:05 -04: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 21:13:12 -03:00
|
|
|
|
|
|
|
|
|
class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
|
2019-07-28 15:59:24 -03:00
|
|
|
|
def test_egg_info(self):
|
2019-05-29 21:13:12 -03:00
|
|
|
|
# make an `EGG-INFO` directory that's unrelated
|
|
|
|
|
self.site_dir.joinpath('EGG-INFO').mkdir()
|
|
|
|
|
# used to crash with `IsADirectoryError`
|
2019-07-28 15:59:24 -03: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 21:05:10 -04:00
|
|
|
|
|
|
|
|
|
|
2020-02-11 22:58:47 -04: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 16:49:42 -04:00
|
|
|
|
super().setUp()
|
2020-02-11 22:58:47 -04: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 21:05:10 -04:00
|
|
|
|
class TestEntryPoints(unittest.TestCase):
|
|
|
|
|
def __init__(self, *args):
|
2021-12-16 16:49:42 -04:00
|
|
|
|
super().__init__(*args)
|
|
|
|
|
self.ep = importlib.metadata.EntryPoint(
|
|
|
|
|
name='name', value='value', group='group'
|
|
|
|
|
)
|
2019-12-10 21:05:10 -04:00
|
|
|
|
|
|
|
|
|
def test_entry_point_pickleable(self):
|
|
|
|
|
revived = pickle.loads(pickle.dumps(self.ep))
|
|
|
|
|
assert revived == self.ep
|
|
|
|
|
|
2021-12-16 16:49:42 -04:00
|
|
|
|
def test_positional_args(self):
|
|
|
|
|
"""
|
|
|
|
|
Capture legacy (namedtuple) construction, discouraged.
|
|
|
|
|
"""
|
|
|
|
|
EntryPoint('name', 'value', 'group')
|
|
|
|
|
|
2019-12-10 21:05:10 -04: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 12:10:26 -03:00
|
|
|
|
"""EntryPoints should be hashable"""
|
2019-12-10 21:05:10 -04:00
|
|
|
|
hash(self.ep)
|
|
|
|
|
|
2020-06-05 17:34:16 -03:00
|
|
|
|
def test_module(self):
|
|
|
|
|
assert self.ep.module == 'value'
|
|
|
|
|
|
|
|
|
|
def test_attr(self):
|
|
|
|
|
assert self.ep.attr is None
|
|
|
|
|
|
2020-12-31 13:56:43 -04:00
|
|
|
|
def test_sortable(self):
|
2022-04-17 12:10:26 -03:00
|
|
|
|
"""
|
|
|
|
|
EntryPoint objects are sortable, but result is undefined.
|
|
|
|
|
"""
|
2024-03-21 00:01:24 -03:00
|
|
|
|
sorted([
|
|
|
|
|
EntryPoint(name='b', value='val', group='group'),
|
|
|
|
|
EntryPoint(name='a', value='val', group='group'),
|
|
|
|
|
])
|
2020-12-31 13:56:43 -04:00
|
|
|
|
|
2020-06-05 17:34:16 -03:00
|
|
|
|
|
2020-06-07 11:57:45 -03:00
|
|
|
|
class FileSystem(
|
2020-12-31 13:56:43 -04:00
|
|
|
|
fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase
|
|
|
|
|
):
|
2020-06-05 17:34:16 -03:00
|
|
|
|
def test_unicode_dir_on_sys_path(self):
|
2022-04-17 12:10:26 -03:00
|
|
|
|
"""
|
|
|
|
|
Ensure a Unicode subdirectory of a directory on sys.path
|
|
|
|
|
does not crash.
|
|
|
|
|
"""
|
2020-06-07 11:57:45 -03:00
|
|
|
|
fixtures.build_files(
|
|
|
|
|
{self.unicode_filename(): {}},
|
|
|
|
|
prefix=self.site_dir,
|
2020-12-31 13:56:43 -04:00
|
|
|
|
)
|
2020-06-05 17:34:16 -03:00
|
|
|
|
list(distributions())
|
2021-12-16 16:49:42 -04: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 23:12:48 -03: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-21 00:01:24 -03: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 23:12:48 -03: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 11:10:03 -04:00
|
|
|
|
@os_helper.skip_unless_symlink
|
2023-12-21 16:04:05 -04: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 23:12:48 -03: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 16:04:05 -04: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
|