Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detecting and handling PDM's symlinked packages #453

Merged
merged 12 commits into from Jun 18, 2023
7 changes: 7 additions & 0 deletions CHANGES.rst
@@ -1,3 +1,10 @@
v6.7.0
======

* #453: When inferring top-level names that are importable for
distributions in ``package_distributions``, now symlinks to
other directories are honored.

v6.6.0
======

Expand Down
8 changes: 6 additions & 2 deletions importlib_metadata/__init__.py
Expand Up @@ -981,30 +981,34 @@ def _topmost(name: PackagePath) -> Optional[str]:
return top if rest else None


def _get_toplevel_name(name: PackagePath) -> Optional[str]:
def _get_toplevel_name(name: PackagePath) -> str:
"""
Infer a possibly importable module name from a name presumed on
sys.path.

>>> _get_toplevel_name(PackagePath('foo.py'))
'foo'
>>> _get_toplevel_name(PackagePath('foo'))
'foo'
>>> _get_toplevel_name(PackagePath('foo.pyc'))
'foo'
>>> _get_toplevel_name(PackagePath('foo/__init__.py'))
'foo'
>>> _get_toplevel_name(PackagePath('foo.pth'))
'foo.pth'
>>> _get_toplevel_name(PackagePath('foo.dist-info'))
'foo.dist-info'
"""
return _topmost(name) or (
# python/typeshed#10328
inspect.getmodulename(name) # type: ignore
or str(name)
)


def _top_level_inferred(dist):
opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))

@pass_none
def importable_name(name):
return '.' not in name

Expand Down
22 changes: 22 additions & 0 deletions tests/test_main.py
Expand Up @@ -9,6 +9,7 @@

from . import fixtures
from ._context import suppress
from ._path import Symlink
from importlib_metadata import (
Distribution,
EntryPoint,
Expand Down Expand Up @@ -399,6 +400,27 @@ def test_packages_distributions_all_module_types(self):

assert not any(name.endswith('.dist-info') for name in distributions)

def test_packages_distributions_symlinked_top_level(self):
"""
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']


class PackagesDistributionsEggTest(
fixtures.EggInfoPkg,
Expand Down