diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 4d7796a..bbebb1f 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -64,6 +64,7 @@ from IPython.utils import PyColorize from IPython.utils import io from IPython.utils import py3compat from IPython.utils import openpy +from IPython.utils.decorators import undoc from IPython.utils.doctestreload import doctest_reload from IPython.utils.io import ask_yes_no from IPython.utils.ipstruct import Struct @@ -90,6 +91,7 @@ dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') # Utilities #----------------------------------------------------------------------------- +@undoc def softspace(file, newvalue): """Copied from code.py, to remove the dependency""" @@ -105,9 +107,10 @@ def softspace(file, newvalue): pass return oldvalue - +@undoc def no_op(*a, **kw): pass +@undoc class NoOpContext(object): def __enter__(self): pass def __exit__(self, type, value, traceback): pass @@ -115,6 +118,7 @@ no_op_context = NoOpContext() class SpaceInInput(Exception): pass +@undoc class Bunch: pass diff --git a/IPython/utils/decorators.py b/IPython/utils/decorators.py index b96379f..208bf83 100644 --- a/IPython/utils/decorators.py +++ b/IPython/utils/decorators.py @@ -44,3 +44,11 @@ def flag_calls(func): wrapper.__doc__ = func.__doc__ return wrapper +def undoc(func): + """Mark a function or class as undocumented. + + This is found by inspecting the AST, so for now it must be used directly + as @undoc, not as e.g. @decorators.undoc + """ + return func + diff --git a/docs/sphinxext/apigen.py b/docs/sphinxext/apigen.py index ac93c19..3c16173 100644 --- a/docs/sphinxext/apigen.py +++ b/docs/sphinxext/apigen.py @@ -18,6 +18,7 @@ PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed project.""" # Stdlib imports +import ast import os import re @@ -94,21 +95,6 @@ class ApiDocWriter(object): package_name = property(get_package_name, set_package_name, None, 'get/set package_name') - def _get_object_name(self, line): - ''' Get second token in line - >>> docwriter = ApiDocWriter('sphinx') - >>> docwriter._get_object_name(" def func(): ") - 'func' - >>> docwriter._get_object_name(" class Klass(object): ") - 'Klass' - >>> docwriter._get_object_name(" class Klass: ") - 'Klass' - ''' - name = line.split()[1].split('(')[0].strip() - # in case we have classes which are not derived from object - # ie. old style classes - return name.rstrip(':') - def _uri2path(self, uri): ''' Convert uri to absolute filepath @@ -164,30 +150,31 @@ class ApiDocWriter(object): if filename is None: # nothing that we could handle here. return ([],[]) - f = open(filename, 'rt') - functions, classes = self._parse_lines(f) - f.close() - return functions, classes - - def _parse_lines(self, linesource): - ''' Parse lines of text for functions and classes ''' - functions = [] - classes = [] - for line in linesource: - if line.startswith('def ') and line.count('('): - # exclude private stuff - name = self._get_object_name(line) - if not name.startswith('_'): - functions.append(name) - elif line.startswith('class '): - # exclude private stuff - name = self._get_object_name(line) - if not name.startswith('_'): - classes.append(name) - else: - pass - functions.sort() - classes.sort() + with open(filename, 'rb') as f: + mod = ast.parse(f.read()) + return self._find_functions_classes(mod) + + @staticmethod + def _find_functions_classes(mod): + """Extract top-level functions and classes from a module AST. + + Skips objects with an @undoc decorator, or a name starting with '_'. + """ + def has_undoc_decorator(node): + return any(isinstance(d, ast.Name) and d.id == 'undoc' \ + for d in node.decorator_list) + + functions, classes = [], [] + for node in mod.body: + if isinstance(node, ast.FunctionDef) and \ + not node.name.startswith('_') and \ + not has_undoc_decorator(node): + functions.append(node.name) + elif isinstance(node, ast.ClassDef) and \ + not node.name.startswith('_') and \ + not has_undoc_decorator(node): + classes.append(node.name) + return functions, classes def generate_api_doc(self, uri):