Merge with '87998df9cbef2380345d436121e6bca43345d2bd' on stable

Conflicts:
	tests/test_build_latex.py
This commit is contained in:
shimizukawa 2016-01-10 10:21:27 +09:00
commit e5860fd75c
28 changed files with 242 additions and 62 deletions

View File

@ -68,6 +68,14 @@ Bugs fixed
* #1237: Fix footnotes not working in definition list in LaTeX * #1237: Fix footnotes not working in definition list in LaTeX
* #2168: Fix raw directive does not work for text writer * #2168: Fix raw directive does not work for text writer
* #2171: Fix cannot linkcheck url with unicode * #2171: Fix cannot linkcheck url with unicode
* #2182: LaTeX: support image file names with more than 1 dots
* #2189: Fix previous sibling link for first file in subdirectory uses last file, not intended previous from root toctree
* #2003: Fix decode error under python2 (only) when ``make linkcheck`` is run
* #2186: Fix LaTeX output of \mathbb in math
* #1480, #2188: LaTeX: Support math in section titles
* #2071: Fix same footnote in more than two section titles => LaTeX/PDF Bug
* #2040: Fix UnicodeDecodeError in sphinx-apidoc when author contains non-ascii characters
* #2193: Fix shutil.SameFileError if source directory and destination directory are same
Release 1.3.3 (released Dec 2, 2015) Release 1.3.3 (released Dec 2, 2015)

View File

@ -15,11 +15,7 @@ a highlighted version of the source code, and a link will be added to all object
descriptions that leads to the source code of the described object. A link back descriptions that leads to the source code of the described object. A link back
from the source to the description will also be inserted. from the source to the description will also be inserted.
There are currently no configuration values for this extension; you just need to There is an additional config value:
add ``'sphinx.ext.viewcode'`` to your :confval:`extensions` value for it to
work.
There is also an additional config value:
.. confval:: viewcode_import .. confval:: viewcode_import

View File

