diff --git a/.gitignore b/.gitignore index 13d64bde..b7c7e9ef 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ output/*/index.html # Sphinx doc/build +doc/contributor/api/* # pbr generates these AUTHORS diff --git a/doc/source/conf.py b/doc/source/conf.py index 9d51df67..5665583d 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -15,7 +15,127 @@ import os import sys +import django + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) + +sys.path.insert(0, ROOT) + sys.path.insert(0, os.path.abspath('../..')) + +# Starting in Django 1.7, standalone scripts, such as a sphinx build +# require that django.setup() be called first. +# https://docs.djangoproject.com/en/1.8/releases/1.7/#standalone-scripts +django.setup() + +def write_autodoc_index(): + + def find_autodoc_modules(module_name, sourcedir): + """returns a list of modules in the SOURCE directory.""" + modlist = [] + os.chdir(os.path.join(sourcedir, module_name)) + print("SEARCHING %s" % sourcedir) + for root, dirs, files in os.walk("."): + for filename in files: + if filename == 'tests.py': + continue + if filename.endswith(".py"): + # remove the pieces of the root + elements = root.split(os.path.sep) + # replace the leading "." with the module name + elements[0] = module_name + # and get the base module name + base, extension = os.path.splitext(filename) + if not (base == "__init__"): + elements.append(base) + result = ".".join(elements) + # print result + modlist.append(result) + return modlist + + RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "contributor/api")) + SRCS = [('ironic_ui', ROOT), ] + + EXCLUDED_MODULES = () + CURRENT_SOURCES = {} + + if not(os.path.exists(RSTDIR)): + os.mkdir(RSTDIR) + CURRENT_SOURCES[RSTDIR] = ['autoindex.rst'] + + INDEXOUT = open(os.path.join(RSTDIR, "autoindex.rst"), "w") + INDEXOUT.write(""" +================= +Source Code Index +================= +.. contents:: + :depth: 1 + :local: +""") + + for modulename, path in SRCS: + sys.stdout.write("Generating source documentation for %s\n" % + modulename) + INDEXOUT.write("\n%s\n" % modulename.capitalize()) + INDEXOUT.write("%s\n" % ("=" * len(modulename),)) + INDEXOUT.write(".. toctree::\n") + INDEXOUT.write(" :maxdepth: 1\n") + INDEXOUT.write("\n") + + MOD_DIR = os.path.join(RSTDIR, modulename) + CURRENT_SOURCES[MOD_DIR] = [] + if not(os.path.exists(MOD_DIR)): + os.mkdir(MOD_DIR) + for module in find_autodoc_modules(modulename, path): + if any([module.startswith(exclude) for exclude + in EXCLUDED_MODULES]): + print("Excluded module %s." % module) + continue + mod_path = os.path.join(path, *module.split(".")) + generated_file = os.path.join(MOD_DIR, "%s.rst" % module) + + INDEXOUT.write(" %s/%s\n" % (modulename, module)) + + # Find the __init__.py module if this is a directory + if os.path.isdir(mod_path): + source_file = ".".join((os.path.join(mod_path, "__init__"), + "py",)) + else: + source_file = ".".join((os.path.join(mod_path), "py")) + + CURRENT_SOURCES[MOD_DIR].append("%s.rst" % module) + # Only generate a new file if the source has changed or we don't + # have a doc file to begin with. + if not os.access(generated_file, os.F_OK) or ( + os.stat(generated_file).st_mtime < + os.stat(source_file).st_mtime): + print("Module %s updated, generating new documentation." + % module) + FILEOUT = open(generated_file, "w") + header = "The :mod:`%s` Module" % module + FILEOUT.write("%s\n" % ("=" * len(header),)) + FILEOUT.write("%s\n" % header) + FILEOUT.write("%s\n" % ("=" * len(header),)) + FILEOUT.write(".. automodule:: %s\n" % module) + FILEOUT.write(" :members:\n") + FILEOUT.write(" :undoc-members:\n") + FILEOUT.write(" :show-inheritance:\n") + FILEOUT.write(" :noindex:\n") + FILEOUT.close() + + INDEXOUT.close() + + # Delete auto-generated .rst files for sources which no longer exist + for directory, subdirs, files in list(os.walk(RSTDIR)): + for old_file in files: + if old_file not in CURRENT_SOURCES.get(directory, []): + print("Removing outdated file for %s" % old_file) + os.remove(os.path.join(directory, old_file)) + + +write_autodoc_index() + # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -40,6 +160,10 @@ master_doc = 'index' project = u'ironic-ui' copyright = u'2016, OpenStack Foundation' + +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = ['ironic-ui.'] + # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index 717581d2..7843fe53 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -9,3 +9,11 @@ the following will help get you started. :maxdepth: 1 contributing + +Autogenerated API Documentation +=============================== + +.. toctree:: + :maxdepth: 1 + + api/autoindex diff --git a/setup.cfg b/setup.cfg index eb7eab77..fb9fcf56 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,12 @@ classifier = Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 +[pbr] +autodoc_index_modules = True +autodoc_exclude_modules = + ironic_ui.test.* +api_doc_dir = contributor/api + [files] packages = ironic_ui diff --git a/tox.ini b/tox.ini index ecd8e480..55bde815 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,7 @@ setenv = VIRTUAL_ENV={envdir} NOSE_OPENSTACK_RED=0.05 NOSE_OPENSTACK_YELLOW=0.025 NOSE_OPENSTACK_SHOW_ELAPSED=1 + DJANGO_SETTINGS_MODULE=ironic_ui.test.settings deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = {toxinidir}/manage.py test ironic_ui --settings=ironic_ui.test.settings