Skip to content

Commit b2cd0fa

Browse files
committed
For issue pandas-dev#23135 Validate the description of the See Also
1 parent 913f71f commit b2cd0fa

File tree

2 files changed

+383
-250
lines changed

2 files changed

+383
-250
lines changed

scripts/tests/test_validate_docstrings.py

+172-80
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66

77
import validate_docstrings
8+
89
validate_one = validate_docstrings.validate_one
910

1011
from pandas.util.testing import capture_stderr
@@ -18,7 +19,7 @@ class GoodDocStrings(object):
1819
script without any errors.
1920
"""
2021

21-
def plot(self, kind, color='blue', **kwargs):
22+
def plot(self, kind, color="blue", **kwargs):
2223
"""
2324
Generate a plot.
2425
@@ -218,6 +219,9 @@ def mode(self, axis, numeric_only):
218219
"""
219220
pass
220221

222+
def SeeAlsoFormatting(self):
223+
pass
224+
221225

222226
class BadGenericDocStrings(object):
223227
"""Everything here has a bad docstring
@@ -335,7 +339,6 @@ def method(self, foo=None, bar=None):
335339

336340

337341
class BadSummaries(object):
338-
339342
def wrong_line(self):
340343
"""Exists on the wrong line"""
341344
pass
@@ -457,7 +460,6 @@ def blank_lines(self, kind):
457460

458461

459462
class BadReturns(object):
460-
461463
def return_not_documented(self):
462464
"""
463465
Lacks section for Returns
@@ -502,8 +504,31 @@ def no_punctuation(self):
502504
return "Hello world!"
503505

504506

505-
class TestValidator(object):
507+
class BadSeeAlso(object):
508+
"""
509+
Everything here has a problem with its See Also section.
510+
"""
511+
512+
def no_period_see_also(self):
513+
"""
514+
Provides type and description but no period.
515+
516+
See Also
517+
-------
518+
str : Lorem ipsum
519+
"""
520+
521+
def no_capital_first_see_also(self):
522+
"""
523+
Provides type and description first letter is not capitalized.
524+
525+
See Also
526+
-------
527+
str : lorem ipsum.
528+
"""
506529

530+
531+
class TestValidator(object):
507532
def _import_path(self, klass=None, func=None):
508533
"""
509534
Build the required import path for tests in this module.
@@ -532,86 +557,144 @@ def _import_path(self, klass=None, func=None):
532557

533558
@capture_stderr
534559
def test_good_class(self):
535-
errors = validate_one(self._import_path(
536-
klass='GoodDocStrings'))['errors']
560+
errors = validate_one(self._import_path(klass="GoodDocStrings"))["errors"]
537561
assert isinstance(errors, list)
538562
assert not errors
539563

540564
@capture_stderr
541-
@pytest.mark.parametrize("func", [
542-
'plot', 'sample', 'random_letters', 'sample_values', 'head', 'head1',
543-
'contains', 'mode'])
565+
@pytest.mark.parametrize(
566+
"func",
567+
[
568+
"plot",
569+
"sample",
570+
"random_letters",
571+
"sample_values",
572+
"head",
573+
"head1",
574+
"contains",
575+
"mode",
576+
],
577+
)
544578
def test_good_functions(self, func):
545-
errors = validate_one(self._import_path(
546-
klass='GoodDocStrings', func=func))['errors']
579+
errors = validate_one(self._import_path(klass="GoodDocStrings", func=func))[
580+
"errors"
581+
]
547582
assert isinstance(errors, list)
548583
assert not errors
549584

550585
@capture_stderr
551586
def test_bad_class(self):
552-
errors = validate_one(self._import_path(
553-
klass='BadGenericDocStrings'))['errors']
587+
errors = validate_one(self._import_path(klass="BadGenericDocStrings"))["errors"]
554588
assert isinstance(errors, list)
555589
assert errors
556590

557591
@capture_stderr
558-
@pytest.mark.parametrize("func", [
559-
'func', 'astype', 'astype1', 'astype2', 'astype3', 'plot', 'method'])
592+
@pytest.mark.parametrize(
593+
"func", ["func", "astype", "astype1", "astype2", "astype3", "plot", "method"]
594+
)
560595
def test_bad_generic_functions(self, func):
561-
errors = validate_one(self._import_path( # noqa:F821
562-
klass='BadGenericDocStrings', func=func))['errors']
596+
errors = validate_one(
597+
self._import_path(klass="BadGenericDocStrings", func=func) # noqa:F821
598+
)["errors"]
563599
assert isinstance(errors, list)
564600
assert errors
565601

