Skip to content

Commit

Permalink
feat: Provide line numbers for classes and functions when inspecting
Browse files Browse the repository at this point in the history
Issue-272: #272
  • Loading branch information
pawamoy committed Jun 8, 2024
1 parent 08c3f40 commit b6ddcc4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 5 deletions.
21 changes: 20 additions & 1 deletion src/griffe/agents/inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

import ast
from inspect import Parameter as SignatureParameter
from inspect import Signature, cleandoc
from inspect import Signature, cleandoc, getsourcelines
from inspect import signature as getsignature
from typing import TYPE_CHECKING, Any, Sequence

Expand Down Expand Up @@ -150,6 +150,16 @@ def _get_docstring(self, node: ObjectNode) -> Docstring | None:
parser_options=self.docstring_options,
)

def _get_linenos(self, node: ObjectNode) -> tuple[int, int] | tuple[None, None]:
# Line numbers won't be useful if we don't have the source code.
if not self.filepath or self.filepath not in self.lines_collection:
return None, None
try:
lines, lineno = getsourcelines(node.obj)
except (OSError, TypeError):
return None, None
return lineno, lineno + "".join(lines).rstrip().count("\n")

def get_module(self, import_paths: Sequence[str | Path] | None = None) -> Module:
"""Build and return the object representing the module attached to this inspector.
Expand Down Expand Up @@ -291,10 +301,13 @@ def inspect_class(self, node: ObjectNode) -> None:
continue
bases.append(f"{base.__module__}.{base.__qualname__}")

lineno, endlineno = self._get_linenos(node)
class_ = Class(
name=node.name,
docstring=self._get_docstring(node),
bases=bases,
lineno=lineno,
endlineno=endlineno,
)
self.current.set_member(node.name, class_)
self.current = class_
Expand Down Expand Up @@ -413,6 +426,8 @@ def handle_function(self, node: ObjectNode, labels: set | None = None) -> None:
else _convert_object_to_annotation(return_annotation, parent=self.current)
)

lineno, endlineno = self._get_linenos(node)

obj: Attribute | Function
labels = labels or set()
if "property" in labels:
Expand All @@ -421,13 +436,17 @@ def handle_function(self, node: ObjectNode, labels: set | None = None) -> None:
value=None,
annotation=returns,
docstring=self._get_docstring(node),
lineno=lineno,
endlineno=endlineno,
)
else:
obj = Function(
name=node.name,
parameters=parameters,
returns=returns,
docstring=self._get_docstring(node),
lineno=lineno,
endlineno=endlineno,
)
obj.labels |= labels
self.current.set_member(node.name, obj)
Expand Down
3 changes: 3 additions & 0 deletions src/griffe/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def __getitem__(self, key: Path) -> list[str]:
def __setitem__(self, key: Path, value: list[str]) -> None:
self._data[key] = value

def __contains__(self, item: Path) -> bool:
return item in self._data

def __bool__(self) -> bool:
return True

Expand Down
41 changes: 37 additions & 4 deletions tests/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import griffe
from griffe.dataclasses import Docstring, Module
from griffe.loader import GriffeLoader
from griffe.tests import module_vtree, temporary_pypackage, temporary_visited_package
from griffe.tests import module_vtree, temporary_inspected_module, temporary_pypackage, temporary_visited_package


def test_submodule_exports() -> None:
Expand Down Expand Up @@ -317,14 +317,14 @@ def __init__(self, r: float): ...
assert ["x", "y", "z", "__init__"] == list(point_b.members)


def test_module_source() -> None:
def test_visited_module_source() -> None:
"""Check the source property of a module."""
code = "print('hello')\nprint('world')"
with temporary_visited_package("package", {"__init__.py": code}) as module:
assert module.source == code


def test_class_source() -> None:
def test_visited_class_source() -> None:
"""Check the source property of a class."""
code = """
class A:
Expand All @@ -335,7 +335,7 @@ def __init__(self, x: int):
assert module["A"].source == dedent(code).strip()


def test_object_source_with_missing_line_number() -> None:
def test_visited_object_source_with_missing_line_number() -> None:
"""Check the source property of an object with missing line number."""
code = """
class A:
Expand All @@ -348,3 +348,36 @@ def __init__(self, x: int):
module["A"].endlineno = 3
module["A"].lineno = None
assert not module["A"].source


def test_inspected_module_source() -> None:
"""Check the source property of a module."""
code = "print('hello')\nprint('world')"
with temporary_inspected_module(code) as module:
assert module.source == code


def test_inspected_class_source() -> None:
"""Check the source property of a class."""
code = """
class A:
def __init__(self, x: int):
self.x = x
"""
with temporary_inspected_module(code) as module:
assert module["A"].source == dedent(code).strip()


def test_inspected_object_source_with_missing_line_number() -> None:
"""Check the source property of an object with missing line number."""
code = """
class A:
def __init__(self, x: int):
self.x = x
"""
with temporary_inspected_module(code) as module:
module["A"].endlineno = None
assert not module["A"].source
module["A"].endlineno = 3
module["A"].lineno = None
assert not module["A"].source

0 comments on commit b6ddcc4

Please sign in to comment.