Skip to content

Commit f47a8b8

Browse files
authored
DOC: Add ignore_functions option to validate_docstrings.py (pandas-dev#50509)
* DOC: Add ignore_functions option to validate_docstrings.py * fixup! DOC: Add ignore_functions option to validate_docstrings.py * fixup! fixup! DOC: Add ignore_functions option to validate_docstrings.py * fixup! fixup! fixup! DOC: Add ignore_functions option to validate_docstrings.py * Make get_all_api_items a generator and improve ignore_functions test
1 parent 059b2c1 commit f47a8b8

File tree

3 files changed

+97
-15
lines changed

3 files changed

+97
-15
lines changed

ci/code_checks.sh

+30
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,36 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
8383
$BASE_DIR/scripts/validate_docstrings.py --format=actions --errors=EX04,GL01,GL02,GL03,GL04,GL05,GL06,GL07,GL09,GL10,PR03,PR04,PR05,PR06,PR08,PR09,PR10,RT01,RT04,RT05,SA02,SA03,SA04,SS01,SS02,SS03,SS04,SS05,SS06
8484
RET=$(($RET + $?)) ; echo $MSG "DONE"
8585

86+
MSG='Partially validate docstrings (RT02)' ; echo $MSG
87+
$BASE_DIR/scripts/validate_docstrings.py --format=actions --errors=RT02 --ignore_functions \
88+
pandas.Series.align \
89+
pandas.Series.dt.total_seconds \
90+
pandas.Series.cat.rename_categories \
91+
pandas.Series.cat.reorder_categories \
92+
pandas.Series.cat.add_categories \
93+
pandas.Series.cat.remove_categories \
94+
pandas.Series.cat.remove_unused_categories \
95+
pandas.Index.all \
96+
pandas.Index.any \
97+
pandas.CategoricalIndex.rename_categories \
98+
pandas.CategoricalIndex.reorder_categories \
99+
pandas.CategoricalIndex.add_categories \
100+
pandas.CategoricalIndex.remove_categories \
101+
pandas.CategoricalIndex.remove_unused_categories \
102+
pandas.MultiIndex.drop \
103+
pandas.DatetimeIndex.to_pydatetime \
104+
pandas.TimedeltaIndex.to_pytimedelta \
105+
pandas.core.groupby.SeriesGroupBy.apply \
106+
pandas.core.groupby.DataFrameGroupBy.apply \
107+
pandas.io.formats.style.Styler.export \
108+
pandas.api.extensions.ExtensionArray.astype \
109+
pandas.api.extensions.ExtensionArray.dropna \
110+
pandas.api.extensions.ExtensionArray.isna \
111+
pandas.api.extensions.ExtensionArray.repeat \
112+
pandas.api.extensions.ExtensionArray.unique \
113+
pandas.DataFrame.align
114+
RET=$(($RET + $?)) ; echo $MSG "DONE"
115+
86116
fi
87117

88118
### DOCUMENTATION NOTEBOOKS ###

scripts/tests/test_validate_docstrings.py

+36-4
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,32 @@ def test_leftover_files_raises(self):
199199
self._import_path(klass="BadDocstrings", func="leftover_files")
200200
)
201201

202+
def test_validate_all_ignore_functions(self, monkeypatch):
203+
monkeypatch.setattr(
204+
validate_docstrings,
205+
"get_all_api_items",
206+
lambda: [
207+
(
208+
"pandas.DataFrame.align",
209+
"func",
210+
"current_section",
211+
"current_subsection",
212+
),
213+
(
214+
"pandas.Index.all",
215+
"func",
216+
"current_section",
217+
"current_subsection",
218+
),
219+
],
220+
)
221+
result = validate_docstrings.validate_all(
222+
prefix=None,
223+
ignore_functions=["pandas.DataFrame.align"],
224+
)
225+
assert len(result) == 1
226+
assert "pandas.Index.all" in result
227+
202228
def test_validate_all_ignore_deprecated(self, monkeypatch):
203229
monkeypatch.setattr(
204230
validate_docstrings,
@@ -339,14 +365,15 @@ def test_exit_status_for_main(self, monkeypatch):
339365
errors=[],
340366
output_format="default",
341367
ignore_deprecated=False,
368+
ignore_functions=None,
342369
)
343370
assert exit_status == 0
344371

