mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '1.6-release'
This commit is contained in:
commit
5ca651f220
10
CHANGES
10
CHANGES
@ -58,16 +58,22 @@ Incompatible changes
|
||||
|
||||
* LaTeX package ``eqparbox`` is not used and not loaded by Sphinx anymore
|
||||
* LaTeX package ``multirow`` is not used and not loaded by Sphinx anymore
|
||||
* Add line numbers to citation data in std domain
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* LaTeX ``\sphinxstylethead`` is deprecated at 1.6 and will be removed at 1.7.
|
||||
Please move customization into new macro ``\sphinxstyletheadfamily``.
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
* ``LATEXMKOPTS`` variable for the Makefile in ``$BUILDDIR/latex`` to pass
|
||||
options to ``latexmk`` when executing ``make latexpdf``. Default is ``-f``.
|
||||
(refs #3695)
|
||||
* Add a new event `env-check-consistency` to check consistency to extensions
|
||||
* Add `Domain.check_consistency()` to check consistency
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
@ -81,6 +87,8 @@ Bugs fixed
|
||||
* #3683: Failed to build document if builder.css_file.insert() is called
|
||||
* #3714: viewcode extension not taking ``highlight_code='none'`` in account
|
||||
* #3698: Moving :doc: to std domain broke backwards compatibility
|
||||
* #3633: misdetect unreferenced citations
|
||||
* LaTeX tables do not allow multiple paragraphs in a header cell
|
||||
|
||||
Testing
|
||||
--------
|
||||
@ -307,6 +315,8 @@ Bugs fixed
|
||||
:confval:`html_compact_lists` is True.
|
||||
* #3685: AttributeError when using 3rd party domains
|
||||
* #3702: LaTeX writer styles figure legends with a hard-coded ``\small``
|
||||
* #3708: LaTeX writer allows irc scheme
|
||||
* #3717: Stop enforcing that favicon's must be .ico
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -590,6 +590,15 @@ handlers to the events. Example:
|
||||
.. versionchanged:: 1.3
|
||||
The handlers' return value is now used.
|
||||
|
||||
.. event:: env-check-consistency (env)
|
||||
|
||||
Emmited when Consistency checks phase. You can check consistency of
|
||||
metadata for whole of documents.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
As a **experimental** event
|
||||
|
||||
.. event:: html-collect-pages (app)
|
||||
|
||||
Emitted when the HTML builder is starting to write non-document pages. You
|
||||
|
@ -9,7 +9,6 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import codecs
|
||||
@ -400,8 +399,6 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
favicon = self.config.html_favicon and \
|
||||
path.basename(self.config.html_favicon) or ''
|
||||
if favicon and os.path.splitext(favicon)[1] != '.ico':
|
||||
logger.warning('html_favicon is not an .ico file')
|
||||
|
||||
if not isinstance(self.config.html_use_opensearch, string_types):
|
||||
logger.warning('html_use_opensearch config value must now be a string')
|
||||
|
@ -240,6 +240,11 @@ class Domain(object):
|
||||
"""Process a document after it is read by the environment."""
|
||||
pass
|
||||
|
||||
def check_consistency(self):
|
||||
# type: () -> None
|
||||
"""Do consistency checks (**experimental**)."""
|
||||
pass
|
||||
|
||||
def process_field_xref(self, pnode):
|
||||
# type: (nodes.Node) -> None
|
||||
"""Process a pending xref created in a doc field.
|
||||
|
@ -490,15 +490,16 @@ class StandardDomain(Domain):
|
||||
} # type: Dict[unicode, Union[RoleFunction, XRefRole]]
|
||||
|
||||
initial_data = {
|
||||
'progoptions': {}, # (program, name) -> docname, labelid
|
||||
'objects': {}, # (type, name) -> docname, labelid
|
||||
'citations': {}, # name -> docname, labelid
|
||||
'labels': { # labelname -> docname, labelid, sectionname
|
||||
'progoptions': {}, # (program, name) -> docname, labelid
|
||||
'objects': {}, # (type, name) -> docname, labelid
|
||||
'citations': {}, # name -> docname, labelid, lineno
|
||||
'citation_refs': {}, # name -> list of docnames
|
||||
'labels': { # labelname -> docname, labelid, sectionname
|
||||
'genindex': ('genindex', '', l_('Index')),
|
||||
'modindex': ('py-modindex', '', l_('Module Index')),
|
||||
'search': ('search', '', l_('Search Page')),
|
||||
},
|
||||
'anonlabels': { # labelname -> docname, labelid
|
||||
'anonlabels': { # labelname -> docname, labelid
|
||||
'genindex': ('genindex', ''),
|
||||
'modindex': ('py-modindex', ''),
|
||||
'search': ('search', ''),
|
||||
@ -530,9 +531,14 @@ class StandardDomain(Domain):
|
||||
for key, (fn, _l) in list(self.data['objects'].items()):
|
||||
if fn == docname:
|
||||
del self.data['objects'][key]
|
||||
for key, (fn, _l) in list(self.data['citations'].items()):
|
||||
for key, (fn, _l, lineno) in list(self.data['citations'].items()):
|
||||
if fn == docname:
|
||||
del self.data['citations'][key]
|
||||
for key, docnames in list(self.data['citation_refs'].items()):
|
||||
if docnames == [docname]:
|
||||
del self.data['citation_refs'][key]
|
||||
elif docname in docnames:
|
||||
docnames.pop(docname)
|
||||
for key, (fn, _l, _l) in list(self.data['labels'].items()):
|
||||
if fn == docname:
|
||||
del self.data['labels'][key]
|
||||
@ -552,6 +558,11 @@ class StandardDomain(Domain):
|
||||
for key, data in otherdata['citations'].items():
|
||||
if data[0] in docnames:
|
||||
self.data['citations'][key] = data
|
||||
for key, data in otherdata['citation_refs'].items():
|
||||
citation_refs = self.data['citation_refs'].setdefault(key, [])
|
||||
for docname in data:
|
||||
if docname in docnames:
|
||||
citation_refs.append(docname)
|
||||
for key, data in otherdata['labels'].items():
|
||||
if data[0] in docnames:
|
||||
self.data['labels'][key] = data
|
||||
@ -562,6 +573,7 @@ class StandardDomain(Domain):
|
||||
def process_doc(self, env, docname, document):
|
||||
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
||||
self.note_citations(env, docname, document)
|
||||
self.note_citation_refs(env, docname, document)
|
||||
self.note_labels(env, docname, document)
|
||||
|
||||
def note_citations(self, env, docname, document):
|
||||
@ -572,7 +584,13 @@ class StandardDomain(Domain):
|
||||
path = env.doc2path(self.data['citations'][label][0])
|
||||
logger.warning('duplicate citation %s, other instance in %s', label, path,
|
||||
location=node, type='ref', subtype='citation')
|
||||
self.data['citations'][label] = (docname, node['ids'][0])
|
||||
self.data['citations'][label] = (docname, node['ids'][0], node.line)
|
||||
|
||||
def note_citation_refs(self, env, docname, document):
|
||||
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
||||
for name, refs in iteritems(document.citation_refs):
|
||||
citation_refs = self.data['citation_refs'].setdefault(name, [])
|
||||
citation_refs.append(docname)
|
||||
|
||||
def note_labels(self, env, docname, document):
|
||||
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
||||
@ -614,6 +632,14 @@ class StandardDomain(Domain):
|
||||
continue
|
||||
labels[name] = docname, labelid, sectname
|
||||
|
||||
def check_consistency(self):
|
||||
# type: () -> None
|
||||
for name, (docname, labelid, lineno) in iteritems(self.data['citations']):
|
||||
if labelid not in self.data['citation_refs']:
|
||||
logger.warning('Citation [%s] is not referenced.', name,
|
||||
type='ref', subtype='citation',
|
||||
location=(docname, lineno))
|
||||
|
||||
def build_reference_node(self, fromdocname, builder, docname, labelid,
|
||||
sectname, rolename, **options):
|
||||
# type: (unicode, Builder, unicode, unicode, unicode, unicode, Any) -> nodes.Node
|
||||
@ -788,7 +814,7 @@ class StandardDomain(Domain):
|
||||
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA
|
||||
from sphinx.environment import NoUri
|
||||
|
||||
docname, labelid = self.data['citations'].get(target, ('', ''))
|
||||
docname, labelid, lineno = self.data['citations'].get(target, ('', '', 0))
|
||||
if not docname:
|
||||
if 'ids' in node:
|
||||
# remove ids attribute that annotated at
|
||||
|
@ -78,7 +78,7 @@ default_settings = {
|
||||
# or changed to properly invalidate pickle files.
|
||||
#
|
||||
# NOTE: increase base version by 2 to have distinct numbers for Py2 and 3
|
||||
ENV_VERSION = 51 + (sys.version_info[0] - 2)
|
||||
ENV_VERSION = 52 + (sys.version_info[0] - 2)
|
||||
|
||||
|
||||
dummy_reporter = Reporter('', 4, 4)
|
||||
@ -990,3 +990,8 @@ class BuildEnvironment(object):
|
||||
continue
|
||||
logger.warning('document isn\'t included in any toctree',
|
||||
location=docname)
|
||||
|
||||
# call check-consistency for all extensions
|
||||
for domain in self.domains.values():
|
||||
domain.check_consistency()
|
||||
self.app.emit('env-check-consistency', self)
|
||||
|
@ -31,6 +31,7 @@ core_events = {
|
||||
'env-get-updated': 'env',
|
||||
'env-purge-doc': 'env, docname',
|
||||
'env-before-read-docs': 'env, docnames',
|
||||
'env-check-consistency': 'env',
|
||||
'source-read': 'docname, source text',
|
||||
'doctree-read': 'the doctree before being pickled',
|
||||
'env-merge-info': 'env, read docnames, other env instance',
|
||||
|
@ -1359,7 +1359,17 @@
|
||||
\let\sphinxstylesidebartitle\sphinxstyletopictitle
|
||||
\protected\def\sphinxstyleothertitle {\textbf}
|
||||
\protected\def\sphinxstylesidebarsubtitle #1{~\\\textbf{#1} \smallskip}
|
||||
\protected\def\sphinxstylethead {\textsf}
|
||||
% \text.. commands do not allow multiple paragraphs
|
||||
\let\sphinxstylethead\empty
|
||||
\protected\def\sphinxstyletheadfamily {\ifx\sphinxstylethead\empty\sffamily\fi}
|
||||
\AtBeginDocument{\ifx\sphinxstylethead\empty\else
|
||||
\sphinxdeprecationwarning{\sphinxstylethead}{1.6}{1.7}{%
|
||||
\string\sphinxstyletheadfamily\space replaces it
|
||||
(it defaults to \string\sffamily) to allow use^^J
|
||||
with multiple paragraphs. Backwards compatibility is maintained, but please^^J
|
||||
move customization into \string\sphinxstyletheadfamily\space
|
||||
in time for 1.7.^^J
|
||||
And if you do it now, you will spare yourself this warning!}\fi}
|
||||
\protected\def\sphinxstyleemphasis {\emph}
|
||||
\protected\def\sphinxstyleliteralemphasis#1{\emph{\sphinxcode{#1}}}
|
||||
\protected\def\sphinxstylestrong {\textbf}
|
||||
|
@ -296,12 +296,6 @@ class UnreferencedFootnotesDetector(SphinxTransform):
|
||||
type='ref', subtype='footnote',
|
||||
location=node)
|
||||
|
||||
for node in self.document.citations:
|
||||
if node['names'][0] not in self.document.citation_refs:
|
||||
logger.warning('Citation [%s] is not referenced.', node['names'][0],
|
||||
type='ref', subtype='citation',
|
||||
location=node)
|
||||
|
||||
|
||||
class FilterSystemMessages(SphinxTransform):
|
||||
"""Filter system messages from a doctree."""
|
||||
|
@ -1446,7 +1446,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
if len(node) == 1 and isinstance(node[0], nodes.paragraph) and node.astext() == '':
|
||||
pass
|
||||
else:
|
||||
self.body.append('\\sphinxstylethead{\\relax ')
|
||||
self.body.append('\\sphinxstylethead{\\sphinxstyletheadfamily ')
|
||||
context = '\\unskip}\\relax ' + context
|
||||
if self.needs_linetrimming:
|
||||
self.pushbody([])
|
||||
@ -2003,16 +2003,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
uri = '%' + self.curfilestack[-1] + '#' + node['refid']
|
||||
if self.in_title or not uri:
|
||||
self.context.append('')
|
||||
elif uri.startswith(URI_SCHEMES):
|
||||
if len(node) == 1 and uri == node[0]:
|
||||
if node.get('nolinkurl'):
|
||||
self.body.append('\\sphinxnolinkurl{%s}' % self.encode_uri(uri))
|
||||
else:
|
||||
self.body.append('\\sphinxurl{%s}' % self.encode_uri(uri))
|
||||
raise nodes.SkipNode
|
||||
else:
|
||||
self.body.append('\\sphinxhref{%s}{' % self.encode_uri(uri))
|
||||
self.context.append('}')
|
||||
elif uri.startswith('#'):
|
||||
# references to labels in the same document
|
||||
id = self.curfilestack[-1] + ':' + uri[1:]
|
||||
@ -2047,9 +2037,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
else:
|
||||
self.context.append('}}}')
|
||||
else:
|
||||
logger.warning('unusable reference target found: %s', uri,
|
||||
location=(self.curfilestack[-1], node.line))
|
||||
self.context.append('')
|
||||
if len(node) == 1 and uri == node[0]:
|
||||
if node.get('nolinkurl'):
|
||||
self.body.append('\\sphinxnolinkurl{%s}' % self.encode_uri(uri))
|
||||
else:
|
||||
self.body.append('\\sphinxurl{%s}' % self.encode_uri(uri))
|
||||
raise nodes.SkipNode
|
||||
else:
|
||||
self.body.append('\\sphinxhref{%s}{' % self.encode_uri(uri))
|
||||
self.context.append('}')
|
||||
|
||||
def depart_reference(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
|
@ -858,8 +858,9 @@ def test_latex_table_tabulars(app, status, warning):
|
||||
assert ('\\begin{savenotes}\\sphinxattablestart\n\\centering\n'
|
||||
'\\begin{tabulary}{\\linewidth}[t]{|T|T|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax' in table)
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader2\n\\unskip}\\relax'
|
||||
in table)
|
||||
assert ('\\hline\ncell1-1\n&\ncell1-2\n\\\\' in table)
|
||||
assert ('\\hline\ncell2-1\n&\ncell2-2\n\\\\' in table)
|
||||
assert ('\\hline\ncell3-1\n&\ncell3-2\n\\\\' in table)
|
||||
@ -930,15 +931,15 @@ def test_latex_table_longtable(app, status, warning):
|
||||
assert ('\\begin{savenotes}\\sphinxatlongtablestart'
|
||||
'\\begin{longtable}{|l|l|}\n\\hline' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax \\\\\n'
|
||||
'\\hline\n\\endfirsthead' in table)
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader2\n\\unskip}\\relax '
|
||||
'\\\\\n\\hline\n\\endfirsthead' in table)
|
||||
assert ('\\multicolumn{2}{c}%\n'
|
||||
'{\\makebox[0pt]{\\sphinxtablecontinued{\\tablename\\ \\thetable{} -- '
|
||||
'continued from previous page}}}\\\\\n\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax \\\\\n'
|
||||
'\\hline\n\\endhead' in table)
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader2\n\\unskip}\\relax '
|
||||
'\\\\\n\\hline\n\\endhead' in table)
|
||||
assert ('\\hline\n\\multicolumn{2}{r}'
|
||||
'{\\makebox[0pt][r]{\\sphinxtablecontinued{Continued on next page}}}\\\\\n'
|
||||
'\\endfoot\n\n\\endlastfoot' in table)
|
||||
@ -995,9 +996,10 @@ def test_latex_table_complex_tables(app, status, warning):
|
||||
table = tables['grid table']
|
||||
assert ('\\begin{tabulary}{\\linewidth}[t]{|T|T|T|}' in table)
|
||||
assert ('\\hline\n'
|
||||
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\relax \nheader3\n\\unskip}\\relax \\\\' in table)
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader1\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader2\n\\unskip}\\relax &'
|
||||
'\\sphinxstylethead{\\sphinxstyletheadfamily \nheader3\n\\unskip}\\relax '
|
||||
'\\\\' in table)
|
||||
assert ('\\hline\ncell1-1\n&\\sphinxmultirow{2}{5}{%\n\\begin{varwidth}[t]'
|
||||
'{\\sphinxcolwidth{1}{3}}\n'
|
||||
'cell1-2\n\\par\n' in table)
|
||||
|
@ -25,6 +25,7 @@ def test_process_doc_handle_figure_caption():
|
||||
nametypes={'testname': True},
|
||||
nameids={'testname': 'testid'},
|
||||
ids={'testid': figure_node},
|
||||
citation_refs={},
|
||||
)
|
||||
document.traverse.return_value = []
|
||||
|
||||
@ -47,6 +48,7 @@ def test_process_doc_handle_table_title():
|
||||
nametypes={'testname': True},
|
||||
nameids={'testname': 'testid'},
|
||||
ids={'testid': table_node},
|
||||
citation_refs={},
|
||||
)
|
||||
document.traverse.return_value = []
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user