566-
@pytest.mark.parametrize("klass,func,msgs", [
567-
# Summary tests
568-
('BadSummaries', 'wrong_line',
569-
('should start in the line immediately after the opening quotes',)),
570-
('BadSummaries', 'no_punctuation',
571-
('Summary does not end with a period',)),
572-
('BadSummaries', 'no_capitalization',
573-
('Summary does not start with a capital letter',)),
574-
('BadSummaries', 'no_capitalization',
575-
('Summary must start with infinitive verb',)),
576-
('BadSummaries', 'multi_line',
577-
('Summary should fit in a single line.',)),
578-
('BadSummaries', 'two_paragraph_multi_line',
579-
('Summary should fit in a single line.',)),
580-
# Parameters tests
581-
('BadParameters', 'missing_params',
582-
('Parameters {**kwargs} not documented',)),
583-
('BadParameters', 'bad_colon_spacing',
584-
('Parameters {kind} not documented',
585-
'Unknown parameters {kind: str}',
586-
'Parameter "kind: str" has no type')),
587-
('BadParameters', 'no_description_period',
588-
('Parameter "kind" description should finish with "."',)),
589-
('BadParameters', 'no_description_period_with_directive',
590-
('Parameter "kind" description should finish with "."',)),
591-
('BadParameters', 'parameter_capitalization',
592-
('Parameter "kind" description should start with a capital letter',)),
593-
pytest.param('BadParameters', 'blank_lines', ('No error yet?',),
594-
marks=pytest.mark.xfail),
595-
# Returns tests
596-
('BadReturns', 'return_not_documented', ('No Returns section found',)),
597-
('BadReturns', 'yield_not_documented', ('No Yields section found',)),
598-
pytest.param('BadReturns', 'no_type', ('foo',),
599-
marks=pytest.mark.xfail),
600-
pytest.param('BadReturns', 'no_description', ('foo',),
601-
marks=pytest.mark.xfail),
602-
pytest.param('BadReturns', 'no_punctuation', ('foo',),
603-
marks=pytest.mark.xfail)
604-
])
602+
@pytest.mark.parametrize(
603+
"klass,func,msgs",
604+
[
605+
# Summary tests
606+
(
607+
"BadSummaries",
608+
"wrong_line",
609+
("should start in the line immediately after the opening quotes",),
610+
),
611+
("BadSummaries", "no_punctuation", ("Summary does not end with a period",)),
612+
(
613+
"BadSummaries",
614+
"no_capitalization",
615+
("Summary does not start with a capital letter",),
616+
),
617+
(
618+
"BadSummaries",
619+
"no_capitalization",
620+
("Summary must start with infinitive verb",),
621+
),
622+
("BadSummaries", "multi_line", ("Summary should fit in a single line.",)),
623+
(
624+
"BadSummaries",
625+
"two_paragraph_multi_line",
626+
("Summary should fit in a single line.",),
627+
),
628+
# Parameters tests
629+
(
630+
"BadParameters",
631+
"missing_params",
632+
("Parameters {**kwargs} not documented",),
633+
),
634+
(
635+
"BadParameters",
636+
"bad_colon_spacing",
637+
(
638+
"Parameters {kind} not documented",
639+
"Unknown parameters {kind: str}",
640+
'Parameter "kind: str" has no type',
641+
),
642+
),
643+
(
644+
"BadParameters",
645+
"no_description_period",
646+
('Parameter "kind" description should finish with "."',),
647+
),
648+
(
649+
"BadParameters",
650+
"no_description_period_with_directive",
651+
('Parameter "kind" description should finish with "."',),
652+
),
653+
(
654+
"BadParameters",
655+
"parameter_capitalization",
656+
('Parameter "kind" description should start with a capital letter',),
657+
),
658+
pytest.param(
659+
"BadParameters",
660+
"blank_lines",
661+
("No error yet?",),
662+
marks=pytest.mark.xfail,
663+
),
664+
# Returns tests
665+
("BadReturns", "return_not_documented", ("No Returns section found",)),
666+
("BadReturns", "yield_not_documented", ("No Yields section found",)),
667+
pytest.param("BadReturns", "no_type", ("foo",), marks=pytest.mark.xfail),
668+
pytest.param(
669+
"BadReturns", "no_description", ("foo",), marks=pytest.mark.xfail
670+
),
671+
pytest.param(
672+
"BadReturns", "no_punctuation", ("foo",), marks=pytest.mark.xfail
673+
),
674+
# See Also tests
675+
(
676+
"BadSeeAlso",
677+
"no_period_see_also",
678+
("No period at the end of the See Also.",),
679+
),
680+
(
681+
"BadSeeAlso",
682+
"no_capital_first_see_also",
683+
("First letter of the See Also is not capitalized.",),
684+
),
685+
],
686+
)
605687
def test_bad_examples(self, capsys, klass, func, msgs):
606688
result = validate_one(self._import_path(klass=klass, func=func)) # noqa:F821
607689
for msg in msgs:
608-
assert msg in ' '.join(result['errors'])
690+
assert msg in " ".join(result["errors"])
609691

