Skip to content

Commit 6d5eb42

Browse files
mattgebertstefanvlarsoner
authored
MAINT: Changed class constructor __init__ GL08 reporting (#592)
Co-authored-by: Stefan van der Walt <sjvdwalt@gmail.com> Co-authored-by: Eric Larson <larson.eric.d@gmail.com>
1 parent 06cd4a7 commit 6d5eb42

File tree

4 files changed

+170
-2
lines changed

4 files changed

+170
-2
lines changed

doc/format.rst

Lines changed: 3 additions & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 141 additions & 0 deletions
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

Lines changed: 23 additions & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy