Skip to content

Commit 6d5eb42

Browse files
mattgebertstefanvlarsoner
authored
MAINT: Changed class constructor __init__ GL08 reporting (#592)
Co-authored-by: Stefan van der Walt <[email protected]> Co-authored-by: Eric Larson <[email protected]>
1 parent 06cd4a7 commit 6d5eb42

File tree

4 files changed

+170
-2
lines changed

4 files changed

+170
-2
lines changed

doc/format.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,9 @@ Class docstring
557557
Use the same sections as outlined above (all except :ref:`Returns <returns>`
558558
are applicable). The constructor (``__init__``) should also be documented
559559
here, the :ref:`Parameters <params>` section of the docstring details the
560-
constructor's parameters.
560+
constructor's parameters. While repetition is unnecessary, a docstring for
561+
the class constructor (``__init__``) can, optionally, be added to provide
562+
detailed initialization documentation.
561563

562564
An **Attributes** section, located below the :ref:`Parameters <params>`
563565
section, may be used to describe non-method attributes of the class::

doc/validation.rst

+3
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ inline comments:
183183
def __init__(self): # numpydoc ignore=GL08
184184
pass
185185
186+
Note that a properly formatted :ref:`class <classdoc>` docstring
187+
silences ``G08`` for an ``__init__`` constructor without a docstring.
188+
186189
This is supported by the :ref:`CLI <validation_via_cli>`,
187190
:ref:`pre-commit hook <pre_commit_hook>`, and
188191
:ref:`Sphinx extension <validation_during_sphinx_build>`.

numpydoc/tests/test_validate.py

+141
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,113 @@ def missing_whitespace_after_comma(self):
11981198
"""
11991199

12001200

1201+
class ConstructorDocumentedInClassAndInit:
1202+
"""
1203+
Class to test constructor documented via class and constructor docstrings.
1204+
1205+
A case where both the class docstring and the constructor docstring are
1206+
defined.
1207+
1208+
Parameters
1209+
----------
1210+
param1 : int
1211+
Description of param1.
1212+
1213+
See Also
1214+
--------
1215+
otherclass : A class that does something else.
1216+
1217+
Examples
1218+
--------
1219+
This is an example of how to use ConstructorDocumentedInClassAndInit.
1220+
"""
1221+
1222+
def __init__(self, param1: int) -> None:
1223+
"""
1224+
Constructor docstring with additional information.
1225+
1226+
Extended information.
1227+
1228+
Parameters
1229+
----------
1230+
param1 : int
1231+
Description of param1 with extra details.
1232+
1233+
See Also
1234+
--------
1235+
otherclass : A class that does something else.
1236+
1237+
Examples
1238+
--------
1239+
This is an example of how to use ConstructorDocumentedInClassAndInit.
1240+
"""
1241+
1242+
1243+
class ConstructorDocumentedInClass:
1244+
"""
1245+
Class to test constructor documented via class docstring.
1246+
1247+
Useful to ensure that validation of `__init__` does not signal GL08,
1248+
when the class docstring properly documents the `__init__` constructor.
1249+
1250+
Parameters
1251+
----------
1252+
param1 : int
1253+
Description of param1.
1254+
1255+
See Also
1256+
--------
1257+
otherclass : A class that does something else.
1258+
1259+
Examples
1260+
--------
1261+
This is an example of how to use ConstructorDocumentedInClass.
1262+
"""
1263+
1264+
def __init__(self, param1: int) -> None:
1265+
pass
1266+
1267+
1268+
class ConstructorDocumentedInClassWithNoParameters:
1269+
"""
1270+
Class to test constructor documented via class docstring with no parameters.
1271+
1272+
Useful to ensure that validation of `__init__` does not signal GL08,
1273+
when the class docstring properly documents the `__init__` constructor.
1274+
1275+
See Also
1276+
--------
1277+
otherclass : A class that does something else.
1278+
1279+
Examples
1280+
--------
1281+
This is an example of how to use ConstructorDocumentedInClassWithNoParameters.
1282+
"""
1283+
1284+
def __init__(self) -> None:
1285+
pass
1286+
1287+
1288+
class IncompleteConstructorDocumentedInClass:
1289+
"""
1290+
Class to test an incomplete constructor docstring.
1291+
1292+
This class does not properly document parameters.
1293+
Unnecessary extended summary.
1294+
1295+
See Also
1296+
--------
1297+
otherclass : A class that does something else.
1298+
1299+
Examples
1300+
--------
1301+
This is an example of how to use IncompleteConstructorDocumentedInClass.
1302+
"""
1303+
1304+
def __init__(self, param1: int):
1305+
pass
1306+
1307+
12011308
class TestValidator:
12021309
def _import_path(self, klass=None, func=None):
12031310
"""
@@ -1536,6 +1643,40 @@ def test_bad_docstrings(self, capsys, klass, func, msgs):
15361643
for msg in msgs:
15371644
assert msg in " ".join(err[1] for err in result["errors"])
15381645

1646+
@pytest.mark.parametrize(
1647+
"klass,exp_init_codes,exc_init_codes,exp_klass_codes",
1648+
[
1649+
("ConstructorDocumentedInClass", tuple(), ("GL08",), tuple()),
1650+
("ConstructorDocumentedInClassAndInit", tuple(), ("GL08",), tuple()),
1651+
(
1652+
"ConstructorDocumentedInClassWithNoParameters",
1653+
tuple(),
1654+
("GL08",),
1655+
tuple(),
1656+
),
1657+
(
1658+
"IncompleteConstructorDocumentedInClass",
1659+
("GL08",),
1660+
tuple(),
1661+
("PR01"), # Parameter not documented in class constructor
1662+
),
1663+
],
1664+
)
1665+
def test_constructor_docstrings(
1666+
self, klass, exp_init_codes, exc_init_codes, exp_klass_codes
1667+
):
1668+
# First test the class docstring itself, checking expected_klass_codes match
1669+
result = validate_one(self._import_path(klass=klass))
1670+
for err in result["errors"]:
1671+
assert err[0] in exp_klass_codes
1672+
1673+
# Then test the constructor docstring
1674+
result = validate_one(self._import_path(klass=klass, func="__init__"))
1675+
for code in exp_init_codes:
1676+
assert code in " ".join(err[0] for err in result["errors"])
1677+
for code in exc_init_codes:
1678+
assert code not in " ".join(err[0] for err in result["errors"])
1679+
15391680

15401681
def decorator(x):
15411682
"""Test decorator."""

numpydoc/validate.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,29 @@ def validate(obj_name, validator_cls=None, **validator_kwargs):
637637

638638
errs = []
639639
if not doc.raw_doc:
640-
if "GL08" not in ignore_validation_comments:
640+
report_GL08: bool = True
641+
# Check if the object is a class and has a docstring in the constructor
642+
# Also check if code_obj is defined, as undefined for the AstValidator in validate_docstrings.py.
643+
if (
644+
doc.name.endswith(".__init__")
645+
and doc.is_function_or_method
646+
and hasattr(doc, "code_obj")
647+
):
648+
cls_name = doc.code_obj.__qualname__.split(".")[0]
649+
cls = Validator._load_obj(f"{doc.code_obj.__module__}.{cls_name}")
650+
# cls = Validator._load_obj(f"{doc.name[:-9]}.{cls_name}") ## Alternative
651+
cls_doc = Validator(get_doc_object(cls))
652+
653+
# Parameter_mismatches, PR01, PR02, PR03 are checked for the class docstring.
654+
# If cls_doc has PR01, PR02, PR03 errors, i.e. invalid class docstring,
655+
# then we also report missing constructor docstring, GL08.
656+
report_GL08 = len(cls_doc.parameter_mismatches) > 0
657+
658+
# Check if GL08 is to be ignored:
659+
if "GL08" in ignore_validation_comments:
660+
report_GL08 = False
661+
# Add GL08 error?
662+
if report_GL08:
641663
errs.append(error("GL08"))
642664
return {
643665
"type": doc.type,

0 commit comments

Comments
 (0)