@ -20,6 +20,7 @@ import os
import sys import sys
import optparse import optparse
from os import path from os import path
from six import binary_type
from sphinx.util.osutil import walk from sphinx.util.osutil import walk
from sphinx import __display_version__ from sphinx import __display_version__
@ -369,6 +370,15 @@ Note: By default this script will not overwrite already created files.""")
mastertoctree = text, mastertoctree = text,
language = 'en', language = 'en',
) )
if isinstance(opts.header, binary_type):
d['project'] = d['project'].decode('utf-8')
if isinstance(opts.author, binary_type):
d['author'] = d['author'].decode('utf-8')
if isinstance(opts.version, binary_type):
d['version'] = d['version'].decode('utf-8')
if isinstance(opts.release, binary_type):
d['release'] = d['release'].decode('utf-8')
if not opts.dryrun: if not opts.dryrun:
qs.generate(d, silent=True, overwrite=opts.force) qs.generate(d, silent=True, overwrite=opts.force)
elif not opts.notoc: elif not opts.notoc:

View File

@ -95,6 +95,17 @@ def check_anchor(f, hash):
return parser.found return parser.found
def get_content_charset(f):
content_type = f.headers.get('content-type')
if content_type:
params = (p.strip() for p in content_type.split(';')[1:])
for param in params:
if param.startswith('charset='):
return param[8:]
return None
class CheckExternalLinksBuilder(Builder): class CheckExternalLinksBuilder(Builder):
""" """
Checks for broken external links. Checks for broken external links.
@ -165,6 +176,8 @@ class CheckExternalLinksBuilder(Builder):
encoding = 'utf-8' encoding = 'utf-8'
if hasattr(f.headers, 'get_content_charset'): if hasattr(f.headers, 'get_content_charset'):
encoding = f.headers.get_content_charset() or encoding encoding = f.headers.get_content_charset() or encoding
else:
encoding = get_content_charset(f) or encoding
found = check_anchor(TextIOWrapper(f, encoding), unquote(hash)) found = check_anchor(TextIOWrapper(f, encoding), unquote(hash))
f.close() f.close()

View File

@ -144,6 +144,10 @@ def main(argv):
file=sys.stderr) file=sys.stderr)
return 1 return 1
outdir = abspath(args[1]) outdir = abspath(args[1])
if srcdir == outdir:
print('Error: source directory and destination directory are same.',
file=sys.stderr)
return 1
except IndexError: except IndexError:
parser.print_help() parser.print_help()
return 1 return 1

View File

@ -23,8 +23,8 @@ from os import path
from glob import glob from glob import glob
from itertools import groupby from itertools import groupby
from six import iteritems, itervalues, text_type, class_types, string_types from six import iteritems, itervalues, text_type, class_types, string_types, next
from six.moves import cPickle as pickle, zip from six.moves import cPickle as pickle
from docutils import nodes from docutils import nodes
from docutils.io import FileInput, NullOutput from docutils.io import FileInput, NullOutput
from docutils.core import Publisher from docutils.core import Publisher
@ -1951,54 +1951,31 @@ class BuildEnvironment:
for (key_, group) in groupby(newlist, keyfunc2)] for (key_, group) in groupby(newlist, keyfunc2)]
def collect_relations(self): def collect_relations(self):
relations = {} traversed = set()
getinc = self.toctree_includes.get
def traverse_toctree(parent, docname):
# traverse toctree by pre-order
yield parent, docname
traversed.add(docname)
for child in (self.toctree_includes.get(docname) or []):
for subparent, subdocname in traverse_toctree(docname, child):
if subdocname not in traversed:
yield subparent, subdocname
traversed.add(subdocname)
relations = {}
docnames = traverse_toctree(None, self.config.master_doc)
prevdoc = None
parent, docname = next(docnames)
for nextparent, nextdoc in docnames:
relations[docname] = [parent, prevdoc, nextdoc]
prevdoc = docname
docname = nextdoc
parent = nextparent
relations[docname] = [parent, prevdoc, None]
def collect(parents, parents_set, docname, previous, next):
# circular relationship?
if docname in parents_set:
# we will warn about this in resolve_toctree()
return
includes = getinc(docname)
# previous
if not previous:
# if no previous sibling, go to parent
previous = parents[0][0]
else:
# else, go to previous sibling, or if it has children, to
# the last of its children, or if that has children, to the
# last of those, and so forth
while 1:
previncs = getinc(previous)
if previncs:
previous = previncs[-1]
else:
break
# next
if includes:
# if it has children, go to first of them
next = includes[0]
elif next:
# else, if next sibling, go to it
pass
else:
# else, go to the next sibling of the parent, if present,
# else the grandparent's sibling, if present, and so forth
for parname, parindex in parents:
parincs = getinc(parname)
if parincs and parindex + 1 < len(parincs):
next = parincs[parindex+1]
break
# else it will stay None
# same for children
if includes:
for subindex, args in enumerate(zip(includes,
[None] + includes,
includes[1:] + [None])):
collect([(docname, subindex)] + parents,
parents_set.union([docname]), *args)
relations[docname] = [parents[0][0], previous, next]
collect([(None, 0)], set(), self.config.master_doc, None, None)
return relations return relations
def check_consistency(self): def check_consistency(self):

View File

@ -56,6 +56,17 @@ def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
return [node], [] return [node], []
def is_in_section_title(node):
"""Determine whether the node is in a section title"""
from sphinx.util.nodes import traverse_parent
for ancestor in traverse_parent(node):
if isinstance(ancestor, nodes.title) and \
isinstance(ancestor.parent, nodes.section):
return True
return False
class MathDirective(Directive): class MathDirective(Directive):
has_content = True has_content = True
@ -91,7 +102,12 @@ class MathDirective(Directive):
def latex_visit_math(self, node): def latex_visit_math(self, node):
self.body.append('\\(' + node['latex'] + '\\)') if is_in_section_title(node):
protect = r'\protect'
else:
protect = ''
equation = protect + r'\(' + node['latex'] + protect + r'\)'
self.body.append(equation)
raise nodes.SkipNode raise nodes.SkipNode
@ -214,3 +230,4 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
app.add_role('eq', eq_role) app.add_role('eq', eq_role)
app.add_directive('math', MathDirective) app.add_directive('math', MathDirective)
app.connect('doctree-resolved', number_equations) app.connect('doctree-resolved', number_equations)
app.add_latex_package('amsfonts')

View File

@ -1063,6 +1063,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.remember_multirowcol[self.table.col] = node.get('morecols') self.remember_multirowcol[self.table.col] = node.get('morecols')
self.table.col += node.get('morecols') self.table.col += node.get('morecols')
if isinstance(node.parent.parent, nodes.thead): if isinstance(node.parent.parent, nodes.thead):
if len(node) == 1 and isinstance(node[0], nodes.paragraph) and node.astext() == '':
pass
else:
self.body.append('\\textsf{\\relax ') self.body.append('\\textsf{\\relax ')
context += '}' context += '}'
while self.remember_multirow.get(self.table.col + 1, 0): while self.remember_multirow.get(self.table.col + 1, 0):
@ -1661,7 +1664,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# if a footnote has been inserted once, it shouldn't be repeated # if a footnote has been inserted once, it shouldn't be repeated
# by the next reference # by the next reference
if used: if used:
if self.table or self.in_term: if self.table or self.in_term or self.in_title:
self.body.append('\\protect\\footnotemark[%s]' % num) self.body.append('\\protect\\footnotemark[%s]' % num)
else: else:
self.body.append('\\footnotemark[%s]' % num) self.body.append('\\footnotemark[%s]' % num)

View File

@ -187,6 +187,15 @@ Tables
| 2 | Empty cells: | | | 2 | Empty cells: | |
+----+----------------+----+ +----+----------------+----+
.. table:: empty cell in table header
===== ======
\
===== ======
1 2
3 4
===== ======
Tables with multirow and multicol: Tables with multirow and multicol:
.. only:: latex .. only:: latex

View File

@ -1,5 +1,5 @@
Test math extensions Test math extensions :math:`E = m c^2`
==================== ======================================
This is inline math: :math:`a^2 + b^2 = c^2`. This is inline math: :math:`a^2 + b^2 = c^2`.
@ -19,4 +19,8 @@ This is inline math: :math:`a^2 + b^2 = c^2`.
e^{ix} = \cos x + i\sin x e^{ix} = \cos x + i\sin x
.. math::
n \in \mathbb N
Referencing equation :eq:`foo`. Referencing equation :eq:`foo`.

View File

@ -33,6 +33,9 @@ The section with a reference to [AuthorYear]_
.. [1] Second .. [1] Second
.. [#] Third .. [#] Third
The section with a reference to [1]_
=====================================
`URL in term <http://sphinx-doc.org/>`_ `URL in term <http://sphinx-doc.org/>`_
Description Description Description ... Description Description Description ...

View File

@ -0,0 +1,4 @@
Bar-1
=====
bar

View File

@ -0,0 +1,4 @@
Bar-2
=====
bar

View File

@ -0,0 +1,4 @@
Bar-3
=====
bar

View File

@ -0,0 +1,4 @@
Bar-4
=====
bar

View File

@ -0,0 +1,8 @@
Bar
===
.. toctree::
:glob:
*
bar_4/index

View File

@ -0,0 +1,4 @@
Baz
===
baz

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
master_doc = 'index'
html_theme = 'classic'

View File

@ -0,0 +1,4 @@
Foo
===
foo

View File

@ -0,0 +1,11 @@
test-toctree-glob
=================
.. toctree::
:glob:
foo
bar/index
bar/*
baz
qux/index

View File

@ -0,0 +1,4 @@
Quux
====
quux

View File

@ -0,0 +1,8 @@
Qux
===
.. toctree::
:glob:
:hidden:
*

View File

@ -0,0 +1,4 @@
Qux-1
=====
qux

View File

@ -0,0 +1,4 @@
Qux-1
=====
qux

View File

@ -12,6 +12,7 @@
from __future__ import print_function from __future__ import print_function
import sys import sys
from six import PY2
from sphinx import apidoc from sphinx import apidoc
@ -40,3 +41,43 @@ def test_simple(tempdir):
assert_build() assert_build()
finally: finally:
sys.path.remove(codedir) sys.path.remove(codedir)
@with_tempdir
def test_multibyte_parameters(tempdir):
codedir = rootdir / 'root'
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir,
'--doc-project', u'プロジェクト名'.encode('utf-8'),
'--doc-author', u'著者名'.encode('utf-8'),
'--doc-version', u'バージョン'.encode('utf-8'),
'--doc-release', u'リリース'.encode('utf-8')]
apidoc.main(args)
assert (outdir / 'conf.py').isfile()
assert (outdir / 'autodoc_fodder.rst').isfile()
assert (outdir / 'index.rst').isfile()
conf_py = (outdir / 'conf.py').text()
if PY2:
assert u"project = u'プロジェクト名'" in conf_py
assert u"author = u'著者名'" in conf_py
assert u"version = u'バージョン'" in conf_py
assert u"release = u'リリース'" in conf_py
else:
assert u"project = 'プロジェクト名'" in conf_py
assert u"author = '著者名'" in conf_py
assert u"version = 'バージョン'" in conf_py
assert u"release = 'リリース'" in conf_py
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)

View File

@ -90,7 +90,6 @@ def test_latex(app, status, warning):
if p.returncode != 0: if p.returncode != 0:
print(stdout) print(stdout)
print(stderr) print(stderr)
del app.cleanup_trees[:]
assert False, 'latex exited with return code %s' % p.returncode assert False, 'latex exited with return code %s' % p.returncode
finally: finally:
os.chdir(cwd) os.chdir(cwd)
@ -335,6 +334,7 @@ def test_reference_in_caption(app, status, warning):
assert '\\chapter{The section with a reference to {[}AuthorYear{]}}' in result assert '\\chapter{The section with a reference to {[}AuthorYear{]}}' in result
assert '\\caption{The table title with a reference to {[}AuthorYear{]}}' in result assert '\\caption{The table title with a reference to {[}AuthorYear{]}}' in result
assert '\\paragraph{The rubric title with a reference to {[}AuthorYear{]}}' in result assert '\\paragraph{The rubric title with a reference to {[}AuthorYear{]}}' in result
assert '\\chapter{The section with a reference to \\protect\\footnotemark[1]}' in result
@with_app(buildername='latex', testroot='footnotes', @with_app(buildername='latex', testroot='footnotes',

View File

@ -58,7 +58,6 @@ def test_texinfo(app, status, warning):
if retcode != 0: if retcode != 0:
print(stdout) print(stdout)
print(stderr) print(stderr)
del app.cleanup_trees[:]
assert False, 'makeinfo exited with return code %s' % retcode assert False, 'makeinfo exited with return code %s' % retcode
finally: finally:
os.chdir(cwd) os.chdir(cwd)

29
tests/test_toctree.py Normal file
View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
"""
test_toctree
~~~~~~~~~~~~
Test the HTML builder and check output against XPath.
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from util import with_app
@with_app(testroot='toctree-glob')
def test_relations(app, status, warning):
app.builder.build_all()
assert app.builder.relations['index'] == [None, None, 'foo']
assert app.builder.relations['foo'] == ['index', 'index', 'bar/index']
assert app.builder.relations['bar/index'] == ['index', 'foo', 'bar/bar_1']
assert app.builder.relations['bar/bar_1'] == ['bar/index', 'bar/index', 'bar/bar_2']
assert app.builder.relations['bar/bar_2'] == ['bar/index', 'bar/bar_1', 'bar/bar_3']
assert app.builder.relations['bar/bar_3'] == ['bar/index', 'bar/bar_2', 'bar/bar_4/index']
assert app.builder.relations['bar/bar_4/index'] == ['bar/index', 'bar/bar_3', 'baz']
assert app.builder.relations['baz'] == ['index', 'bar/bar_4/index', 'qux/index']
assert app.builder.relations['qux/index'] == ['index', 'baz', 'qux/qux_1']
assert app.builder.relations['qux/qux_1'] == ['qux/index', 'qux/index', 'qux/qux_2']
assert app.builder.relations['qux/qux_2'] == ['qux/index', 'qux/qux_1', None]
assert 'quux' not in app.builder.relations