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
* #2168: Fix raw directive does not work for text writer
* #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)

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
from the source to the description will also be inserted.
There are currently no configuration values for this extension; you just need to
add ``'sphinx.ext.viewcode'`` to your :confval:`extensions` value for it to
work.
There is also an additional config value:
There is an additional config value:
.. confval:: viewcode_import

View File

@ -20,6 +20,7 @@ import os
import sys
import optparse
from os import path
from six import binary_type
from sphinx.util.osutil import walk
from sphinx import __display_version__
@ -369,6 +370,15 @@ Note: By default this script will not overwrite already created files.""")
mastertoctree = text,
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:
qs.generate(d, silent=True, overwrite=opts.force)
elif not opts.notoc:

View File

@ -95,6 +95,17 @@ def check_anchor(f, hash):
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):
"""
Checks for broken external links.
@ -165,6 +176,8 @@ class CheckExternalLinksBuilder(Builder):
encoding = 'utf-8'
if hasattr(f.headers, 'get_content_charset'):
encoding = f.headers.get_content_charset() or encoding
else:
encoding = get_content_charset(f) or encoding
found = check_anchor(TextIOWrapper(f, encoding), unquote(hash))
f.close()

View File

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

View File

@ -23,8 +23,8 @@ from os import path
from glob import glob
from itertools import groupby
from six import iteritems, itervalues, text_type, class_types, string_types
from six.moves import cPickle as pickle, zip
from six import iteritems, itervalues, text_type, class_types, string_types, next
from six.moves import cPickle as pickle
from docutils import nodes
from docutils.io import FileInput, NullOutput
from docutils.core import Publisher
@ -1951,54 +1951,31 @@ class BuildEnvironment:
for (key_, group) in groupby(newlist, keyfunc2)]
def collect_relations(self):
relations = {}
getinc = self.toctree_includes.get
traversed = set()
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
def check_consistency(self):

View File

@ -56,6 +56,17 @@ def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
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):
has_content = True
@ -91,7 +102,12 @@ class MathDirective(Directive):
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
@ -214,3 +230,4 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
app.add_role('eq', eq_role)
app.add_directive('math', MathDirective)
app.connect('doctree-resolved', number_equations)
app.add_latex_package('amsfonts')

View File

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

View File

@ -187,6 +187,15 @@ Tables
| 2 | Empty cells: | |
+----+----------------+----+
.. table:: empty cell in table header
===== ======
\
===== ======
1 2
3 4
===== ======
Tables with multirow and multicol:
.. 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`.
@ -19,4 +19,8 @@ This is inline math: :math:`a^2 + b^2 = c^2`.
e^{ix} = \cos x + i\sin x
.. math::
n \in \mathbb N
Referencing equation :eq:`foo`.

View File

@ -33,6 +33,9 @@ The section with a reference to [AuthorYear]_
.. [1] Second
.. [#] Third
The section with a reference to [1]_
=====================================
`URL in term <http://sphinx-doc.org/>`_
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
import sys
from six import PY2
from sphinx import apidoc
@ -40,3 +41,43 @@ def test_simple(tempdir):
assert_build()
finally:
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:
print(stdout)
print(stderr)
del app.cleanup_trees[:]
assert False, 'latex exited with return code %s' % p.returncode
finally:
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 '\\caption{The table 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',

View File

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