Merge branch 'master' into refactor_literalinclude

This commit is contained in:
Takeshi KOMIYA
2017-02-22 00:18:31 +09:00
25 changed files with 321 additions and 126 deletions

View File

@@ -6,10 +6,10 @@ cache:
- $HOME/.cache/pip
python:
- "pypy-5.4.1"
- "2.7"
- "3.4"
- "3.5"
- "3.6"
- "3.5"
- "3.4"
- "2.7"
- "nightly"
env:
global:
@@ -43,7 +43,8 @@ install:
- pip install -U pip setuptools
- pip install docutils==$DOCUTILS
- pip install -r test-reqs.txt
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy 'typed-ast<1.0'; fi
script:
- flake8
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make style-check test-async; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make style-check type-check test-async; fi
- if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi

View File

@@ -58,6 +58,8 @@ Features added
:confval:`suppress_warnings`.
* #3377: latex: Add support for Docutils 0.13 ``:align:`` option for tables
(but does not implement text flow around table).
* latex: footnotes from inside tables are hyperlinked (except from captions or
headers) (refs: #3422)
* Emit warning if over dedent has detected on ``literalinclude`` directive
(refs: #3416)
@@ -88,6 +90,10 @@ Deprecated
- ``BuildEnvironment.create_index()``
Please use ``sphinx.environment.adapters`` modules instead.
* The LaTeX package ``footnote.sty`` will not be loaded anymore at Sphinx 1.7,
as Sphinx's ``footnotehyper-sphinx.sty`` will define all macros rather than
patch them. In particular the ``\makesavenoteenv`` macro is not in use anymore
and its definition will be removed at Sphinx 1.7.
Release 1.5.3 (in development)
==============================
@@ -121,6 +127,7 @@ Bugs fixed
* #3410: return code in :confval:`release` breaks html search
* #3427: autodoc: memory addresses are not stripped on Windows
* #3428: xetex build tests fail due to fontspec v2.6 defining ``\strong``
* #3349: Result of ``IndexBuilder.load()`` is broken
Testing
--------

View File

@@ -1,6 +1,7 @@
[mypy]
python_version = 2.7
silent_imports = True
ignore_missing_imports = True
follow_imports = skip
fast_parser = True
incremental = True
check_untyped_defs = True

View File

@@ -60,11 +60,11 @@ if __version__.endswith('+'):
def main(argv=sys.argv):
# type: (List[str]) -> None
# type: (List[str]) -> int
if sys.argv[1:2] == ['-M']:
sys.exit(make_main(argv))
return make_main(argv)
else:
sys.exit(build_main(argv))
return build_main(argv)
def build_main(argv=sys.argv):
@@ -117,4 +117,4 @@ def make_main(argv=sys.argv):
if __name__ == '__main__':
sys.exit(main(sys.argv)) # type: ignore
sys.exit(main(sys.argv))

View File

@@ -4404,7 +4404,7 @@ class DefinitionParser(object):
templatePrefix = self._check_template_consistency(name, templatePrefix,
fullSpecShorthand=False)
res = ASTNamespace(name, templatePrefix)
res.objectType = 'namespace'
res.objectType = 'namespace' # type: ignore
return res
def parse_xref_object(self):
@@ -4417,7 +4417,7 @@ class DefinitionParser(object):
templatePrefix = self._check_template_consistency(name, templatePrefix,
fullSpecShorthand=True)
res = ASTNamespace(name, templatePrefix)
res.objectType = 'xref'
res.objectType = 'xref' # type: ignore
return res
@@ -4729,7 +4729,7 @@ class CPPNamespacePushObject(Directive):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
return
return []
parser = DefinitionParser(self.arguments[0], self, env.config)
try:
ast = parser.parse_namespace_object()

View File

@@ -52,6 +52,7 @@ add_documenter(InstanceAttributeDocumenter)
if False:
# For type annotation
from typing import Any, Callable, Tuple, List # NOQA
from jinja2 import BaseLoader # NOQA
from sphinx import addnodes # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@@ -118,6 +119,8 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
template_dirs = None # type: List[unicode]
template_dirs = [os.path.join(package_dir, 'ext',
'autosummary', 'templates')]
template_loader = None # type: BaseLoader
if builder is not None:
# allow the user to override the templates
template_loader = BuiltinTemplateLoader()
@@ -125,7 +128,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
else:
if template_dir:
template_dirs.insert(0, template_dir)
template_loader = FileSystemLoader(template_dirs)
template_loader = FileSystemLoader(template_dirs) # type: ignore
template_env = SandboxedEnvironment(loader=template_loader)
# read
@@ -221,7 +224,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
ns['underline'] = len(name) * '='
rendered = template.render(**ns)
f.write(rendered)
f.write(rendered) # type: ignore
# descend recursively to new files
if new_files:

View File

@@ -307,10 +307,9 @@ class DocTestBuilder(Builder):
self.outfile = None # type: IO
self.outfile = codecs.open(path.join(self.outdir, 'output.txt'), # type: ignore
'w', encoding='utf-8')
self.outfile.write('''\
Results of doctest builder run on %s
==================================%s
''' % (date, '=' * len(date)))
self.outfile.write(('Results of doctest builder run on %s\n' # type: ignore
'==================================%s\n') %
(date, '=' * len(date)))
def _out(self, text):
# type: (unicode) -> None

View File

@@ -25,8 +25,8 @@ from sphinx.util.osutil import mtimes_of_files
if False:
# For type annotation
from typing import Any, Callable, Iterator, Tuple # NOQA
from jinja2.environment import Environment # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.themes import Theme # NOQA
@@ -101,7 +101,7 @@ class SphinxFileSystemLoader(FileSystemLoader):
"""
def get_source(self, environment, template):
# type: (BuildEnvironment, unicode) -> Tuple[unicode, unicode, Callable]
# type: (Environment, unicode) -> Tuple[unicode, unicode, Callable]
for searchpath in self.searchpath:
filename = path.join(searchpath, template)
f = open_if_exists(filename)
@@ -169,11 +169,10 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
self.environment.globals['accesskey'] = contextfunction(accesskey)
self.environment.globals['idgen'] = idgen
if use_i18n:
self.environment.install_gettext_translations(
builder.app.translator)
self.environment.install_gettext_translations(builder.app.translator) # type: ignore # NOQA
def render(self, template, context):
# type: (unicode, Dict) -> None
def render(self, template, context): # type: ignore
# type: (unicode, Dict) -> unicode
return self.environment.get_template(template).render(context)
def render_string(self, source, context):
@@ -187,7 +186,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
# Loader interface
def get_source(self, environment, template):
# type: (BuildEnvironment, unicode) -> Tuple[unicode, unicode, Callable]
# type: (Environment, unicode) -> Tuple[unicode, unicode, Callable]
loaders = self.loaders
# exclamation mark starts search from theme
if template.startswith('!'):

View File

@@ -293,7 +293,8 @@ class IndexBuilder(object):
if not isinstance(frozen, dict) or \
frozen.get('envversion') != self.env.version:
raise ValueError('old format')
index2fn = frozen['filenames']
index2fn = frozen['docnames']
self._filenames = dict(zip(index2fn, frozen['filenames']))
self._titles = dict(zip(index2fn, frozen['titles']))
def load_terms(mapping):
@@ -387,18 +388,21 @@ class IndexBuilder(object):
# type: () -> unicode
return "%s (code: %s)" % (self.lang.language_name, self.lang.lang)
def prune(self, filenames):
def prune(self, docnames):
# type: (Iterable[unicode]) -> None
"""Remove data for all filenames not in the list."""
"""Remove data for all docnames not in the list."""
new_titles = {}
for filename in filenames:
if filename in self._titles:
new_titles[filename] = self._titles[filename]
new_filenames = {}
for docname in docnames:
if docname in self._titles:
new_titles[docname] = self._titles[docname]
new_filenames[docname] = self._filenames[docname]
self._titles = new_titles
self._filenames = new_filenames
for wordnames in itervalues(self._mapping):
wordnames.intersection_update(filenames)
wordnames.intersection_update(docnames)
for wordnames in itervalues(self._title_mapping):
wordnames.intersection_update(filenames)
wordnames.intersection_update(docnames)
def feed(self, docname, filename, title, doctree):
# type: (unicode, unicode, unicode, nodes.Node) -> None

View File

@@ -1,4 +1,4 @@
\begin{longtable}
\begin{savenotes}\begin{longtable}
<%- if table.align == 'center' -%>
[c]
<%- elif table.align == 'left' -%>
@@ -26,6 +26,8 @@
\endfoot
\endlastfoot
<% if table.caption_footnotetexts -%>
<%= ''.join(table.caption_footnotetexts) %>
<% endif -%>
<%= ''.join(table.body) %>
\end{longtable}
\end{longtable}\end{savenotes}

View File

@@ -1,4 +1,4 @@
\begingroup
\begin{savenotes}
<% if table.align -%>
<%- if table.align == 'center' -%>
\centering
@@ -17,10 +17,13 @@
\begin{tabular}<%= table.get_colspec() -%>
\hline
<%= ''.join(table.header) %>
<%- if table.caption_footnotetexts -%>
<%= ''.join(table.caption_footnotetexts) -%>
<%- endif -%>
<%=- ''.join(table.body) %>
\end{tabular}
<%- if table.caption %>
\end{threeparttable}
<%- endif %>
\par
\endgroup
\end{savenotes}

View File

@@ -1,4 +1,4 @@
\begingroup
\begin{savenotes}
<% if table.align -%>
<%- if table.align == 'center' -%>
\centering
@@ -17,10 +17,13 @@
\begin{tabulary}{\linewidth}<%= table.get_colspec() -%>
\hline
<%= ''.join(table.header) %>
<%- if table.caption_footnotetexts -%>
<%= ''.join(table.caption_footnotetexts) -%>
<%- endif -%>
<%=- ''.join(table.body) %>
\end{tabulary}
<%- if table.caption %>
\end{threeparttable}
<%- endif %>
\par
\endgroup
\end{savenotes}

View File

@@ -1,6 +1,6 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{footnotehyper-sphinx}%
[2017/01/16 v1.5.2 hyperref aware footnote.sty for sphinx (JFB)]
[2017/02/15 v1.6 hyperref aware footnote.sty for sphinx (JFB)]
%%
%% Package: footnotehyper-sphinx
%% Version: based on footnotehyper.sty v0.9f (2016/10/03)
@@ -10,13 +10,14 @@
%% Differences from footnotehyper v0.9f (2016/10/03):
%% 1. hyperref is assumed in use (with default hyperfootnotes=true),
%% 2. no need to check if footnote.sty was loaded,
%% 3. a special tabulary compatibility layer added, (partial but enough for
%% Sphinx),
%% 4. \sphinxfootnotemark, and use of \spx@opt@BeforeFootnote from sphinx.sty.
%% Note: with \footnotemark[N]/\footnotetext[N] syntax, hyperref
%% does not insert an hyperlink. This is _not_ improved here.
%% 3. a tabulary compatibility layer added, (partial but enough for Sphinx),
%% 4. \sphinxfootnotemark, and use of \spx@opt@BeforeFootnote from sphinx.sty,
%% 5. use of \sphinxunactivateextrasandspace for parsed literals
%%
%% 6. macro \sphinxlongtablepatch
%% 7. some import from future footnotehyper v0.99 (\FNH@fn@fntext)
%% 8. deprecation of \makesavenoteenv.
%% Future version of footnotehyper will not load footnote.sty and it will define
%% all macros rather than adding hyperref awareness and fixing bugs.
\DeclareOption*{\PackageWarning{footnotehyper}{Option `\CurrentOption' is unknown}}%
\ProcessOptions\relax
\let\FNH@@makefntext\@makefntext\let\@makefntext\@firstofone
@@ -31,6 +32,7 @@
\let\fn@latex@@footnote \footnote % meaning of \footnote at end of preamble
\let\fn@latex@@footnotetext\footnotetext
\let\fn@fntext \FNH@hyper@fntext
\let\savenotes \FNH@savenotes
\let\spewnotes \FNH@hyper@spewnotes
\let\endsavenotes\spewnotes
\let\fn@endfntext\FNH@fixed@endfntext
@@ -39,13 +41,33 @@
\let\endfootnote\fn@endfntext
\let\endfootnotetext\endfootnote
}%
\def\FNH@hyper@fntext {%
%% patch \savenotes for good functioning of \footnotetext[N]{..} syntax even
%% though Sphinx now uses only environment form. In future, will simply copy
%% over full footnotehyper v0.99.
\toks@\expandafter\expandafter\expandafter{\expandafter\@gobble\savenotes}%
\edef\FNH@savenotes{\begingroup
\unexpanded{\if@savingnotes\else\let\H@@mpfootnotetext\FNH@fn@fntext\fi}%
\the\toks@ }%
\def\FNH@fn@fntext {\FNH@fntext\FNH@fn@fntext@i}%
\def\FNH@hyper@fntext{\FNH@fntext\FNH@hyper@fntext@i}%
\def\FNH@fntext #1{%
%% amsmath compatibility
\ifx\ifmeasuring@\undefined\expandafter\@secondoftwo
\else\expandafter\@firstofone\fi
{\ifmeasuring@\expandafter\@gobbletwo\else\expandafter\@firstofone\fi}%
%% partial tabulary compatibility, [N] must be used, but Sphinx does it
{\ifx\equation$\expandafter\@gobbletwo\fi\FNH@hyper@fntext@i }%$
{\ifx\equation$\expandafter\@gobbletwo\fi #1}%$
}%
%% footnote.sty's replacement for \@footnotetext
\long\def\FNH@fn@fntext@i #1{\global\setbox\fn@notes\vbox
{\unvbox\fn@notes
\fn@startnote
\@makefntext
{\rule\z@\footnotesep\ignorespaces
#1%
\@finalstrut\strutbox
}%
\fn@endnote }%
}%
\long\def\FNH@hyper@fntext@i #1{\global\setbox\fn@notes\vbox
{\unvbox\fn@notes
@@ -62,7 +84,8 @@
\let\@currentHref\Hy@footnote@currentHref
\let\@currentlabelname\@empty
#1}%
\@finalstrut\strutbox }%
\@finalstrut\strutbox
}%
\fn@endnote }%
}%
\def\FNH@hyper@spewnotes {\endgroup
@@ -87,7 +110,7 @@
\def\FNH@endfntext@nolink {\begingroup
\let\@makefntext\@empty\let\@finalstrut\@gobble
\let\rule\@gobbletwo
\if@savingnotes\expandafter\fn@fntext\else\expandafter\H@@footnotetext\fi
\if@savingnotes\expandafter\FNH@fn@fntext\else\expandafter\H@@footnotetext\fi
{\unvbox\z@}\endgroup
}%
%% \spx@opt@BeforeFootnote is defined in sphinx.sty
@@ -139,12 +162,13 @@
\def\FNH@check@c #11.2!3?4,#2#3\relax
{\ifx\FNH@check@c#2\expandafter\@gobble\fi\FNH@bad@footnote@env}%
\def\FNH@bad@footnote@env
{\PackageWarningNoLine{footnotehyper}%
{The footnote environment from package footnote^^J
will be dysfunctional, sorry (not my fault...). You may try to make a bug^^J
report at https://github.com/sphinx-doc/sphinx including the next lines:}%
{\PackageWarningNoLine{footnotehyper-sphinx}%
{Footnotes will be sub-optimal, sorry. This is due to the class or^^J
some package modifying macro \string\@makefntext.^^J
You can try to report this incompatibility at^^J
https://github.com/sphinx-doc/sphinx with this info:}%
\typeout{\meaning\@makefntext}%
\let\fn@prefntext\@empty\let\fn@postfntext\@empty
\let\fn@prefntext\@empty\let\fn@postfntext\@empty
}%
%% \sphinxfootnotemark: usable in section titles and silently removed from
%% TOCs.
@@ -153,6 +177,19 @@
\protect\footnotemark[#1]\fi}%
\AtBeginDocument % let hyperref less complain
{\pdfstringdefDisableCommands{\def\sphinxfootnotemark [#1]{}}}%
% to obtain hyperlinked footnotes in longtable environment we must replace
% hyperref's patch of longtable's patch of \@footnotetext by our own
\def\sphinxlongtablepatch {% only for longtable wrapped in "savenotes"
\let\LT@p@ftntext\FNH@hyper@fntext
}%
%% deprecate \makesavenoteenv
\def\makesavenoteenv{%
\AtEndDocument{\PackageWarning{footnotehyper-sphinx}
{^^J^^J^^J!\@spaces**** SPHINX DEPRECATION WARNING ****^^J!^^J%
!\@spaces\string\makesavenoteenv\space from footnote.sty is deprecated^^J%
!\@spaces and will be removed at Sphinx 1.7 !^^J^^J^^J}}%
\@ifnextchar[\fn@msne@ii\fn@msne@i%]
}%
\endinput
%%
%% End of file `footnotehyper-sphinx.sty'.

View File

@@ -59,10 +59,7 @@
% For hyperlinked footnotes in tables; also for gathering footnotes from
% topic and warning blocks. Also to allow code-blocks in footnotes.
\RequirePackage{footnotehyper-sphinx}
\makesavenoteenv{tabulary}
\makesavenoteenv{tabular}
\makesavenoteenv{threeparttable}
% (longtable is hyperref compatible and needs no special treatment here.)
\AtBeginDocument{\@ifpackageloaded{longtable}{\sphinxlongtablepatch}{}}%
% For the H specifier. Do not \restylefloat{figure}, it breaks Sphinx code
% for allowing figures in tables.
\RequirePackage{float}

View File

@@ -224,7 +224,7 @@ def save_traceback(app):
platform.python_version(),
platform.python_implementation(),
docutils.__version__, docutils.__version_details__,
jinja2.__version__,
jinja2.__version__, # type: ignore
last_msgs)).encode('utf-8'))
if app is not None:
for extname, extmod in iteritems(app._extensions):

View File

@@ -49,7 +49,7 @@ VERBOSITY_MAP.update({
2: logging.DEBUG,
})
COLOR_MAP = defaultdict(lambda text: text) # type: Dict[int, unicode]
COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode]
COLOR_MAP.update({
logging.WARNING: 'darkred',
logging.DEBUG: 'darkgray',
@@ -90,7 +90,7 @@ class SphinxWarningLogRecord(logging.LogRecord):
class SphinxLoggerAdapter(logging.LoggerAdapter):
"""LoggerAdapter allowing ``type`` and ``subtype`` keywords."""
def log(self, level, msg, *args, **kwargs):
def log(self, level, msg, *args, **kwargs): # type: ignore
# type: (Union[int, str], unicode, Any, Any) -> None
if isinstance(level, int):
super(SphinxLoggerAdapter, self).log(level, msg, *args, **kwargs)

View File

@@ -25,7 +25,8 @@ class BooleanParser(Parser):
"""
def parse_compare(self):
# type: () -> None
# type: () -> nodes.Node
node = None # type: nodes.Node
token = self.stream.current
if token.type == 'name':
if token.value in ('true', 'false', 'True', 'False'):
@@ -79,18 +80,18 @@ class Tags(object):
def eval_node(node):
# type: (nodes.Node) -> bool
if isinstance(node, nodes.CondExpr):
if eval_node(node.test):
return eval_node(node.expr1)
if eval_node(node.test): # type: ignore
return eval_node(node.expr1) # type: ignore
else:
return eval_node(node.expr2)
return eval_node(node.expr2) # type: ignore
elif isinstance(node, nodes.And):
return eval_node(node.left) and eval_node(node.right)
return eval_node(node.left) and eval_node(node.right) # type: ignore
elif isinstance(node, nodes.Or):
return eval_node(node.left) or eval_node(node.right)
return eval_node(node.left) or eval_node(node.right) # type: ignore
elif isinstance(node, nodes.Not):
return not eval_node(node.node)
return not eval_node(node.node) # type: ignore
elif isinstance(node, nodes.Name):
return self.tags.get(node.name, False)
return self.tags.get(node.name, False) # type: ignore
else:
raise ValueError('invalid node, check parsing')

View File

@@ -26,7 +26,7 @@ class BaseRenderer(object):
# type: (BaseLoader) -> None
self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n'])
self.env.filters['repr'] = repr
self.env.install_gettext_translations(get_translator())
self.env.install_gettext_translations(get_translator()) # type: ignore
def render(self, template_name, context):
# type: (unicode, Dict) -> unicode

View File

@@ -327,7 +327,7 @@ class HTMLTranslator(BaseTranslator):
def append_fignumber(figtype, figure_id):
# type: (unicode, unicode) -> None
if self.builder.name == 'singlehtml':
key = "%s/%s" % (self.docnames[-1], figtype)
key = u"%s/%s" % (self.docnames[-1], figtype)
else:
key = figtype

View File

@@ -327,6 +327,8 @@ class Table(object):
self.has_problematic = False
self.has_verbatim = False
self.caption = None # type: List[unicode]
self.caption_footnotetexts = [] # type: List[unicode]
self.header_footnotetexts = [] # type: List[unicode]
# current position
self.col = 0
@@ -1052,6 +1054,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
elif isinstance(parent, nodes.table):
# Redirect body output until title is finished.
self.pushbody([])
self.restrict_footnote(node)
else:
logger.warning('encountered title node not in section, topic, table, '
'admonition or sidebar',
@@ -1065,9 +1068,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_title = 0
if isinstance(node.parent, nodes.table):
self.table.caption = self.popbody()
# temporary buffer for footnotes from caption
self.pushbody([])
self.unrestrict_footnote(node)
# the footnote texts from caption
self.table.caption_footnotetexts = self.popbody()
else:
self.body.append(self.context.pop())
self.unrestrict_footnote(node)
self.unrestrict_footnote(node)
def visit_subtitle(self, node):
# type: (nodes.Node) -> None
@@ -1262,7 +1270,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_collected_footnote(self, node):
# type: (nodes.Node) -> None
if 'footnotetext' in node:
self.body.append('%\n\\end{footnotetext}')
# the \ignorespaces in particular for after table header use
self.body.append('%\n\\end{footnotetext}\\ignorespaces ')
else:
if self.in_parsed_literal:
self.body.append('\\end{footnote}')
@@ -1293,7 +1302,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.next_table_colspec:
self.table.colspec = '{%s}\n' % self.next_table_colspec
self.next_table_colspec = None
self.restrict_footnote(node)
def depart_table(self, node):
# type: (nodes.Node) -> None
@@ -1310,7 +1318,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(table)
self.body.append("\n")
self.unrestrict_footnote(node)
self.table = None
def visit_colspec(self, node):
@@ -1333,15 +1340,27 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_thead(self, node):
# type: (nodes.Node) -> None
self.pushbody(self.table.header) # Redirect head output until header is finished.
# Redirect head output until header is finished.
self.pushbody(self.table.header)
# footnotes in longtable header must be restricted
self.restrict_footnote(node)
def depart_thead(self, node):
# type: (nodes.Node) -> None
self.popbody()
# temporary buffer for footnotes from table header
self.pushbody([])
self.unrestrict_footnote(node)
# the footnote texts from header
self.table.header_footnotetexts = self.popbody()
def visit_tbody(self, node):
# type: (nodes.Node) -> None
self.pushbody(self.table.body) # Redirect body output until table is finished.
# Redirect body output until table is finished.
self.pushbody(self.table.body)
# insert footnotetexts from header at start of body (due to longtable)
# those from caption are handled by templates (to allow caption at foot)
self.body.extend(self.table.header_footnotetexts)
def depart_tbody(self, node):
# type: (nodes.Node) -> None
@@ -2244,7 +2263,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_line(self, node):
# type: (nodes.Node) -> None
self.body.append(r'\item[] ')
self.body.append('\\item[] ')
def depart_line(self, node):
# type: (nodes.Node) -> None

View File

@@ -33,7 +33,7 @@ footnotes in table
* - name [#]_
- desription
* - VIDIOC_CROPCAP
- Information about VIDIOC_CROPCAP
- Information about VIDIOC_CROPCAP [#]_
footenotes
--------------------
@@ -50,6 +50,8 @@ footenotes
.. [bar] cite
.. [#] footnotes in table caption
.. [#] footnote in table caption
.. [#] footnotes in table
.. [#] footnote in table header
.. [#] footnote in table not in header

View File

@@ -415,18 +415,19 @@ def test_static_output(app):
(".//li/a", "double"),
],
'footnote.html': [
(".//a[@class='footnote-reference'][@href='#id7'][@id='id1']", r"\[1\]"),
(".//a[@class='footnote-reference'][@href='#id8'][@id='id2']", r"\[2\]"),
(".//a[@class='footnote-reference'][@href='#id8'][@id='id1']", r"\[1\]"),
(".//a[@class='footnote-reference'][@href='#id9'][@id='id2']", r"\[2\]"),
(".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
(".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
(".//a[@class='footnote-reference'][@href='#id9'][@id='id5']", r"\[4\]"),
(".//a[@class='footnote-reference'][@href='#id10'][@id='id6']", r"\[5\]"),
(".//a[@class='footnote-reference'][@href='#id10'][@id='id5']", r"\[4\]"),
(".//a[@class='footnote-reference'][@href='#id11'][@id='id6']", r"\[5\]"),
(".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
(".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
(".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
(".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
(".//a[@class='fn-backref'][@href='#id5']", r"\[4\]"),
(".//a[@class='fn-backref'][@href='#id6']", r"\[5\]"),
(".//a[@class='fn-backref'][@href='#id7']", r"\[6\]"),
],
'otherext.html': [
(".//h1", "Generated section"),

View File

@@ -480,12 +480,15 @@ def test_footnote(app, status, warning):
'{\\phantomsection\\label{\\detokenize{footnote:bar}} '
'\ncite\n}') in result
assert '\\caption{Table caption \\sphinxfootnotemark[4]' in result
assert 'name \\sphinxfootnotemark[5]' in result
assert ('\\end{threeparttable}\n\\par\n\\endgroup\n%\n'
'\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
'footnotes in table caption\n%\n\\end{footnotetext}%\n'
assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n'
'\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
'footnotes in table\n%\n\\end{footnotetext}') in result
'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces \n'
'VIDIOC\\_CROPCAP\n&\n') in result
assert ('Information about VIDIOC\\_CROPCAP %\n'
'\\begin{footnote}[6]\\sphinxAtStartFootnote\n'
'footnote in table not in header\n%\n\\end{footnote}\n\\\\\n\hline\n'
'\\end{tabulary}\n\\end{threeparttable}\n\\par\n\\end{savenotes}\n') in result
@pytest.mark.sphinx('latex', testroot='footnotes')
@@ -514,14 +517,18 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
'in caption of normal table}\\label{\\detokenize{index:id28}}') in result
assert ('\\caption{footnote \\sphinxfootnotemark[8] '
'in caption \\sphinxfootnotemark[9] of longtable}') in result
assert ('\\end{longtable}\n%\n\\begin{footnotetext}[8]'
'\\sphinxAtStartFootnote\n'
'Foot note in longtable\n%\n\\end{footnotetext}' in result)
assert ('\\endlastfoot\n%\n\\begin{footnotetext}[8]\\sphinxAtStartFootnote\n'
'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n'
'\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n'
'Second footnote in caption of longtable\n') in result
assert ('This is a reference to the code-block in the footnote:\n'
'{\\hyperref[\\detokenize{index:codeblockinfootnote}]'
'{\\sphinxcrossref{\\DUrole{std,std-ref}{I am in a footnote}}}}') in result
assert ('&\nThis is one more footnote with some code in it '
'\\sphinxfootnotemark[10].\n\\\\') in result
assert ('&\nThis is one more footnote with some code in it %\n'
'\\begin{footnote}[10]\\sphinxAtStartFootnote\n'
'Third footnote in longtable\n') in result
assert ('\\end{sphinxVerbatim}\n\\let\\sphinxVerbatimTitle\\empty\n'
'\\let\\sphinxLiteralBlockLabel\\empty\n%\n\\end{footnote}.\n') in result
assert '\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]' in result
@@ -561,7 +568,8 @@ def test_latex_show_urls_is_inline(app, status, warning):
'(http://sphinx-doc.org/)}] \\leavevmode\nDescription' in result)
assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] '
'\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces \n'
'Description') in result
assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist} '
'(http://sphinx-doc.org/)}] \\leavevmode\nDescription') in result
assert '\\url{https://github.com/sphinx-doc/sphinx}\n' in result
@@ -607,15 +615,16 @@ def test_latex_show_urls_is_footnote(app, status, warning):
'{URL in term}\\sphinxfootnotemark[8]}] '
'\\leavevmode%\n\\begin{footnotetext}[8]\\sphinxAtStartFootnote\n'
'\\nolinkurl{http://sphinx-doc.org/}\n%\n'
'\\end{footnotetext}\nDescription') in result
'\\end{footnotetext}\\ignorespaces \nDescription') in result
assert ('\\item[{Footnote in term \\sphinxfootnotemark[10]}] '
'\\leavevmode%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n'
'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces \n'
'Description') in result
assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}'
'\\sphinxfootnotemark[9]}] '
'\\leavevmode%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n'
'\\nolinkurl{http://sphinx-doc.org/}\n%\n'
'\\end{footnotetext}\nDescription') in result
'\\end{footnotetext}\\ignorespaces \nDescription') in result
assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result)
assert ('\\href{mailto:sphinx-dev@googlegroups.com}'
'{sphinx-dev@googlegroups.com}\n') in result
@@ -655,7 +664,8 @@ def test_latex_show_urls_is_no(app, status, warning):
'\\leavevmode\nDescription') in result
assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] '
'\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result
'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces \n'
'Description') in result
assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}}] '
'\\leavevmode\nDescription') in result
assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result)
@@ -831,32 +841,33 @@ def test_latex_table_tabulars(app, status, warning):
# simple_table
table = tables['simple table']
assert ('\\begingroup\n\\centering\n\\begin{tabulary}{\\linewidth}{|L|L|}' in table)
assert ('\\begin{savenotes}\n\\centering\n'
'\\begin{tabulary}{\\linewidth}{|L|L|}' in table)
assert ('\\hline\n'
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
'\\sphinxstylethead{\\relax \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)
assert ('\\hline\n\\end{tabulary}\n\\par\n\\endgroup' in table)
assert ('\\hline\n\\end{tabulary}\n\\par\n\\end{savenotes}' in table)
# table having :widths: option
table = tables['table having :widths: option']
assert ('\\begingroup\n\\centering\n'
assert ('\\begin{savenotes}\n\\centering\n'
'\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}' in table)
assert ('\\hline\n\\end{tabular}\n\\par\n\\endgroup' in table)
assert ('\\hline\n\\end{tabular}\n\\par\n\\end{savenotes}' in table)
# table having :align: option (tabulary)
table = tables['table having :align: option (tabulary)']
assert ('\\begingroup\n\\raggedleft\n'
assert ('\\begin{savenotes}\n\\raggedleft\n'
'\\begin{tabulary}{\\linewidth}{|L|L|}\n' in table)
assert ('\\hline\n\\end{tabulary}\n\\par\n\\endgroup' in table)
assert ('\\hline\n\\end{tabulary}\n\\par\n\\end{savenotes}' in table)
# table having :align: option (tabular)
table = tables['table having :align: option (tabular)']
assert ('\\begingroup\n\\raggedright\n'
assert ('\\begin{savenotes}\n\\raggedright\n'
'\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}\n' in table)
assert ('\\hline\n\\end{tabular}\n\\par\n\\endgroup' in table)
assert ('\\hline\n\\end{tabular}\n\\par\n\\end{savenotes}' in table)
# table with tabularcolumn
table = tables['table with tabularcolumn']
@@ -864,12 +875,12 @@ def test_latex_table_tabulars(app, status, warning):
# table having caption
table = tables['table having caption']
assert ('\\begingroup\n\\centering\n'
assert ('\\begin{savenotes}\n\\centering\n'
'\\begin{threeparttable}\n\\capstart\\caption{caption for table}'
'\\label{\\detokenize{tabular:id1}}' in table)
assert ('\\begin{tabulary}{\\linewidth}{|L|L|}' in table)
assert ('\\hline\n\\end{tabulary}\n\\end{threeparttable}'
'\n\\par\n\\endgroup' in table)
'\n\\par\n\\end{savenotes}' in table)
# table having verbatim
table = tables['table having verbatim']
@@ -898,7 +909,7 @@ def test_latex_table_longtable(app, status, warning):
# longtable
table = tables['longtable']
assert ('\\begin{longtable}{|l|l|}\n\\hline' in table)
assert ('\\begin{savenotes}\\begin{longtable}{|l|l|}\n\\hline' in table)
assert ('\\hline\n'
'\\sphinxstylethead{\\relax \nheader1\n\\unskip}\\relax &'
'\\sphinxstylethead{\\relax \nheader2\n\\unskip}\\relax \\\\\n'
@@ -915,7 +926,7 @@ def test_latex_table_longtable(app, status, warning):
assert ('\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)
assert ('\\hline\n\\end{longtable}' in table)
assert ('\\hline\n\\end{longtable}\\end{savenotes}' in table)
# longtable having :widths: option
table = tables['longtable having :widths: option']

View File

@@ -9,6 +9,9 @@
:license: BSD, see LICENSE for details.
"""
from collections import namedtuple
from six import BytesIO
from docutils import frontend, utils
from docutils.parsers import rst
@@ -16,6 +19,17 @@ from sphinx.search import IndexBuilder
from sphinx.util import jsdump
import pytest
DummyEnvironment = namedtuple('DummyEnvironment', ['version', 'domains'])
class DummyDomain(object):
def __init__(self, data):
self.data = data
self.object_types = {}
def get_objects(self):
return self.data
settings = parser = None
@@ -39,23 +53,15 @@ def is_registered_term(index, keyword):
FILE_CONTENTS = '''\
section_title
=============
.. test that comments are not indexed: boson
test that non-comments are indexed: fermion
'''
def test_wordcollector():
doc = utils.new_document(b'test data', settings)
doc['file'] = 'dummy'
parser.parse(FILE_CONTENTS, doc)
ix = IndexBuilder(None, 'en', {}, None)
ix.feed('docname', 'filename', 'title', doc)
assert 'boson' not in ix._mapping
assert 'fermion' in ix._mapping
@pytest.mark.sphinx(testroot='ext-viewcode')
def test_objects_are_escaped(app, status, warning):
app.builder.build_all()
@@ -123,3 +129,102 @@ def test_term_in_raw_directive(app, status, warning):
assert not is_registered_term(searchindex, 'raw')
assert is_registered_term(searchindex, 'rawword')
assert not is_registered_term(searchindex, 'latex_keyword')
def test_IndexBuilder():
domain = DummyDomain([('objname', 'objdispname', 'objtype', 'docname', '#anchor', 1),
('objname2', 'objdispname2', 'objtype2', 'docname2', '', -1)])
env = DummyEnvironment('1.0', {'dummy': domain})
doc = utils.new_document(b'test data', settings)
doc['file'] = 'dummy'
parser.parse(FILE_CONTENTS, doc)
# feed
index = IndexBuilder(env, 'en', {}, None)
index.feed('docname', 'filename', 'title', doc)
index.feed('docname2', 'filename2', 'title2', doc)
assert index._titles == {'docname': 'title', 'docname2': 'title2'}
assert index._filenames == {'docname': 'filename', 'docname2': 'filename2'}
assert index._mapping == {
'fermion': {'docname', 'docname2'},
'comment': {'docname', 'docname2'},
'non': {'docname', 'docname2'},
'index': {'docname', 'docname2'},
'test': {'docname', 'docname2'}
}
assert index._title_mapping == {'section_titl': {'docname', 'docname2'}}
assert index._objtypes == {}
assert index._objnames == {}
# freeze
assert index.freeze() == {
'docnames': ('docname', 'docname2'),
'envversion': '1.0',
'filenames': ['filename', 'filename2'],
'objects': {'': {'objname': (0, 0, 1, '#anchor')}},
'objnames': {0: ('dummy', 'objtype', 'objtype')},
'objtypes': {0: 'dummy:objtype'},
'terms': {'comment': [0, 1],
'fermion': [0, 1],
'index': [0, 1],
'non': [0, 1],
'test': [0, 1]},
'titles': ('title', 'title2'),
'titleterms': {'section_titl': [0, 1]}
}
assert index._objtypes == {('dummy', 'objtype'): 0}
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}
# dump / load
stream = BytesIO()
index.dump(stream, 'pickle')
stream.seek(0)
index2 = IndexBuilder(env, 'en', {}, None)
index2.load(stream, 'pickle')
assert index2._titles == index._titles
assert index2._filenames == index._filenames
assert index2._mapping == index._mapping
assert index2._title_mapping == index._title_mapping
assert index2._objtypes == {}
assert index2._objnames == {}
# freeze after load
assert index2.freeze() == index.freeze()
assert index2._objtypes == index._objtypes
assert index2._objnames == index._objnames
# prune
index.prune(['docname2'])
assert index._titles == {'docname2': 'title2'}
assert index._filenames == {'docname2': 'filename2'}
assert index._mapping == {
'fermion': {'docname2'},
'comment': {'docname2'},
'non': {'docname2'},
'index': {'docname2'},
'test': {'docname2'}
}
assert index._title_mapping == {'section_titl': {'docname2'}}
assert index._objtypes == {('dummy', 'objtype'): 0}
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}
# freeze after prune
assert index.freeze() == {
'docnames': ('docname2',),
'envversion': '1.0',
'filenames': ['filename2'],
'objects': {},
'objnames': {0: ('dummy', 'objtype', 'objtype')},
'objtypes': {0: 'dummy:objtype'},
'terms': {'comment': 0,
'fermion': 0,
'index': 0,
'non': 0,
'test': 0},
'titles': ('title2',),
'titleterms': {'section_titl': 0}
}
assert index._objtypes == {('dummy', 'objtype'): 0}
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}

View File

@@ -48,7 +48,7 @@ deps=
[testenv:py35]
deps=
mypy-lang
mypy
typed_ast
{[testenv]deps}
commands=