345372
def test_exit_status_errors_for_validate_all(self, monkeypatch):
346373
monkeypatch.setattr(
347374
validate_docstrings,
348375
"validate_all",
349-
lambda prefix, ignore_deprecated=False: {
376+
lambda prefix, ignore_deprecated=False, ignore_functions=None: {
350377
"docstring1": {
351378
"errors": [
352379
("ER01", "err desc"),
@@ -369,14 +396,15 @@ def test_exit_status_errors_for_validate_all(self, monkeypatch):
369396
errors=[],
370397
output_format="default",
371398
ignore_deprecated=False,
399+
ignore_functions=None,
372400
)
373401
assert exit_status == 5
374402

375403
def test_no_exit_status_noerrors_for_validate_all(self, monkeypatch):
376404
monkeypatch.setattr(
377405
validate_docstrings,
378406
"validate_all",
379-
lambda prefix, ignore_deprecated=False: {
407+
lambda prefix, ignore_deprecated=False, ignore_functions=None: {
380408
"docstring1": {"errors": [], "warnings": [("WN01", "warn desc")]},
381409
"docstring2": {"errors": []},
382410
},
@@ -387,6 +415,7 @@ def test_no_exit_status_noerrors_for_validate_all(self, monkeypatch):
387415
errors=[],
388416
output_format="default",
389417
ignore_deprecated=False,
418+
ignore_functions=None,
390419
)
391420
assert exit_status == 0
392421

@@ -395,7 +424,7 @@ def test_exit_status_for_validate_all_json(self, monkeypatch):
395424
monkeypatch.setattr(
396425
validate_docstrings,
397426
"validate_all",
398-
lambda prefix, ignore_deprecated=False: {
427+
lambda prefix, ignore_deprecated=False, ignore_functions=None: {
399428
"docstring1": {
400429
"errors": [
401430
("ER01", "err desc"),
@@ -412,14 +441,15 @@ def test_exit_status_for_validate_all_json(self, monkeypatch):
412441
errors=[],
413442
output_format="json",
414443
ignore_deprecated=False,
444+
ignore_functions=None,
415445
)
416446
assert exit_status == 0
417447

418448
def test_errors_param_filters_errors(self, monkeypatch):
419449
monkeypatch.setattr(
420450
validate_docstrings,
421451
"validate_all",
422-
lambda prefix, ignore_deprecated=False: {
452+
lambda prefix, ignore_deprecated=False, ignore_functions=None: {
423453
"Series.foo": {
424454
"errors": [
425455
("ER01", "err desc"),
@@ -447,6 +477,7 @@ def test_errors_param_filters_errors(self, monkeypatch):
447477
errors=["ER01"],
448478
output_format="default",
449479
ignore_deprecated=False,
480+
ignore_functions=None,
450481
)
451482
assert exit_status == 3
452483

@@ -456,5 +487,6 @@ def test_errors_param_filters_errors(self, monkeypatch):
456487
errors=["ER03"],
457488
output_format="default",
458489
ignore_deprecated=False,
490+
ignore_functions=None,
459491
)
460492
assert exit_status == 1

scripts/validate_docstrings.py

+31-11
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def pandas_validate(func_name: str):
295295
return result
296296

297297

298-
def validate_all(prefix, ignore_deprecated=False):
298+
def validate_all(prefix, ignore_deprecated=False, ignore_functions=None):
299299
"""
300300
Execute the validation of all docstrings, and return a dict with the
301301
results.
@@ -307,6 +307,8 @@ def validate_all(prefix, ignore_deprecated=False):
307307
validated. If None, all docstrings will be validated.
308308
ignore_deprecated: bool, default False
309309
If True, deprecated objects are ignored when validating docstrings.
310+
ignore_functions: list of str or None, default None
311+
If not None, contains a list of function to ignore
310312
311313
Returns
312314
-------
@@ -317,14 +319,11 @@ def validate_all(prefix, ignore_deprecated=False):
317319
result = {}
318320
seen = {}
319321

320-
base_path = pathlib.Path(__file__).parent.parent
321-
api_doc_fnames = pathlib.Path(base_path, "doc", "source", "reference")
322-
api_items = []
323-
for api_doc_fname in api_doc_fnames.glob("*.rst"):
324-
with open(api_doc_fname) as f:
325-
api_items += list(get_api_items(f))
322+
ignore_functions = set(ignore_functions or [])
326323

327-
for func_name, _, section, subsection in api_items:
324+
for func_name, _, section, subsection in get_all_api_items():
325+
if func_name in ignore_functions:
326+
continue
328327
if prefix and not func_name.startswith(prefix):
329328
continue
330329
doc_info = pandas_validate(func_name)
@@ -348,16 +347,25 @@ def validate_all(prefix, ignore_deprecated=False):
348347
return result
349348

350349

350+
def get_all_api_items():
351+
base_path = pathlib.Path(__file__).parent.parent
352+
api_doc_fnames = pathlib.Path(base_path, "doc", "source", "reference")
353+
for api_doc_fname in api_doc_fnames.glob("*.rst"):
354+
with open(api_doc_fname) as f:
355+
yield from get_api_items(f)
356+
357+
351358
def print_validate_all_results(
352359
prefix: str,
353360
errors: list[str] | None,
354361
output_format: str,
355362
ignore_deprecated: bool,
363+
ignore_functions: list[str] | None,
356364
):
357365
if output_format not in ("default", "json", "actions"):
358366
raise ValueError(f'Unknown output_format "{output_format}"')
359367

360-
result = validate_all(prefix, ignore_deprecated)
368+
result = validate_all(prefix, ignore_deprecated, ignore_functions)
361369

362370
if output_format == "json":
363371
sys.stdout.write(json.dumps(result))
@@ -408,13 +416,17 @@ def header(title, width=80, char="#"):
408416
sys.stderr.write(result["examples_errs"])
409417

410418

411-
def main(func_name, prefix, errors, output_format, ignore_deprecated):
419+
def main(func_name, prefix, errors, output_format, ignore_deprecated, ignore_functions):
412420
"""
413421
Main entry point. Call the validation for one or for all docstrings.
414422
"""
415423
if func_name is None:
416424
return print_validate_all_results(
417-
prefix, errors, output_format, ignore_deprecated
425+
prefix,
426+
errors,
427+
output_format,
428+
ignore_deprecated,
429+
ignore_functions,
418430
)
419431
else:
420432
print_validate_one_results(func_name)
@@ -464,6 +476,13 @@ def main(func_name, prefix, errors, output_format, ignore_deprecated):
464476
"deprecated objects are ignored when validating "
465477
"all docstrings",
466478
)
479+
argparser.add_argument(
480+
"--ignore_functions",
481+
nargs="*",
482+
help="function or method to not validate "
483+
"(e.g. pandas.DataFrame.head). "
484+
"Inverse of the `function` argument.",
485+
)
467486

468487
args = argparser.parse_args()
469488
sys.exit(
@@ -473,5 +492,6 @@ def main(func_name, prefix, errors, output_format, ignore_deprecated):
473492
args.errors.split(",") if args.errors else None,
474493
args.format,
475494
args.ignore_deprecated,
495+
args.ignore_functions,
476496
)
477497
)

0 commit comments

Comments
 (0)