Merge branch 'stable' into 1.7-release

This commit is contained in:
Takeshi KOMIYA 2018-01-28 00:18:33 +09:00
commit af25fa123d
6 changed files with 142 additions and 37 deletions

View File

@ -204,6 +204,8 @@ Bugs fixed
* #4438: math: math with labels with whitespace cause html error
* #2437: make full reference for classes, aliased with "alias of"
* #4434: pure numbers as link targets produce warning
* #4477: Build fails after building specific files
* #4449: apidoc: include "empty" packages that contain modules
Testing
--------

View File

@ -54,9 +54,11 @@ from sphinx.environment.adapters.indexentries import IndexEntries
if False:
# For type annotation
from typing import Any, Dict, Iterable, Iterator, List, Type, Tuple, Union # NOQA
from sphinx.domains import Domain, Index # NOQA
from typing import Any, Dict, IO, Iterable, Iterator, List, Type, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.util.tags import Tags # NOQA
# Experimental HTML5 Writer
if is_html5_writer_available():
@ -147,6 +149,56 @@ class Stylesheet(text_type):
return self
class BuildInfo(object):
"""buildinfo file manipulator.
HTMLBuilder and its family are storing their own envdata to ``.buildinfo``.
This class is a manipulator for the file.
"""
@classmethod
def load(cls, f):
# type: (IO) -> BuildInfo
try:
lines = f.readlines()
assert lines[0].rstrip() == '# Sphinx build info version 1'
assert lines[2].startswith('config: ')
assert lines[3].startswith('tags: ')
build_info = BuildInfo()
build_info.config_hash = lines[2].split()[1].strip()
build_info.tags_hash = lines[3].split()[1].strip()
return build_info
except Exception as exc:
raise ValueError('build info file is broken: %r' % exc)
def __init__(self, config=None, tags=None):
# type: (Config, Tags) -> None
self.config_hash = u''
self.tags_hash = u''
if config:
values = dict((c.name, c.value) for c in config.filter('html'))
self.config_hash = get_stable_hash(values)
if tags:
self.tags_hash = get_stable_hash(sorted(tags))
def __eq__(self, other): # type: ignore
# type: (BuildInfo) -> bool
return (self.config_hash == other.config_hash and
self.tags_hash == other.tags_hash)
def dump(self, f):
# type: (IO) -> None
f.write('# Sphinx build info version 1\n'
'# This file hashes the configuration used when building these files.'
' When it is not found, a full rebuild will be done.\n'
'config: %s\n'
'tags: %s\n' %
(self.config_hash, self.tags_hash))
class StandaloneHTMLBuilder(Builder):
"""
Builds standalone HTML docs.
@ -191,9 +243,7 @@ class StandaloneHTMLBuilder(Builder):
def init(self):
# type: () -> None
# a hash of all config values that, if changed, cause a full rebuild
self.config_hash = '' # type: unicode
self.tags_hash = '' # type: unicode
self.build_info = BuildInfo(self.config, self.tags)
# basename of images directory
self.imagedir = '_images'
# section numbers for headings in the currently visited document
@ -274,32 +324,19 @@ class StandaloneHTMLBuilder(Builder):
def get_outdated_docs(self):
# type: () -> Iterator[unicode]
cfgdict = dict((confval.name, confval.value) for confval in self.config.filter('html'))
self.config_hash = get_stable_hash(cfgdict)
self.tags_hash = get_stable_hash(sorted(self.tags))
old_config_hash = old_tags_hash = ''
try:
with open(path.join(self.outdir, '.buildinfo')) as fp:
version = fp.readline()
if version.rstrip() != '# Sphinx build info version 1':
raise ValueError
fp.readline() # skip commentary
cfg, old_config_hash = fp.readline().strip().split(': ')
if cfg != 'config':
raise ValueError
tag, old_tags_hash = fp.readline().strip().split(': ')
if tag != 'tags':
raise ValueError
except ValueError:
logger.warning('unsupported build info format in %r, building all',
path.join(self.outdir, '.buildinfo'))
except Exception:
pass
if old_config_hash != self.config_hash or \
old_tags_hash != self.tags_hash:
buildinfo = BuildInfo.load(fp)
if self.build_info != buildinfo:
for docname in self.env.found_docs:
yield docname
return
except ValueError as exc:
logger.warning('Failed to read build info file: %r', exc)
except IOError:
# ignore errors on reading
pass
if self.templates:
template_mtime = self.templates.newest_template_mtime()
@ -777,14 +814,9 @@ class StandaloneHTMLBuilder(Builder):
def write_buildinfo(self):
# type: () -> None
# write build info file
try:
with open(path.join(self.outdir, '.buildinfo'), 'w') as fp:
fp.write('# Sphinx build info version 1\n'
'# This file hashes the configuration used when building'
' these files. When it is not found, a full rebuild will'
' be done.\nconfig: %s\ntags: %s\n' %
(self.config_hash, self.tags_hash))
self.build_info.dump(fp)
except IOError as exc:
logger.warning('Failed to write build info file: %r', exc)
@ -1257,8 +1289,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
def init(self):
# type: () -> None
self.config_hash = ''
self.tags_hash = ''
self.build_info = BuildInfo(self.config, self.tags)
self.imagedir = '_images'
self.current_docname = None
self.theme = None # no theme necessary

View File

@ -18,6 +18,7 @@
from __future__ import print_function
import argparse
import glob
import os
import sys
from os import path
@ -194,6 +195,16 @@ def shall_skip(module, opts):
# skip it if there is nothing (or just \n or \r\n) in the file
if path.exists(module) and path.getsize(module) <= 2:
skip = True
if os.path.basename(module) == '__init__.py':
pattern = path.join(path.dirname(module), '*.py')
# We only want to skip packages if they do not contain any
# .py files other than __init__.py.
other_modules = list(glob.glob(pattern))
other_modules.remove(module)
skip = not other_modules
if skip:
return True
# skip if it has a "private" name and this is selected

View File

@ -0,0 +1 @@
"Module f"

View File

@ -67,6 +67,7 @@ def test_pep_0420_enabled(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile()
assert (outdir / 'a.b.e.rst').isfile()
assert (outdir / 'a.b.x.rst').isfile()
with open(outdir / 'a.b.c.rst') as f:
@ -74,6 +75,10 @@ def test_pep_0420_enabled(make_app, apidoc):
assert "automodule:: a.b.c.d\n" in rst
assert "automodule:: a.b.c\n" in rst
with open(outdir / 'a.b.e.rst') as f:
rst = f.read()
assert "automodule:: a.b.e.f\n" in rst
with open(outdir / 'a.b.x.rst') as f:
rst = f.read()
assert "automodule:: a.b.x.y\n" in rst
@ -86,12 +91,67 @@ def test_pep_0420_enabled(make_app, apidoc):
builddir = outdir / '_build' / 'text'
assert (builddir / 'a.b.c.txt').isfile()
assert (builddir / 'a.b.e.txt').isfile()
assert (builddir / 'a.b.x.txt').isfile()
with open(builddir / 'a.b.c.txt') as f:
txt = f.read()
assert "a.b.c package\n" in txt
with open(builddir / 'a.b.e.txt') as f:
txt = f.read()
assert "a.b.e.f module\n" in txt
with open(builddir / 'a.b.x.txt') as f:
txt = f.read()
assert "a.b.x namespace\n" in txt
@pytest.mark.apidoc(
coderoot='test-apidoc-pep420/a',
options=["--implicit-namespaces", "--separate"],
)
def test_pep_0420_enabled_separate(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile()
assert (outdir / 'a.b.e.rst').isfile()
assert (outdir / 'a.b.e.f.rst').isfile()
assert (outdir / 'a.b.x.rst').isfile()
assert (outdir / 'a.b.x.y.rst').isfile()
with open(outdir / 'a.b.c.rst') as f:
rst = f.read()
assert ".. toctree::\n\n a.b.c.d\n" in rst
with open(outdir / 'a.b.e.rst') as f:
rst = f.read()
assert ".. toctree::\n\n a.b.e.f\n" in rst
with open(outdir / 'a.b.x.rst') as f:
rst = f.read()
assert ".. toctree::\n\n a.b.x.y\n" in rst
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())
builddir = outdir / '_build' / 'text'
assert (builddir / 'a.b.c.txt').isfile()
assert (builddir / 'a.b.e.txt').isfile()
assert (builddir / 'a.b.e.f.txt').isfile()
assert (builddir / 'a.b.x.txt').isfile()
assert (builddir / 'a.b.x.y.txt').isfile()
with open(builddir / 'a.b.c.txt') as f:
txt = f.read()
assert "a.b.c package\n" in txt
with open(builddir / 'a.b.e.f.txt') as f:
txt = f.read()
assert "a.b.e.f module\n" in txt
with open(builddir / 'a.b.x.txt') as f:
txt = f.read()
assert "a.b.x namespace\n" in txt