Skip to content

Commit 87dd52f

Browse files
committed
Add feature to exclude patterns from docstring validation.
Modify updated config name to avoid sphinx warning. Add documentation for exclusion config value.
1 parent 1d359d8 commit 87dd52f

File tree

4 files changed

+88
-14
lines changed

4 files changed

+88
-14
lines changed

doc/install.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ numpydoc_validation_checks : set
121121

122122
The default is an empty set, thus no warnings from docstring validation
123123
are reported.
124+
numpydoc_validation_exclude : set
125+
A container of strings using :py:mod:`re` syntax specifying patterns to
126+
ignore for docstring validation.
127+
For example, to skip docstring validation for all objects in
128+
``mypkg.mymodule``::
129+
130+
numpydoc_validation_exclude = {"mypkg.mymodule."}
131+
132+
If you wanted to also skip getter methods of ``MyClass``::
133+
134+
numpydoc_validation_exclude = {"mypkg.mymodule.", "MyClass.get"}
135+
136+
The default is an empty set meaning no objects are excluded from docstring
137+
validation by default.
138+
Only has an effect when ``numpydoc_validate = True``.
124139
numpydoc_edit_link : bool
125140
.. deprecated:: 0.7.0
126141

numpydoc/numpydoc.py

Lines changed: 33 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_excluder
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,19 @@ 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+
config.numpydoc_validation_excluder = None
328+
if config.numpydoc_validation_exclude:
329+
exclude_expr = re.compile(
330+
r"|".join(exp for exp in config.numpydoc_validation_exclude)
331+
)
332+
config.numpydoc_validation_excluder = exclude_expr
333+
315334

316335
# ------------------------------------------------------------------------------
317336
# 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: 39 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():
@@ -140,6 +142,8 @@ def test_mangle_docstring_validation_warnings(
140142
# Set up config for test
141143
app.config.numpydoc_validate = numpydoc_validate
142144
app.config.numpydoc_validation_checks = numpydoc_validation_checks
145+
# Update configuration
146+
update_config(app)
143147
# Set up logging
144148
status, warning = StringIO(), StringIO()
145149
logging.setup(app, status, warning)
@@ -153,6 +157,33 @@ def test_mangle_docstring_validation_warnings(
153157
assert w not in warnings
154158

155159

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

163194

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

0 commit comments

Comments
 (0)