1414 $ ./validate_docstrings.py pandas.DataFrame.head
1515"""
1616import os
17+ import subprocess
1718import sys
1819import json
1920import re
2425import inspect
2526import importlib
2627import doctest
28+
2729try :
2830 from io import StringIO
2931except ImportError :
4042from numpydoc .docscrape import NumpyDocString
4143from pandas .io .formats .printing import pprint_thing
4244
43-
4445PRIVATE_CLASSES = ['NDFrame' , 'IndexOpsMixin' ]
4546DIRECTIVES = ['versionadded' , 'versionchanged' , 'deprecated' ]
4647
@@ -446,7 +447,7 @@ def validate_one(func_name):
446447 if doc .summary != doc .summary .lstrip ():
447448 errs .append ('Summary contains heading whitespaces.' )
448449 elif (doc .is_function_or_method
449- and doc .summary .split (' ' )[0 ][- 1 ] == 's' ):
450+ and doc .summary .split (' ' )[0 ][- 1 ] == 's' ):
450451 errs .append ('Summary must start with infinitive verb, '
451452 'not third person (e.g. use "Generate" instead of '
452453 '"Generates")' )
@@ -529,15 +530,16 @@ def validate_one(func_name):
529530 if examples_errs :
530531 errs .append ('Examples do not pass tests' )
531532
532- return {'type' : doc .type ,
533- 'docstring' : doc .clean_doc ,
534- 'deprecated' : doc .deprecated ,
535- 'file' : doc .source_file_name ,
536- 'file_line' : doc .source_file_def_line ,
537- 'github_link' : doc .github_url ,
538- 'errors' : errs ,
539- 'warnings' : wrns ,
540- 'examples_errors' : examples_errs }
533+ return ({'type' : doc .type ,
534+ 'docstring' : doc .clean_doc ,
535+ 'deprecated' : doc .deprecated ,
536+ 'file' : doc .source_file_name ,
537+ 'file_line' : doc .source_file_def_line ,
538+ 'github_link' : doc .github_url ,
539+ 'errors' : errs ,
540+ 'warnings' : wrns ,
541+ 'examples_errors' : examples_errs },
542+ doc )
541543
542544
543545def validate_all ():
@@ -598,10 +600,15 @@ def header(title, width=80, char='#'):
598600 full_line = full_line , title_line = title_line )
599601
600602 if func_name is None :
603+ flake8 = _call_flake8_plugin ()
604+ if flake8 :
605+ fd .write ('Flake8 reported issues:\n ' )
606+ fd .write (flake8 )
607+
601608 json_doc = validate_all ()
602609 fd .write (json .dumps (json_doc ))
603610 else :
604- doc_info = validate_one (func_name )
611+ doc_info , doc = validate_one (func_name )
605612
606613 fd .write (header ('Docstring ({})' .format (func_name )))
607614 fd .write ('{}\n ' .format (doc_info ['docstring' ]))
@@ -615,14 +622,41 @@ def header(title, width=80, char='#'):
615622 for wrn in doc_info ['warnings' ]:
616623 fd .write ('\t {}\n ' .format (wrn ))
617624
618- if not doc_info ['errors' ]:
625+ flake8 = _call_flake8_plugin (doc .source_file_name )
626+ if flake8 :
627+ fd .write ('Flake8 reported issues:\n ' )
628+ fd .write (flake8 )
629+
630+ if not doc_info ['errors' ] and not flake8 :
619631 fd .write ('Docstring for "{}" correct. :)\n ' .format (func_name ))
620632
621633 if doc_info ['examples_errors' ]:
622634 fd .write (header ('Doctests' ))
623635 fd .write (doc_info ['examples_errors' ])
624636
625637
638+ def _call_flake8_plugin (* source_file_name ):
639+ sub_cmds = (['flake8' , '--doctest' , * source_file_name ],
640+ ['flake8-rst' , * source_file_name ])
641+
642+ check_output = [_check_output_safe (sub_cmd ) for sub_cmd in sub_cmds ]
643+ if check_output :
644+ return '\n \n ' .join ([output for output in check_output if output ])
645+ return None
646+
647+
648+ def _check_output_safe (sub_cmd ):
649+ try :
650+ output = subprocess .check_output (sub_cmd )
651+ except subprocess .CalledProcessError as e :
652+ output = e .output
653+ if output :
654+ return '\n ' .join (['Invoking command "{}" returned:'
655+ .format (' ' .join (sub_cmd )), output .decode ("utf-8" )])
656+ else :
657+ return None
658+
659+
626660if __name__ == '__main__' :
627661 func_help = ('function or method to validate (e.g. pandas.DataFrame.head) '
628662 'if not provided, all docstrings are validated and returned '
0 commit comments