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

fix: support editable install with multiple roots #126

Merged
merged 2 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions src/griffe/finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import ast
import os
import re
import sys
Expand Down Expand Up @@ -264,12 +265,13 @@ def _extend_from_pth_files(self):
for directory in _handle_pth_file(item):
self._append_search_path(directory)

def _extend_from_editable_modules(self):
def _extend_from_editable_modules(self): # noqa: WPS231
for path in self.search_paths: # noqa: WPS440
for item in self._contents(path):
if item.stem.startswith(("__editables_", "__editable__")) and item.suffix == ".py":
with suppress(UnhandledEditableModuleError):
self._append_search_path(_handle_editable_module(item))
for editable_path in _handle_editable_module(item):
self._append_search_path(editable_path)

def _filter_py_modules(self, path: Path) -> Iterator[Path]:
for root, dirs, files in os.walk(path, topdown=True):
Expand Down Expand Up @@ -299,7 +301,6 @@ def _top_module_name(self, path: Path) -> str:
_re_pkgresources = re.compile(r"(?:__import__\([\"']pkg_resources[\"']\).declare_namespace\(__name__\))")
_re_pkgutil = re.compile(r"(?:__path__ = __import__\([\"']pkgutil[\"']\).extend_path\(__path__, __name__\))")
_re_import_line = re.compile(r"^import[ \t]")
_re_mapping_line = re.compile(r"^MAPPING = \{['\"].+['\"]: +['\"](.+)['\"]\}")


# TODO: for better robustness, we should load and minify the AST
Expand Down Expand Up @@ -344,16 +345,17 @@ def _handle_editable_module(path: Path): # noqa: WPS231
new_path = Path(editable_lines[-1].split("'")[3])
if new_path.exists(): # TODO: could remove existence check
if new_path.name.startswith("__init__"):
return new_path.parent.parent
return new_path
return [new_path.parent.parent]
return [new_path]
elif path.name.startswith("__editable__"):
# support for how 'setuptools' writes these files:
# example line: MAPPING = {'griffe': '/media/data/dev/griffe/src/griffe'}
for line in editable_lines:
match = _re_mapping_line.match(line)
if match:
new_path = Path(match.group(1))
if new_path.exists(): # TODO: could remove existence check
return new_path.parent
break
# example line: MAPPING = {'griffe': '/media/data/dev/griffe/src/griffe', 'briffe': '/media/data/dev/griffe/src/briffe'}
parsed_module = ast.parse(path.read_text())
for node in parsed_module.body:
if isinstance(node, ast.Assign):
if isinstance(node.targets[0], ast.Name) and node.targets[0].id == "MAPPING":
if isinstance(node.value, ast.Dict):
return [
Path(constant.s).parent for constant in node.value.values if isinstance(constant, ast.Str)
]
raise UnhandledEditableModuleError(path)
17 changes: 15 additions & 2 deletions tests/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_editables_file_handling(tmp_path):
"""
pth_file = tmp_path / "__editables_whatever.py"
pth_file.write_text("hello\nF.map_module('griffe', 'src/griffe/__init__.py')")
assert _handle_editable_module(pth_file) == Path("src")
assert _handle_editable_module(pth_file) == [Path("src")]


def test_setuptools_file_handling(tmp_path):
Expand All @@ -105,4 +105,17 @@ def test_setuptools_file_handling(tmp_path):
"""
pth_file = tmp_path / "__editable__whatever.py"
pth_file.write_text("hello\nMAPPING = {'griffe': 'src/griffe'}")
assert _handle_editable_module(pth_file) == Path("src")
assert _handle_editable_module(pth_file) == [Path("src")]


def test_setuptools_file_handling_multiple_paths(tmp_path):
"""Assert editable modules by `setuptools` are handled when multiple packages are installed in the same editable.

Parameters:
tmp_path: Pytest fixture.
"""
pth_file = tmp_path / "__editable__whatever.py"
pth_file.write_text(
"hello=1\nMAPPING = {\n'griffe':\n 'src1/griffe', 'briffe':'src2/briffe'}\ndef printer():\n print(hello)"
)
assert _handle_editable_module(pth_file) == [Path("src1"), Path("src2")]