Skip to content

Commit

Permalink
feat: Add docstring attribute to parameters
Browse files Browse the repository at this point in the history
This change gives the Parameter class the `docstring` and `function` attributes. 

The docstring value is only populated for dataclasses.
This makes it possible to access this information for all dataclass parameters,
especially those set as `InitVar` where it was not possible to get it
since they are not members (attributes) of the class.

This slot can also be used to support things like PEP 727 (with extensions).
In the future, we might add such a docstring slot to yielded, received and returned values too.

Issue-286: #286
Related-to-mkdocstrings/griffe#252: #252
PR-288: #288
Co-authored-by: Timothée Mazzucotelli <dev@pawamoy.fr>
  • Loading branch information
has2k1 and pawamoy committed Jun 12, 2024
1 parent dd954ad commit e21eabe
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
18 changes: 16 additions & 2 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def __init__(
annotation: str | Expr | None = None,
kind: ParameterKind | None = None,
default: str | Expr | None = None,
docstring: Docstring | None = None,
) -> None:
"""Initialize the parameter.
Expand All @@ -184,6 +185,7 @@ def __init__(
annotation: The parameter annotation, if any.
kind: The parameter kind.
default: The parameter default, if any.
docstring: The parameter docstring.
"""
self.name: str = name
"""The parameter name."""
Expand All @@ -193,6 +195,12 @@ def __init__(
"""The parameter kind."""
self.default: str | Expr | None = default
"""The parameter default value."""
self.docstring: Docstring | None = docstring
"""The parameter docstring."""
# The parent function is set in `Function.__init__`,
# when the parameters are assigned to the function.
self.function: Function | None = None
"""The parent function of the parameter."""

def __str__(self) -> str:
param = f"{self.name}: {self.annotation} = {self.default}"
Expand All @@ -218,7 +226,7 @@ def required(self) -> bool:
"""Whether this parameter is required."""
return self.default is None

def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
"""Return this parameter's data as a dictionary.
Parameters:
Expand All @@ -227,12 +235,15 @@ def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
Returns:
A dictionary.
"""
return {
base: dict[str, Any] = {
"name": self.name,
"annotation": self.annotation,
"kind": self.kind,
"default": self.default,
}
if self.docstring:
base["docstring"] = self.docstring.as_dict(full=full)
return base


class Parameters:
Expand Down Expand Up @@ -1650,6 +1661,9 @@ def __init__(
self.overloads: list[Function] | None = None
"""The overloaded signatures of this function."""

for parameter in self.parameters:
parameter.function = self

@property
def annotation(self) -> str | Expr | None:
"""The type annotation of the returned value."""
Expand Down
1 change: 1 addition & 0 deletions src/griffe/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def _load_parameter(obj_dict: dict[str, Any]) -> Parameter:
annotation=obj_dict["annotation"],
kind=ParameterKind(obj_dict["kind"]),
default=obj_dict["default"],
docstring=_load_docstring(obj_dict),
)


Expand Down
1 change: 1 addition & 0 deletions src/griffe/extensions/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def _dataclass_parameters(class_: Class) -> list[Parameter]:
annotation=member.annotation,
kind=kind,
default=default,
docstring=member.docstring,
),
)

Expand Down
41 changes: 41 additions & 0 deletions tests/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,44 @@ def __init__(self, x: int):
module["A"].endlineno = 3
module["A"].lineno = None
assert not module["A"].source


def test_dataclass_parameter_docstrings() -> None:
"""Class parameters should have a docstring attribute."""
code = """
from dataclasses import dataclass, InitVar
@dataclass
class Base:
a: int
"Parameter a"
b: InitVar[int] = 3
"Parameter b"
@dataclass
class Derived(Base):
c: float
d: InitVar[float]
"Parameter d"
"""

with temporary_visited_package("package", {"__init__.py": code}) as module:
base = module["Base"]
param_self = base.parameters[0]
param_a = base.parameters[1]
param_b = base.parameters[2]
assert param_self.docstring is None
assert param_a.docstring.value == "Parameter a"
assert param_b.docstring.value == "Parameter b"

derived = module["Derived"]
param_self = derived.parameters[0]
param_a = derived.parameters[1]
param_b = derived.parameters[2]
param_c = derived.parameters[3]
param_d = derived.parameters[4]
assert param_self.docstring is None
assert param_a.docstring.value == "Parameter a"
assert param_b.docstring.value == "Parameter b"
assert param_c.docstring is None
assert param_d.docstring.value == "Parameter d"

0 comments on commit e21eabe

Please sign in to comment.