-
Notifications
You must be signed in to change notification settings - Fork 2
Support collecting editable distributions #117
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| from __future__ import annotations | ||
|
|
||
| import re | ||
| from importlib.metadata import Distribution, distributions | ||
| from importlib.metadata import packages_distributions as pkgs_dists | ||
| from pathlib import Path | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Generator, Mapping | ||
|
|
||
|
|
||
| def packages_distributions() -> Mapping[str, list[str]]: | ||
| """Return a mapping of top-level packages to their distributions. | ||
|
|
||
| Unlike :func:`importlib.metadata.packages_distributions`, | ||
| this includes editable packages. | ||
| """ | ||
| pds = dict(pkgs_dists()) | ||
| for dist in distributions(): | ||
| for pkg_name in _top_level_editable(dist): | ||
| if "." not in pkg_name: # apparently that’s what makes an importable name | ||
| pds.setdefault(pkg_name, []).append(dist.name) | ||
| return pds | ||
|
|
||
|
|
||
| def _top_level_editable(dist: Distribution) -> Generator[str, None, None]: | ||
| """Find top-level packages in an editable distribution.""" | ||
| for pth_file in dist.files or (): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I follow what is in
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if len(pth_file.parts) != 1 or pth_file.suffix != ".pth": | ||
| continue | ||
| for line in pth_file.read_text().splitlines(): | ||
| if re.match(r"import\s", line): | ||
| continue # https://docs.python.org/3/library/site.html | ||
| for p in Path(line).iterdir(): | ||
| yield from _find_top_level(p) | ||
|
|
||
|
|
||
| def _find_top_level(root: Path) -> Generator[str, None, None]: | ||
| if root.suffix == ".py" and "." not in root.stem and root.is_file(): | ||
| yield root.stem | ||
| return | ||
| if "." in root.name or not root.is_dir(): | ||
| return | ||
| if (root / "__init__.py").is_file(): | ||
| yield root.name | ||
| return | ||
| for p in root.iterdir(): | ||
| for pkg in _find_top_level(p): | ||
| yield f"{root.name}.{pkg}" | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there docs for this
distributionsfunction https://docs.python.org/3/library/importlib.metadata.html? Presumably it gets all packages in the current env or?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, right. I’m pretty sure it’s safe given that it’s in
__all__and therefore meant to be exported: python/cpython#110937There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, @jaraco said so: python/importlib_metadata#494 (comment)