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: ignore comments in .pth files #85

Merged
merged 5 commits into from
Jun 14, 2022
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
4 changes: 0 additions & 4 deletions src/griffe/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ class NameResolutionError(GriffeError):
"""Exception for names that cannot be resolved in a object scope."""


class UnhandledPthFileError(GriffeError):
"""Exception for unhandled .pth files, when searching modules."""


class UnhandledEditablesModuleError(GriffeError):
"""Exception for unhandled editables modules, when searching modules."""

Expand Down
32 changes: 22 additions & 10 deletions src/griffe/finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import Iterator, Sequence, Tuple

from griffe.dataclasses import Module
from griffe.exceptions import UnhandledEditablesModuleError, UnhandledPthFileError
from griffe.exceptions import UnhandledEditablesModuleError

NamePartsType = Tuple[str, ...]
NamePartsAndPathType = Tuple[NamePartsType, Path]
Expand Down Expand Up @@ -234,8 +234,8 @@ def _extend_from_pth_files(self):
for path in self.search_paths:
for item in self._contents(path):
if item.suffix == ".pth":
with suppress(UnhandledPthFileError):
self._append_search_path(_handle_pth_file(item))
for directory in _handle_pth_file(item):
self._append_search_path(directory)

def _extend_from_editables_modules(self):
for path in self.search_paths: # noqa: WPS440
Expand Down Expand Up @@ -271,6 +271,7 @@ 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]")


# TODO: for better robustness, we should load and minify the AST
Expand All @@ -284,13 +285,24 @@ def _module_depth(name_parts_and_path: NamePartsAndPathType) -> int:
return len(name_parts_and_path[0])


def _handle_pth_file(path):
# support for .pth files pointing to a directory
instructions = path.read_text().strip("\n").split(";")
added_dir = Path(instructions[0])
if added_dir.exists():
return added_dir
raise UnhandledPthFileError(path)
def _handle_pth_file(path) -> list[Path]: # noqa: WPS231
# Support for .pth files pointing to directories.
# From https://docs.python.org/3/library/site.html:
# A path configuration file is a file whose name has the form name.pth
# and exists in one of the four directories mentioned above;
# its contents are additional items (one per line) to be added to sys.path.
# Non-existing items are never added to sys.path,
# and no check is made that the item refers to a directory rather than a file.
# No item is added to sys.path more than once.
# Blank lines and lines beginning with # are skipped.
# Lines starting with import (followed by space or tab) are executed.
directories = []
for line in path.read_text().strip().splitlines(keepends=False):
line = line.strip()
if line and not line.startswith("#") and not _re_import_line.search(line):
if os.path.exists(line):
directories.append(Path(line))
return directories


def _handle_editables_module(path: Path):
Expand Down
28 changes: 27 additions & 1 deletion tests/test_finder.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Tests for the `finder` module."""

from pathlib import Path
from textwrap import dedent

import pytest

from griffe.finder import ModuleFinder
from griffe.finder import ModuleFinder, _handle_pth_file # noqa: WPS450
from tests.helpers import temporary_pypackage


Expand Down Expand Up @@ -58,3 +61,26 @@ def test_find_pkg_style_namespace_packages(statement):
assert package.name == "namespace"
assert package.is_namespace
assert package.path == [tmp_package1.path.parent, tmp_package2.path.parent]


def test_pth_file_handling(tmp_path):
"""Assert .pth files are correctly handled.

Parameters:
tmp_path: Pytest fixture.
"""
pth_file = tmp_path / "hello.pth"
pth_file.write_text(
dedent(
"""
# comment

import thing
import\tthing
/doesnotexist
tests
"""
)
)
directories = _handle_pth_file(pth_file)
assert directories == [Path("tests")]