diff --git a/doc/index.rst b/doc/index.rst index 39d8e5bd..b6123953 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -22,3 +22,4 @@ Documentation install format example + validation diff --git a/doc/validation.rst b/doc/validation.rst new file mode 100644 index 00000000..c8516683 --- /dev/null +++ b/doc/validation.rst @@ -0,0 +1,12 @@ +============================== +Validating NumpyDoc docstrings +============================== + +One tool for validating docstrings is to see how an object's dosctring +translates to Restructured Text. Using numpydoc as a command-line tool +facilitates this. For example to see the Restructured Text generated +for ``numpy.ndarray``, use: + +.. code-block:: bash + + $ python -m numpydoc numpy.ndarray diff --git a/numpydoc/__main__.py b/numpydoc/__main__.py new file mode 100644 index 00000000..25f58cf8 --- /dev/null +++ b/numpydoc/__main__.py @@ -0,0 +1,44 @@ +import argparse +import importlib +import ast + +from .docscrape_sphinx import get_doc_object + + +def main(argv=None): + """Test numpydoc docstring generation for a given object""" + + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument('import_path', help='e.g. numpy.ndarray') + + def _parse_config(s): + key, _, value = s.partition('=') + value = ast.literal_eval(value) + return key, value + + ap.add_argument('-c', '--config', type=_parse_config, + action='append', + help='key=val where val will be parsed by literal_eval, ' + 'e.g. -c use_plots=True. Multiple -c can be used.') + args = ap.parse_args(argv) + + parts = args.import_path.split('.') + + for split_point in range(len(parts), 0, -1): + try: + path = '.'.join(parts[:split_point]) + obj = importlib.import_module(path) + except ImportError: + continue + break + else: + raise ImportError('Could not resolve {!r} to an importable object' + ''.format(args.import_path)) + + for part in parts[split_point:]: + obj = getattr(obj, part) + + print(get_doc_object(obj, config=dict(args.config or []))) + +if __name__ == '__main__': + main() diff --git a/numpydoc/tests/test_main.py b/numpydoc/tests/test_main.py new file mode 100644 index 00000000..e565bb29 --- /dev/null +++ b/numpydoc/tests/test_main.py @@ -0,0 +1,80 @@ +from __future__ import print_function + +from contextlib import contextmanager +import os +import sys +import tempfile +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from numpydoc.__main__ import main + + +PACKAGE_CODE = """ +'''This package has test stuff''' +""" + +MODULE_CODE = """ +'''This module has test stuff''' + +def foo(a, b=5): + '''Hello world + + Parameters + ---------- + something : foo + bar + something_else + bar + ''' +""" + + +@contextmanager +def _mock_module(pkg_name): + try: + tempdir = tempfile.mkdtemp() + os.mkdir(os.path.join(tempdir, pkg_name)) + with open(os.path.join(tempdir, pkg_name, '__init__.py'), 'w') as f: + print(PACKAGE_CODE, file=f) + with open(os.path.join(tempdir, pkg_name, 'module.py'), 'w') as f: + print(MODULE_CODE, file=f) + + sys.path.insert(0, tempdir) + yield tempdir + finally: + try: + os.path.rmdir(tempdir) + sys.path.remove(tempdir) + except: + pass + + +def _capture_main(*args): + f = StringIO() + sys.stdout, old_stdout = f, sys.stdout + try: + main(args) + return f.getvalue().strip('\n\r') + finally: + sys.stdout = old_stdout + + +def test_main(): + # TODO: does not currently check that numpydoc transformations are applied + + assert (_capture_main('numpydoc.__main__.main') == + main.__doc__.strip()) + + # check it works with modules not imported from __init__ + with _mock_module('somepackage1'): + out = _capture_main('somepackage1.module.foo') + assert out.startswith('Hello world\n') + with _mock_module('somepackage2'): + out = _capture_main('somepackage2.module') + assert out.startswith('This module has test') + with _mock_module('somepackage3'): + out = _capture_main('somepackage3') + assert out.startswith('This package has test')