2015-03-27 19:50:56 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
2015-06-10 20:13:34 +00:00
|
|
|
Sphinx Auto-API Top-level Extension.
|
|
|
|
|
|
|
|
This extension allows you to automagically generate API documentation from your project.
|
2015-03-27 19:50:56 +00:00
|
|
|
"""
|
2018-10-11 21:07:03 +00:00
|
|
|
import codecs
|
2015-06-06 23:11:49 +00:00
|
|
|
import os
|
2015-04-07 20:35:50 +00:00
|
|
|
import shutil
|
2015-03-27 19:50:56 +00:00
|
|
|
|
2017-02-08 14:53:19 +00:00
|
|
|
import sphinx
|
2015-06-10 18:35:54 +00:00
|
|
|
from sphinx.util.console import darkgreen, bold
|
2015-07-07 22:30:16 +00:00
|
|
|
from sphinx.addnodes import toctree
|
2015-08-03 20:55:33 +00:00
|
|
|
from sphinx.errors import ExtensionError
|
2019-01-27 01:11:12 +00:00
|
|
|
import sphinx.util.logging
|
2016-11-02 23:29:28 +00:00
|
|
|
from docutils.parsers.rst import directives
|
2015-06-10 18:35:54 +00:00
|
|
|
|
2018-08-01 23:04:45 +00:00
|
|
|
from . import documenters
|
2018-05-11 00:49:32 +00:00
|
|
|
from . import utils
|
2019-01-27 05:20:45 +00:00
|
|
|
from .backends import (
|
|
|
|
default_file_mapping,
|
|
|
|
default_ignore_patterns,
|
|
|
|
default_backend_mapping,
|
|
|
|
)
|
2017-08-31 23:42:47 +00:00
|
|
|
from .directives import AutoapiSummary, NestedParse
|
2015-10-28 18:04:23 +00:00
|
|
|
from .settings import API_ROOT
|
2016-11-03 19:54:26 +00:00
|
|
|
from .toctree import add_domain_to_toctree
|
2015-04-01 00:01:41 +00:00
|
|
|
|
2019-01-27 01:11:12 +00:00
|
|
|
LOGGER = sphinx.util.logging.getLogger(__name__)
|
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
default_options = ["members", "undoc-members", "private-members", "special-members"]
|
2018-05-11 00:49:32 +00:00
|
|
|
_viewcode_cache = {}
|
|
|
|
"""Caches a module's parse results for use in viewcode.
|
|
|
|
|
|
|
|
:type: dict(str, tuple)
|
|
|
|
"""
|
2015-06-10 20:13:34 +00:00
|
|
|
|
2015-04-01 00:01:41 +00:00
|
|
|
|
2015-06-10 18:35:54 +00:00
|
|
|
def run_autoapi(app):
|
2015-04-23 20:31:03 +00:00
|
|
|
"""
|
|
|
|
Load AutoAPI data from the filesystem.
|
|
|
|
"""
|
|
|
|
|
2015-09-23 23:00:43 +00:00
|
|
|
if not app.config.autoapi_dirs:
|
2019-01-27 05:20:45 +00:00
|
|
|
raise ExtensionError("You must configure an autoapi_dirs setting")
|
2015-06-10 18:35:54 +00:00
|
|
|
|
2015-07-07 23:19:25 +00:00
|
|
|
# Make sure the paths are full
|
2015-09-23 23:00:43 +00:00
|
|
|
normalized_dirs = []
|
2016-05-02 02:23:05 +00:00
|
|
|
autoapi_dirs = app.config.autoapi_dirs
|
2016-06-08 23:34:52 +00:00
|
|
|
if isinstance(autoapi_dirs, str):
|
2016-05-02 02:23:05 +00:00
|
|
|
autoapi_dirs = [autoapi_dirs]
|
|
|
|
for path in autoapi_dirs:
|
2015-09-23 23:00:43 +00:00
|
|
|
if os.path.isabs(path):
|
2016-02-03 05:14:28 +00:00
|
|
|
normalized_dirs.append(path)
|
2015-09-23 23:00:43 +00:00
|
|
|
else:
|
2019-01-27 05:20:45 +00:00
|
|
|
normalized_dirs.append(os.path.normpath(os.path.join(app.confdir, path)))
|
2015-07-07 23:19:25 +00:00
|
|
|
|
2015-09-23 23:00:43 +00:00
|
|
|
for _dir in normalized_dirs:
|
|
|
|
if not os.path.exists(_dir):
|
|
|
|
raise ExtensionError(
|
2019-01-27 05:20:45 +00:00
|
|
|
"AutoAPI Directory `{dir}` not found. "
|
|
|
|
"Please check your `autoapi_dirs` setting.".format(dir=_dir)
|
2015-09-23 23:00:43 +00:00
|
|
|
)
|
2015-08-03 17:36:04 +00:00
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
normalized_root = os.path.normpath(
|
|
|
|
os.path.join(app.confdir, app.config.autoapi_root)
|
|
|
|
)
|
|
|
|
url_root = os.path.join("/", app.config.autoapi_root)
|
2015-07-07 23:19:25 +00:00
|
|
|
|
2016-11-04 20:42:15 +00:00
|
|
|
sphinx_mapper = default_backend_mapping[app.config.autoapi_type]
|
2019-01-27 05:20:45 +00:00
|
|
|
sphinx_mapper_obj = sphinx_mapper(
|
|
|
|
app, template_dir=app.config.autoapi_template_dir, url_root=url_root
|
|
|
|
)
|
2017-08-31 23:42:47 +00:00
|
|
|
app.env.autoapi_mapper = sphinx_mapper_obj
|
2015-06-10 18:35:54 +00:00
|
|
|
|
2015-07-07 23:32:38 +00:00
|
|
|
if app.config.autoapi_file_patterns:
|
2015-09-22 15:02:34 +00:00
|
|
|
file_patterns = app.config.autoapi_file_patterns
|
2015-07-07 23:19:25 +00:00
|
|
|
else:
|
2015-07-20 18:44:38 +00:00
|
|
|
file_patterns = default_file_mapping.get(app.config.autoapi_type, [])
|
2015-07-07 23:19:25 +00:00
|
|
|
|
2015-07-20 18:37:31 +00:00
|
|
|
if app.config.autoapi_ignore:
|
|
|
|
ignore_patterns = app.config.autoapi_ignore
|
|
|
|
else:
|
2015-07-20 18:44:38 +00:00
|
|
|
ignore_patterns = default_ignore_patterns.get(app.config.autoapi_type, [])
|
2015-07-20 18:37:31 +00:00
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
if ".rst" in app.config.source_suffix:
|
|
|
|
out_suffix = ".rst"
|
|
|
|
elif ".txt" in app.config.source_suffix:
|
|
|
|
out_suffix = ".txt"
|
2016-08-25 17:58:06 +00:00
|
|
|
else:
|
|
|
|
# Fallback to first suffix listed
|
|
|
|
out_suffix = app.config.source_suffix[0]
|
|
|
|
|
|
|
|
# Actual meat of the run.
|
2019-01-27 05:20:45 +00:00
|
|
|
LOGGER.info(bold("[AutoAPI] ") + darkgreen("Loading Data"))
|
2016-11-04 20:42:15 +00:00
|
|
|
sphinx_mapper_obj.load(
|
2019-01-27 05:20:45 +00:00
|
|
|
patterns=file_patterns, dirs=normalized_dirs, ignore=ignore_patterns
|
2015-06-06 23:11:49 +00:00
|
|
|
)
|
2015-06-10 18:35:54 +00:00
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
LOGGER.info(bold("[AutoAPI] ") + darkgreen("Mapping Data"))
|
2016-11-04 20:42:15 +00:00
|
|
|
sphinx_mapper_obj.map(options=app.config.autoapi_options)
|
2015-06-10 18:35:54 +00:00
|
|
|
|
2018-08-10 16:00:54 +00:00
|
|
|
if app.config.autoapi_generate_api_docs:
|
2019-01-27 05:20:45 +00:00
|
|
|
LOGGER.info(bold("[AutoAPI] ") + darkgreen("Rendering Data"))
|
|
|
|
sphinx_mapper_obj.output_rst(root=normalized_root, source_suffix=out_suffix)
|
2015-03-27 19:50:56 +00:00
|
|
|
|
|
|
|
|
2015-04-07 20:35:50 +00:00
|
|
|
def build_finished(app, exception):
|
2018-08-10 16:00:54 +00:00
|
|
|
if not app.config.autoapi_keep_files and app.config.autoapi_generate_api_docs:
|
2019-01-27 05:20:45 +00:00
|
|
|
normalized_root = os.path.normpath(
|
|
|
|
os.path.join(app.confdir, app.config.autoapi_root)
|
|
|
|
)
|
2015-04-07 20:35:50 +00:00
|
|
|
if app.verbosity > 1:
|
2019-01-27 05:20:45 +00:00
|
|
|
LOGGER.info(bold("[AutoAPI] ") + darkgreen("Cleaning generated .rst files"))
|
2015-07-07 23:19:25 +00:00
|
|
|
shutil.rmtree(normalized_root)
|
2015-04-07 20:35:50 +00:00
|
|
|
|
2016-11-04 20:42:15 +00:00
|
|
|
sphinx_mapper = default_backend_mapping[app.config.autoapi_type]
|
2019-01-27 05:20:45 +00:00
|
|
|
if hasattr(sphinx_mapper, "build_finished"):
|
2016-11-04 20:42:15 +00:00
|
|
|
sphinx_mapper.build_finished(app, exception)
|
2015-08-03 17:36:04 +00:00
|
|
|
|
2015-04-07 20:35:50 +00:00
|
|
|
|
2015-07-07 22:30:16 +00:00
|
|
|
def doctree_read(app, doctree):
|
2016-11-03 19:54:26 +00:00
|
|
|
"""
|
|
|
|
Inject AutoAPI into the TOC Tree dynamically.
|
|
|
|
"""
|
2019-01-27 05:20:45 +00:00
|
|
|
if app.env.docname == "index":
|
2017-06-28 00:12:47 +00:00
|
|
|
all_docs = set()
|
|
|
|
insert = True
|
2015-07-07 22:30:16 +00:00
|
|
|
nodes = doctree.traverse(toctree)
|
2019-01-27 05:20:45 +00:00
|
|
|
toc_entry = "%s/index" % app.config.autoapi_root
|
2019-04-21 19:16:18 +00:00
|
|
|
add_entry = (
|
|
|
|
nodes
|
|
|
|
and app.config.autoapi_generate_api_docs
|
|
|
|
and app.config.autoapi_add_toctree_entry
|
|
|
|
)
|
|
|
|
if not add_entry:
|
2015-07-07 22:30:16 +00:00
|
|
|
return
|
2017-06-28 00:12:47 +00:00
|
|
|
# Capture all existing toctree entries
|
2015-07-07 22:30:16 +00:00
|
|
|
for node in nodes:
|
2019-01-27 05:20:45 +00:00
|
|
|
for entry in node["entries"]:
|
2015-07-07 22:30:16 +00:00
|
|
|
all_docs.add(entry[1])
|
2017-06-28 00:12:47 +00:00
|
|
|
# Don't insert autoapi it's already present
|
2015-07-07 22:30:16 +00:00
|
|
|
for doc in all_docs:
|
2015-07-07 22:48:00 +00:00
|
|
|
if doc.find(app.config.autoapi_root) != -1:
|
2015-07-07 22:30:16 +00:00
|
|
|
insert = False
|
|
|
|
if insert and app.config.autoapi_add_toctree_entry:
|
2018-08-06 23:19:45 +00:00
|
|
|
# Insert AutoAPI index
|
2019-01-27 05:20:45 +00:00
|
|
|
nodes[-1]["entries"].append((None, u"%s/index" % app.config.autoapi_root))
|
|
|
|
nodes[-1]["includefiles"].append(u"%s/index" % app.config.autoapi_root)
|
|
|
|
message_prefix = bold("[AutoAPI] ")
|
2018-08-07 21:33:42 +00:00
|
|
|
message = darkgreen(
|
2019-01-27 05:20:45 +00:00
|
|
|
"Adding AutoAPI TOCTree [{0}] to index.rst".format(toc_entry)
|
2018-08-07 21:33:42 +00:00
|
|
|
)
|
2019-01-27 01:11:12 +00:00
|
|
|
LOGGER.info(message_prefix + message)
|
2015-07-07 22:30:16 +00:00
|
|
|
|
|
|
|
|
2017-08-31 23:42:47 +00:00
|
|
|
def clear_env(app, env):
|
|
|
|
"""Clears the environment of the unpicklable objects that we left behind."""
|
|
|
|
env.autoapi_mapper = None
|
|
|
|
|
|
|
|
|
2018-05-11 00:49:32 +00:00
|
|
|
def viewcode_find(app, modname):
|
|
|
|
mapper = app.env.autoapi_mapper
|
2018-08-13 23:52:52 +00:00
|
|
|
if modname not in mapper.objects:
|
2018-05-11 00:49:32 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
if modname in _viewcode_cache:
|
|
|
|
return _viewcode_cache[modname]
|
|
|
|
|
|
|
|
locations = {}
|
2018-08-13 23:52:52 +00:00
|
|
|
module = mapper.objects[modname]
|
2018-05-11 00:49:32 +00:00
|
|
|
for child in module.children:
|
2019-01-27 05:20:45 +00:00
|
|
|
stack = [("", child)]
|
2018-05-11 00:49:32 +00:00
|
|
|
while stack:
|
|
|
|
prefix, obj = stack.pop()
|
2019-01-27 05:20:45 +00:00
|
|
|
type_ = "other"
|
|
|
|
if obj.type == "class":
|
|
|
|
type_ = "class"
|
|
|
|
elif obj.type in ("function", "method"):
|
|
|
|
type_ = "def"
|
2018-05-11 00:49:32 +00:00
|
|
|
full_name = prefix + obj.name
|
2019-01-27 05:20:45 +00:00
|
|
|
if "from_line_no" in obj.obj:
|
2018-08-13 04:54:46 +00:00
|
|
|
locations[full_name] = (
|
2019-01-27 05:20:45 +00:00
|
|
|
type_,
|
|
|
|
obj.obj["from_line_no"],
|
|
|
|
obj.obj["to_line_no"],
|
2018-08-13 04:54:46 +00:00
|
|
|
)
|
2019-01-27 05:20:45 +00:00
|
|
|
children = getattr(obj, "children", ())
|
|
|
|
stack.extend((full_name + ".", gchild) for gchild in children)
|
2018-05-11 00:49:32 +00:00
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
if module.obj["encoding"]:
|
2018-10-11 21:07:03 +00:00
|
|
|
source = codecs.open(
|
2019-01-27 05:20:45 +00:00
|
|
|
module.obj["file_path"], encoding=module.obj["encoding"]
|
2018-10-11 21:07:03 +00:00
|
|
|
).read()
|
|
|
|
else:
|
2019-01-27 05:20:45 +00:00
|
|
|
source = open(module.obj["file_path"]).read()
|
2018-10-11 21:07:03 +00:00
|
|
|
|
|
|
|
result = (source, locations)
|
2018-05-11 00:49:32 +00:00
|
|
|
_viewcode_cache[modname] = result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2018-08-13 23:52:52 +00:00
|
|
|
def viewcode_follow_imported(app, modname, attribute):
|
2019-01-27 05:20:45 +00:00
|
|
|
fullname = "{}.{}".format(modname, attribute)
|
2018-08-13 23:52:52 +00:00
|
|
|
mapper = app.env.autoapi_mapper
|
|
|
|
if fullname not in mapper.all_objects:
|
|
|
|
return None
|
|
|
|
|
2019-01-27 05:20:45 +00:00
|
|
|
orig_path = mapper.all_objects[fullname].obj.get("original_path", "")
|
2018-08-13 23:52:52 +00:00
|
|
|
if orig_path.endswith(attribute):
|
2019-01-27 05:20:45 +00:00
|
|
|
return orig_path[: -len(attribute) - 1]
|
2018-08-13 23:52:52 +00:00
|
|
|
|
|
|
|
return modname
|
|
|
|
|
|
|
|
|
2015-03-27 19:50:56 +00:00
|
|
|
def setup(app):
|
2019-01-27 05:20:45 +00:00
|
|
|
app.connect("builder-inited", run_autoapi)
|
|
|
|
app.connect("doctree-read", doctree_read)
|
|
|
|
app.connect("doctree-resolved", add_domain_to_toctree)
|
|
|
|
app.connect("build-finished", build_finished)
|
|
|
|
app.connect("env-updated", clear_env)
|
2018-08-13 23:52:52 +00:00
|
|
|
if sphinx.version_info >= (1, 8):
|
2019-01-27 05:20:45 +00:00
|
|
|
if "viewcode-find-source" in app.events.events:
|
|
|
|
app.connect("viewcode-find-source", viewcode_find)
|
|
|
|
if "viewcode-follow-imported" in app.events.events:
|
|
|
|
app.connect("viewcode-follow-imported", viewcode_follow_imported)
|
|
|
|
app.add_config_value("autoapi_type", "python", "html")
|
|
|
|
app.add_config_value("autoapi_root", API_ROOT, "html")
|
|
|
|
app.add_config_value("autoapi_ignore", [], "html")
|
|
|
|
app.add_config_value("autoapi_options", default_options, "html")
|
|
|
|
app.add_config_value("autoapi_file_patterns", None, "html")
|
|
|
|
app.add_config_value("autoapi_dirs", [], "html")
|
|
|
|
app.add_config_value("autoapi_keep_files", False, "html")
|
|
|
|
app.add_config_value("autoapi_add_toctree_entry", True, "html")
|
|
|
|
app.add_config_value("autoapi_template_dir", None, "html")
|
|
|
|
app.add_config_value("autoapi_include_summaries", False, "html")
|
|
|
|
app.add_config_value("autoapi_python_class_content", "class", "html")
|
|
|
|
app.add_config_value("autoapi_generate_api_docs", True, "html")
|
2018-08-01 23:04:45 +00:00
|
|
|
app.add_autodocumenter(documenters.AutoapiFunctionDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiClassDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiMethodDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiDataDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiAttributeDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiModuleDocumenter)
|
|
|
|
app.add_autodocumenter(documenters.AutoapiExceptionDocumenter)
|
2019-01-27 05:20:45 +00:00
|
|
|
directives.register_directive("autoapi-nested-parse", NestedParse)
|
|
|
|
directives.register_directive("autoapisummary", AutoapiSummary)
|
|
|
|
app.setup_extension("sphinx.ext.autosummary")
|