Skip to content

Commit e21eabe

Browse files
has2k1pawamoy
andauthored
feat: Add docstring attribute to parameters
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 <[email protected]>
1 parent dd954ad commit e21eabe

File tree

4 files changed

+59
-2
lines changed

4 files changed

+59
-2
lines changed

src/griffe/dataclasses.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ def __init__(
176176
annotation: str | Expr | None = None,
177177
kind: ParameterKind | None = None,
178178
default: str | Expr | None = None,
179+
docstring: Docstring | None = None,
179180
) -> None:
180181
"""Initialize the parameter.
181182
@@ -184,6 +185,7 @@ def __init__(
184185
annotation: The parameter annotation, if any.
185186
kind: The parameter kind.
186187
default: The parameter default, if any.
188+
docstring: The parameter docstring.
187189
"""
188190
self.name: str = name
189191
"""The parameter name."""
@@ -193,6 +195,12 @@ def __init__(
193195
"""The parameter kind."""
194196
self.default: str | Expr | None = default
195197
"""The parameter default value."""
198+
self.docstring: Docstring | None = docstring
199+
"""The parameter docstring."""
200+
# The parent function is set in `Function.__init__`,
201+
# when the parameters are assigned to the function.
202+
self.function: Function | None = None
203+
"""The parent function of the parameter."""
196204

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

221-
def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
229+
def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
222230
"""Return this parameter's data as a dictionary.
223231
224232
Parameters:
@@ -227,12 +235,15 @@ def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
227235
Returns:
228236
A dictionary.
229237
"""
230-
return {
238+
base: dict[str, Any] = {
231239
"name": self.name,
232240
"annotation": self.annotation,
233241
"kind": self.kind,
234242
"default": self.default,
235243
}
244+
if self.docstring:
245+
base["docstring"] = self.docstring.as_dict(full=full)
246+
return base
236247

237248

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

1664+
for parameter in self.parameters:
1665+
parameter.function = self
1666+
16531667
@property
16541668
def annotation(self) -> str | Expr | None:
16551669
"""The type annotation of the returned value."""

src/griffe/encoders.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ def _load_parameter(obj_dict: dict[str, Any]) -> Parameter:
147147
annotation=obj_dict["annotation"],
148148
kind=ParameterKind(obj_dict["kind"]),
149149
default=obj_dict["default"],
150+
docstring=_load_docstring(obj_dict),
150151
)
151152

152153

src/griffe/extensions/dataclasses.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def _dataclass_parameters(class_: Class) -> list[Parameter]:
125125
annotation=member.annotation,
126126
kind=kind,
127127
default=default,
128+
docstring=member.docstring,
128129
),
129130
)
130131

tests/test_dataclasses.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,44 @@ def __init__(self, x: int):
380380
module["A"].endlineno = 3
381381
module["A"].lineno = None
382382
assert not module["A"].source
383+
384+
385+
def test_dataclass_parameter_docstrings() -> None:
386+
"""Class parameters should have a docstring attribute."""
387+
code = """
388+
from dataclasses import dataclass, InitVar
389+
390+
@dataclass
391+
class Base:
392+
a: int
393+
"Parameter a"
394+
b: InitVar[int] = 3
395+
"Parameter b"
396+
397+
@dataclass
398+
class Derived(Base):
399+
c: float
400+
d: InitVar[float]
401+
"Parameter d"
402+
"""
403+
404+
with temporary_visited_package("package", {"__init__.py": code}) as module:
405+
base = module["Base"]
406+
param_self = base.parameters[0]
407+
param_a = base.parameters[1]
408+
param_b = base.parameters[2]
409+
assert param_self.docstring is None
410+
assert param_a.docstring.value == "Parameter a"
411+
assert param_b.docstring.value == "Parameter b"
412+
413+
derived = module["Derived"]
414+
param_self = derived.parameters[0]
415+
param_a = derived.parameters[1]
416+
param_b = derived.parameters[2]
417+
param_c = derived.parameters[3]
418+
param_d = derived.parameters[4]
419+
assert param_self.docstring is None
420+
assert param_a.docstring.value == "Parameter a"
421+
assert param_b.docstring.value == "Parameter b"
422+
assert param_c.docstring is None
423+
assert param_d.docstring.value == "Parameter d"

0 commit comments

Comments
 (0)