summaryrefslogtreecommitdiff
path: root/sphinx/apidoc.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/apidoc.py')
-rw-r--r--sphinx/apidoc.py64
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')