From 43e20b25b9e6385d9d195a43c3bcbb937a87c874 2014-07-03 01:05:40 From: Thomas Kluyver Date: 2014-07-03 01:05:40 Subject: [PATCH] Add option to generate API docs based on __all__ --- diff --git a/IPython/nbformat/current.py b/IPython/nbformat/current.py index b2ad9b2..6bba5c2 100644 --- a/IPython/nbformat/current.py +++ b/IPython/nbformat/current.py @@ -22,6 +22,12 @@ from .validator import validate from IPython.utils.log import get_logger +__all__ = ['NotebookNode', 'new_code_cell', 'new_text_cell', 'new_notebook', +'new_output', 'new_worksheet', 'parse_filename', 'new_metadata', 'new_author', +'new_heading_cell', 'nbformat', 'nbformat_minor', 'nbformat_schema', +'to_notebook_json', 'convert', 'validate', 'NBFormatError', 'parse_py', +'reads_json', 'writes_json', 'reads_py', 'writes_py', 'reads', 'writes', 'read', +'write'] current_nbformat = nbformat current_nbformat_minor = nbformat_minor diff --git a/docs/autogen_api.py b/docs/autogen_api.py index 7899d3c..323d4d4 100755 --- a/docs/autogen_api.py +++ b/docs/autogen_api.py @@ -21,6 +21,8 @@ if __name__ == '__main__': # Extensions are documented elsewhere. r'\.extensions', r'\.config\.profile', + # These should be accessed via nbformat.current + r'\.nbformat\.v\d+', ] # The inputhook* modules often cause problems on import, such as trying to @@ -33,7 +35,18 @@ if __name__ == '__main__': r'\.frontend$', # We document this manually. r'\.utils\.py3compat', + # These are exposed by nbformat.current + r'\.nbformat\.convert', + r'\.nbformat\.validator', ] + + # These modules import functions and classes from other places to expose + # them as part of the public API. They must have __all__ defined. The + # non-API modules they import from should be excluded by the skip patterns + # above. + docwriter.names_from__all__.update({ + 'IPython.nbformat.current', + }) # Now, generate the outputs docwriter.write_api_docs(outdir) diff --git a/docs/sphinxext/apigen.py b/docs/sphinxext/apigen.py index 21f28e2..3be2ec2 100644 --- a/docs/sphinxext/apigen.py +++ b/docs/sphinxext/apigen.py @@ -21,6 +21,7 @@ from __future__ import print_function # Stdlib imports import ast +import inspect import os import re @@ -85,6 +86,7 @@ class ApiDocWriter(object): rst_extension='.rst', package_skip_patterns=None, module_skip_patterns=None, + names_from__all__=None, ): ''' Initialize package for parsing @@ -111,6 +113,12 @@ class ApiDocWriter(object): ``.util.console`` If is None, gives default. Default is: ['\.setup$', '\._'] + names_from__all__ : set, optional + Modules listed in here will be scanned by doing ``from mod import *``, + rather than finding function and class definitions by scanning the + AST. This is intended for API modules which expose things defined in + other files. Modules listed here must define ``__all__`` to avoid + exposing everything they import. ''' if package_skip_patterns is None: package_skip_patterns = ['\\.tests$'] @@ -120,6 +128,7 @@ class ApiDocWriter(object): self.rst_extension = rst_extension self.package_skip_patterns = package_skip_patterns self.module_skip_patterns = module_skip_patterns + self.names_from__all__ = names_from__all__ or set() def get_package_name(self): return self._package_name @@ -204,6 +213,29 @@ class ApiDocWriter(object): mod = ast.parse(f.read()) return FuncClsScanner().scan(mod) + def _import_funcs_classes(self, uri): + """Import * from uri, and separate out functions and classes.""" + ns = {} + exec('from %s import *' % uri, ns) + funcs, classes = [], [] + for name, obj in ns.items(): + if inspect.isclass(obj): + cls = Obj(name=name, has_init='__init__' in obj.__dict__) + classes.append(cls) + elif inspect.isfunction(obj): + funcs.append(name) + + return sorted(funcs), sorted(classes, key=lambda x: x.name) + + def find_funcs_classes(self, uri): + """Find the functions and classes defined in the module ``uri``""" + if uri in self.names_from__all__: + # For API modules which expose things defined elsewhere, import them + return self._import_funcs_classes(uri) + else: + # For other modules, scan their AST to see what they define + return self._parse_module(uri) + def generate_api_doc(self, uri): '''Make autodoc documentation template string for a module @@ -218,7 +250,7 @@ class ApiDocWriter(object): Contents of API doc ''' # get the names of all classes and functions - functions, classes = self._parse_module(uri) + functions, classes = self.find_funcs_classes(uri) if not len(functions) and not len(classes): #print ('WARNING: Empty -', uri) # dbg return ''