Close #8681: viewcode: Support incremental build

Now viewcode supports incremental build. It generates HTML files only
when the original python code is updated from last build.
This commit is contained in:
Takeshi KOMIYA 2021-01-18 02:11:47 +09:00
parent 596dfba841
commit 2e01c34acf
2 changed files with 41 additions and 1 deletions

View File

@ -32,6 +32,7 @@ Features added
type
* #6241: mathjax: Include mathjax.js only on the document using equations
* #8651: std domain: cross-reference for a rubric having inline item is broken
* #8681: viewcode: Support incremental build
* #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright`
* #207: Now :confval:`highlight_language` supports multiple languages
* #2030: :rst:dir:`code-block` and :rst:dir:`literalinclude` supports automatic

View File

@ -9,7 +9,8 @@
"""
import traceback
from typing import Any, Dict, Iterable, Iterator, Set, Tuple
from os import path
from typing import Any, Dict, Iterable, Iterator, Optional, Set, Tuple, cast
from docutils import nodes
from docutils.nodes import Element, Node
@ -17,6 +18,7 @@ from docutils.nodes import Element, Node
import sphinx
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer
@ -138,6 +140,40 @@ def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnod
return None
def get_module_filename(app: Sphinx, modname: str) -> Optional[str]:
"""Get module filename for *modname*."""
source_info = app.emit_firstresult('viewcode-find-source', modname)
if source_info:
return None
else:
try:
filename, source = ModuleAnalyzer.get_module_source(modname)
return filename
except Exception:
return None
def should_generate_module_page(app: Sphinx, modname: str) -> bool:
"""Check generation of module page is needed."""
module_filename = get_module_filename(app, modname)
if module_filename is None:
# Always (re-)generate module page when module filename is not found.
return True
builder = cast(StandaloneHTMLBuilder, app.builder)
basename = modname.replace('.', '/') + builder.out_suffix
page_filename = path.join(app.outdir, '_modules/', basename)
try:
if path.getmtime(module_filename) <= path.getmtime(page_filename):
# generation is not needed if the HTML page is newer than module file.
return False
except IOError:
pass
return True
def collect_pages(app: Sphinx) -> Iterator[Tuple[str, Dict[str, Any], str]]:
env = app.builder.env
if not hasattr(env, '_viewcode_modules'):
@ -154,6 +190,9 @@ def collect_pages(app: Sphinx) -> Iterator[Tuple[str, Dict[str, Any], str]]:
app.verbosity, lambda x: x[0]):
if not entry:
continue
if not should_generate_module_page(app, modname):
continue
code, tags, used, refname = entry
# construct a page name for the highlighted source
pagename = '_modules/' + modname.replace('.', '/')