Skip to content

Commit 7d406ac

Browse files
committed
Add feature to exclude patterns from docstring validation.
1 parent 1d359d8 commit 7d406ac

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

numpydoc/numpydoc.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,26 @@ def mangle_docstrings(app, what, name, obj, options, lines):
174174
logger.error('[numpydoc] While processing docstring for %r', name)
175175
raise
176176

177-
# TODO: validation only applies to non-module docstrings?
178177
if app.config.numpydoc_validate:
179-
# TODO: Currently, all validation checks are run and only those
180-
# selected via config are reported. It would be more efficient to
181-
# only run the selected checks.
182-
errors = validate(doc)["errors"]
183-
if {err[0] for err in errors} & app.config.numpydoc_validation_checks:
184-
msg = (
185-
f"[numpydoc] Validation warnings while processing "
186-
f"docstring for {name!r}:\n"
187-
)
188-
for err in errors:
189-
if err[0] in app.config.numpydoc_validation_checks:
190-
msg += f" {err[0]}: {err[1]}\n"
191-
logger.warning(msg)
178+
# If the user has supplied patterns to ignore via the
179+
# numpydoc_validation_exclude config option, skip validation for
180+
# any objs whose name matches any of the patterns
181+
excluder = app.config.numpydoc_validation_exclude
182+
exclude_from_validation = excluder.search(name) if excluder else False
183+
if not exclude_from_validation:
184+
# TODO: Currently, all validation checks are run and only those
185+
# selected via config are reported. It would be more efficient to
186+
# only run the selected checks.
187+
errors = validate(doc)["errors"]
188+
if {err[0] for err in errors} & app.config.numpydoc_validation_checks:
189+
msg = (
190+
f"[numpydoc] Validation warnings while processing "
191+
f"docstring for {name!r}:\n"
192+
)
193+
for err in errors:
194+
if err[0] in app.config.numpydoc_validation_checks:
195+
msg += f" {err[0]}: {err[1]}\n"
196+
logger.warning(msg)
192197

193198

194199
if (app.config.numpydoc_edit_link and hasattr(obj, '__name__') and
@@ -274,6 +279,7 @@ def setup(app, get_doc_object_=get_doc_object):
274279
app.add_config_value('numpydoc_xref_ignore', set(), True)
275280
app.add_config_value('numpydoc_validate', False, True)
276281
app.add_config_value('numpydoc_validation_checks', set(), True)
282+
app.add_config_value('numpydoc_validation_exclude', set(), False)
277283

278284
# Extra mangling domains
279285
app.add_domain(NumpyPythonDomain)
@@ -312,6 +318,18 @@ def update_config(app, config=None):
312318
f"config value: {invalid_error_codes}"
313319
)
314320

321+
# Generate the regexp for docstrings to ignore during validation
322+
if isinstance(config.numpydoc_validation_exclude, str):
323+
raise ValueError(
324+
f"numpydoc_validation_exclude must be a container of strings, "
325+
f"e.g. [{config.numpydoc_validation_exclude!r}]."
326+
)
327+
if config.numpydoc_validation_exclude:
328+
exclude_expr = re.compile(
329+
r"|".join(exp for exp in config.numpydoc_validation_exclude)
330+
)
331+
config.numpydoc_validation_exclude = exclude_expr
332+
315333

316334
# ------------------------------------------------------------------------------
317335
# Docstring-mangling domains

numpydoc/tests/test_docscrape.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,7 @@ def __init__(self, a, b):
14991499
self.numpydoc_xref_aliases_complete = b
15001500
# numpydoc.update_config fails if this config option not present
15011501
self.numpydoc_validation_checks = set()
1502+
self.numpydoc_validation_exclude = set()
15021503

15031504
xref_aliases_complete = deepcopy(DEFAULT_LINKS)
15041505
for key in xref_aliases:

numpydoc/tests/test_numpydoc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class MockConfig():
2525
numpydoc_citation_re = '[a-z0-9_.-]+'
2626
numpydoc_attributes_as_param_list = True
2727
numpydoc_validate = False
28+
numpydoc_validation_checks = set()
29+
numpydoc_validation_exclude = set()
2830

2931

3032
class MockBuilder():
@@ -153,6 +155,33 @@ def test_mangle_docstring_validation_warnings(
153155
assert w not in warnings
154156

155157

158+
def test_mangle_docstring_validation_exclude():
159+
def function_with_bad_docstring():
160+
"""
161+
This docstring will raise docstring validation warnings."""
162+
app = MockApp()
163+
app.config.numpydoc_validate = True
164+
app.config.numpydoc_validation_checks = {"all"}
165+
app.config.numpydoc_validation_exclude = [r"_bad_"]
166+
# Call update_config to construct regexp from config value
167+
update_config(app)
168+
# Setup for catching warnings
169+
status, warning = StringIO(), StringIO()
170+
logging.setup(app, status, warning)
171+
# Run mangle docstrings on function_with_bad_docstring
172+
mangle_docstrings(
173+
app,
174+
'function',
175+
function_with_bad_docstring.__name__,
176+
function_with_bad_docstring,
177+
None,
178+
function_with_bad_docstring.__doc__.split('\n'),
179+
)
180+
# Validation is skipped due to exclude pattern matching fn name, therefore
181+
# no warnings expected
182+
assert warning.getvalue() == ""
183+
184+
156185
def test_update_config_invalid_validation_set():
157186
app = MockApp()
158187
# Results in {'a', 'l'} instead of {"all"}
@@ -161,6 +190,14 @@ def test_update_config_invalid_validation_set():
161190
update_config(app)
162191

163192

193+
def test_update_config_exclude_str():
194+
app = MockApp()
195+
app.config.numpydoc_validation_checks = set()
196+
app.config.numpydoc_validation_exclude = "shouldnt-be-a-str"
197+
with pytest.raises(ValueError, match="\['shouldnt-be-a-str'\]"):
198+
update_config(app)
199+
200+
164201
if __name__ == "__main__":
165202
import pytest
166203
pytest.main()

0 commit comments

Comments
 (0)