diff options
Diffstat (limited to 'sphinx/apidoc.py')
-rw-r--r-- | sphinx/apidoc.py | 64 |
1 files changed, 41 insertions, 23 deletions
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py index 58724fd5a..05928be81 100644 --- a/sphinx/apidoc.py +++ b/sphinx/apidoc.py @@ -21,8 +21,9 @@ import sys import optparse from os import path from six import binary_type +from fnmatch import fnmatch -from sphinx.util.osutil import walk +from sphinx.util.osutil import FileAvoidWrite, walk from sphinx import __display_version__ # automodule options @@ -62,11 +63,8 @@ def write_file(name, text, opts): print('File %s already exists, skipping.' % fname) else: print('Creating file %s.' % fname) - f = open(fname, 'w') - try: + with FileAvoidWrite(fname) as f: f.write(text) - finally: - f.close() def format_heading(level, text): @@ -94,11 +92,12 @@ def create_module_file(package, module, opts): write_file(makename(package, module), text, opts) -def create_package_file(root, master_package, subroot, py_files, opts, subs): +def create_package_file(root, master_package, subroot, py_files, opts, subs, is_namespace): """Build the text of the file and write the file.""" - text = format_heading(1, '%s package' % makename(master_package, subroot)) + text = format_heading(1, ('%s package' if not is_namespace else "%s namespace") + % makename(master_package, subroot)) - if opts.modulefirst: + if opts.modulefirst and not is_namespace: text += format_directive(subroot, master_package) text += '\n' @@ -141,7 +140,7 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs): text += '\n' text += '\n' - if not opts.modulefirst: + if not opts.modulefirst and not is_namespace: text += format_heading(2, 'Module contents') text += format_directive(subroot, master_package) @@ -168,9 +167,14 @@ def create_modules_toc_file(modules, opts, name='modules'): def shall_skip(module, opts): """Check if we want to skip this module.""" + # skip if the file doesn't exist and not using implicit namespaces + if not opts.implicit_namespaces and not path.exists(module): + return True + # skip it if there is nothing (or just \n or \r\n) in the file - if path.getsize(module) <= 2: + if path.exists(module) and path.getsize(module) <= 2: return True + # skip if it has a "private" name and this is selected filename = path.basename(module) if filename != '__init__.py' and filename.startswith('_') and \ @@ -194,19 +198,22 @@ def recurse_tree(rootpath, excludes, opts): toplevels = [] followlinks = getattr(opts, 'followlinks', False) includeprivate = getattr(opts, 'includeprivate', False) + implicit_namespaces = getattr(opts, 'implicit_namespaces', False) for root, subs, files in walk(rootpath, followlinks=followlinks): # document only Python module files (that aren't excluded) py_files = sorted(f for f in files if path.splitext(f)[1] in PY_SUFFIXES and not is_excluded(path.join(root, f), excludes)) is_pkg = INITPY in py_files + is_namespace = INITPY not in py_files and implicit_namespaces if is_pkg: py_files.remove(INITPY) py_files.insert(0, INITPY) elif root != rootpath: - # only accept non-package at toplevel - del subs[:] - continue + # only accept non-package at toplevel unless using implicit namespaces + if not implicit_namespaces: + del subs[:] + continue # remove hidden ('.') and private ('_') directories, as well as # excluded dirs if includeprivate: @@ -216,15 +223,17 @@ def recurse_tree(rootpath, excludes, opts): subs[:] = sorted(sub for sub in subs if not sub.startswith(exclude_prefixes) and not is_excluded(path.join(root, sub), excludes)) - if is_pkg: + if is_pkg or is_namespace: # we are in a package with something to document - if subs or len(py_files) > 1 or not \ - shall_skip(path.join(root, INITPY), opts): + if subs or len(py_files) > 1 or not shall_skip(path.join(root, INITPY), opts): subpackage = root[len(rootpath):].lstrip(path.sep).\ replace(path.sep, '.') - create_package_file(root, root_package, subpackage, - py_files, opts, subs) - toplevels.append(makename(root_package, subpackage)) + # if this is not a namespace or + # a namespace and there is something there to document + if not is_namespace or len(py_files) > 0: + create_package_file(root, root_package, subpackage, + py_files, opts, subs, is_namespace) + toplevels.append(makename(root_package, subpackage)) else: # if we are at the root level, we don't require it to be a package assert root == rootpath and root_package is None @@ -249,7 +258,7 @@ def is_excluded(root, excludes): e.g. an exlude "foo" also accidentally excluding "foobar". """ for exclude in excludes: - if root == exclude: + if fnmatch(root, exclude): return True return False @@ -258,13 +267,13 @@ def main(argv=sys.argv): """Parse and check the command line arguments.""" parser = optparse.OptionParser( usage="""\ -usage: %prog [options] -o <output_path> <module_path> [exclude_path, ...] +usage: %prog [options] -o <output_path> <module_path> [exclude_pattern, ...] Look recursively in <module_path> for Python modules and packages and create one reST file with automodule directives per package in the <output_path>. -The <exclude_path>s can be files and/or directories that will be excluded -from generation. +The <exclude_pattern>s can be file and/or directory patterns that will be +excluded from generation. Note: By default this script will not overwrite already created files.""") @@ -298,10 +307,17 @@ Note: By default this script will not overwrite already created files.""") dest='modulefirst', help='Put module documentation before submodule ' 'documentation') + parser.add_option('--implicit-namespaces', action='store_true', + dest='implicit_namespaces', + help='Interpret module paths according to PEP-0420 ' + 'implicit namespaces specification') parser.add_option('-s', '--suffix', action='store', dest='suffix', help='file suffix (default: rst)', default='rst') parser.add_option('-F', '--full', action='store_true', dest='full', help='Generate a full project with sphinx-quickstart') + parser.add_option('-a', '--append-syspath', action='store_true', + dest='append_syspath', + help='Append module_path to sys.path, used when --full is given') parser.add_option('-H', '--doc-project', action='store', dest='header', help='Project name (default: root module name)') parser.add_option('-A', '--doc-author', action='store', dest='author', @@ -369,6 +385,8 @@ Note: By default this script will not overwrite already created files.""") mastertocmaxdepth = opts.maxdepth, mastertoctree = text, language = 'en', + module_path = rootpath, + append_syspath = opts.append_syspath, ) if isinstance(opts.header, binary_type): d['project'] = d['project'].decode('utf-8') |