Skip to content

Commit bd64a7d

Browse files
committed
MAINT: Changed class constructor __init__ GL08 reporting to only if class docstring isn't complete
1 parent a233ebf commit bd64a7d

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

numpydoc/tests/test_validate.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,90 @@ def missing_whitespace_after_comma(self):
11981198
"""
11991199

12001200

1201+
class GoodConstructorInclusion:
1202+
"""
1203+
Class to test optional constructor docstring inclusion.
1204+
1205+
As the class docstring can define the constructor, a check should raise GL08 if a constructor docstring is defined.
1206+
1207+
Parameters
1208+
----------
1209+
param1 : int
1210+
Description of param1.
1211+
1212+
See Also
1213+
--------
1214+
otherclass : A class that does something else.
1215+
1216+
Examples
1217+
--------
1218+
This is an example of how to use GoodConstructorInclusion.
1219+
"""
1220+
1221+
def __init__(self, param1: int) -> None:
1222+
"""
1223+
Constructor docstring with additional information.
1224+
1225+
Extended information.
1226+
1227+
Parameters
1228+
----------
1229+
param1 : int
1230+
Description of param1 with extra details.
1231+
1232+
See Also
1233+
--------
1234+
otherclass : A class that does something else.
1235+
1236+
Examples
1237+
--------
1238+
This is an example of how to use GoodConstructorInclusion.
1239+
"""
1240+
1241+
1242+
class GoodConstructorExclusion:
1243+
"""
1244+
Class to test optional constructor docstring exclusion.
1245+
1246+
As the class docstring can define the constructor, a check should not raise GL08 if no constructor docstring is defined.
1247+
1248+
Parameters
1249+
----------
1250+
param1 : int
1251+
Description of param1.
1252+
1253+
See Also
1254+
--------
1255+
otherclass : A class that does something else.
1256+
1257+
Examples
1258+
--------
1259+
This is an example of how to use GoodConstructorExclusion.
1260+
"""
1261+
1262+
def __init__(self, param1: int) -> None:
1263+
pass
1264+
1265+
1266+
class BadConstructorExclusion:
1267+
"""
1268+
Class to test undocumented constructor docstring.
1269+
1270+
Unnecessary extended summary.
1271+
1272+
See Also
1273+
--------
1274+
otherclass : A class that does something else.
1275+
1276+
Examples
1277+
--------
1278+
This is an example of how to use BadConstructorExclusion.
1279+
"""
1280+
1281+
def __init__(self, param1: int):
1282+
pass
1283+
1284+
12011285
class TestValidator:
12021286
def _import_path(self, klass=None, func=None):
12031287
"""
@@ -1536,6 +1620,34 @@ def test_bad_docstrings(self, capsys, klass, func, msgs):
15361620
for msg in msgs:
15371621
assert msg in " ".join(err[1] for err in result["errors"])
15381622

1623+
@pytest.mark.parametrize(
1624+
"klass,exp_init_codes,exc_init_codes,exp_klass_codes",
1625+
[
1626+
("GoodConstructorExclusion", tuple(), ("GL08",), tuple()),
1627+
("GoodConstructorInclusion", tuple(), ("GL08",), tuple()),
1628+
(
1629+
"BadConstructorExclusion",
1630+
("GL08",),
1631+
tuple(),
1632+
("PR01"), # Parameter not documented in class constructor
1633+
),
1634+
],
1635+
)
1636+
def test_constructor_docstrings(
1637+
self, klass, exp_init_codes, exc_init_codes, exp_klass_codes
1638+
):
1639+
# First test the class docstring itself, checking expected_klass_codes match
1640+
result = validate_one(self._import_path(klass=klass))
1641+
for err in result["errors"]:
1642+
assert err[0] in exp_klass_codes
1643+
1644+
# Then test the constructor docstring
1645+
result = validate_one(self._import_path(klass=klass, func="__init__"))
1646+
for code in exp_init_codes:
1647+
assert code in " ".join(err[0] for err in result["errors"])
1648+
for code in exc_init_codes:
1649+
assert code not in " ".join(err[0] for err in result["errors"])
1650+
15391651

15401652
def decorator(x):
15411653
"""Test decorator."""

numpydoc/validate.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,23 @@ def validate(obj_name, validator_cls=None, **validator_kwargs):
633633

634634
errs = []
635635
if not doc.raw_doc:
636-
if "GL08" not in ignore_validation_comments:
636+
report_GL08: bool = True
637+
# Check if the object is a class and has a docstring in the constructor
638+
if doc.name.endswith("__init__") and doc.is_function_or_method:
639+
cls_name = doc.code_obj.__qualname__.split(".")[0]
640+
cls = getattr(importlib.import_module(doc.code_obj.__module__), cls_name)
641+
cls_doc = Validator(get_doc_object(cls))
642+
643+
# Parameter_mismatches, PR01, PR02, PR03 are checked for the class docstring.
644+
# If cls_doc has PR01, PR02, PR03 errors, i.e. invalid class docstring,
645+
# then we also report missing constructor docstring, GL08.
646+
report_GL08 = len(cls_doc.parameter_mismatches) > 0
647+
648+
# Check if GL08 is to be ignored:
649+
if "GL08" in ignore_validation_comments:
650+
report_GL08 = False
651+
# Add GL08 error?
652+
if report_GL08:
637653
errs.append(error("GL08"))
638654
return {
639655
"type": doc.type,

0 commit comments

Comments
 (0)