610692

611693
class ApiItems(object):
612694
@property
613695
def api_doc(self):
614-
return io.StringIO('''
696+
return io.StringIO(
697+
"""
615698
.. currentmodule:: itertools
616699
617700
Itertools
@@ -644,41 +727,50 @@ def api_doc(self):
644727
645728
seed
646729
randint
647-
''')
648-
649-
@pytest.mark.parametrize('idx,name', [(0, 'itertools.cycle'),
650-
(1, 'itertools.count'),
651-
(2, 'itertools.chain'),
652-
(3, 'random.seed'),
653-
(4, 'random.randint')])
730+
"""
731+
)
732+
733+
@pytest.mark.parametrize(
734+
"idx,name",
735+
[
736+
(0, "itertools.cycle"),
737+
(1, "itertools.count"),
738+
(2, "itertools.chain"),
739+
(3, "random.seed"),
740+
(4, "random.randint"),
741+
],
742+
)
654743
def test_item_name(self, idx, name):
655744
result = list(validate_docstrings.get_api_items(self.api_doc))
656745
assert result[idx][0] == name
657746

658-
@pytest.mark.parametrize('idx,func', [(0, 'cycle'),
659-
(1, 'count'),
660-
(2, 'chain'),
661-
(3, 'seed'),
662-
(4, 'randint')])
747+
@pytest.mark.parametrize(
748+
"idx,func",
749+
[(0, "cycle"), (1, "count"), (2, "chain"), (3, "seed"), (4, "randint")],
750+
)
663751
def test_item_function(self, idx, func):
664752
result = list(validate_docstrings.get_api_items(self.api_doc))
665753
assert callable(result[idx][1])
666754
assert result[idx][1].__name__ == func
667755

668-
@pytest.mark.parametrize('idx,section', [(0, 'Itertools'),
669-
(1, 'Itertools'),
670-
(2, 'Itertools'),
671-
(3, 'Random'),
672-
(4, 'Random')])
756+
@pytest.mark.parametrize(
757+
"idx,section",
758+
[
759+
(0, "Itertools"),
760+
(1, "Itertools"),
761+
(2, "Itertools"),
762+
(3, "Random"),
763+
(4, "Random"),
764+
],
765+
)
673766
def test_item_section(self, idx, section):
674767
result = list(validate_docstrings.get_api_items(self.api_doc))
675768
assert result[idx][2] == section
676769

677-
@pytest.mark.parametrize('idx,subsection', [(0, 'Infinite'),
678-
(1, 'Infinite'),
679-
(2, 'Finite'),
680-
(3, 'All'),
681-
(4, 'All')])
770+
@pytest.mark.parametrize(
771+
"idx,subsection",
772+
[(0, "Infinite"), (1, "Infinite"), (2, "Finite"), (3, "All"), (4, "All")],
773+
)
682774
def test_item_subsection(self, idx, subsection):
683775
result = list(validate_docstrings.get_api_items(self.api_doc))
684776
assert result[idx][3] == subsection

0 commit comments

Comments
 (0)