Merge branch 'master' into 3742_index_in_PDF_bookmark_at_RTD

This commit is contained in:
Jean-François B 2018-07-16 17:15:45 +02:00 committed by GitHub
commit 54456ba455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 551 additions and 57 deletions

23
CHANGES
View File

@ -4,6 +4,11 @@ Release 1.8.0 (in development)
Dependencies
------------
* LaTeX: :confval:`latex_use_xindy`, if ``True`` (default for
``xelatex/lualatex``), instructs ``make latexpdf`` to use :program:`xindy`
for general index. Make sure your LaTeX distribution includes it.
(refs: #5134)
Incompatible changes
--------------------
@ -39,6 +44,10 @@ Incompatible changes
Please use ``app.add_js_file()`` instead.
* #5072: Save environment object also with only new documents
* #5035: qthelp builder allows dashes in :confval:`qthelp_namespace`
* LaTeX: with lualatex or xelatex use by default :program:`xindy` as
UTF-8 able replacement of :program:`makeindex` (refs: #5134). After
upgrading Sphinx, please clean latex build repertory of existing project
before new build.
Deprecated
----------
@ -148,6 +157,9 @@ Features added
``pdflatex`` runs for index to show up in PDF bookmarks (refs: #3742)
__ https://github.com/rtfd/readthedocs.org/issues/2857
* Add :confval:`latex_use_xindy` for UTF-8 savvy indexing, defaults to ``True``
if :confval:`latex_engine` is ``'xelatex'`` or ``'lualatex'``.
* #4976: ``SphinxLoggerAdapter.info()`` now supports ``location`` parameter
Bugs fixed
----------
@ -157,6 +169,8 @@ Bugs fixed
* #4945: i18n: fix lang_COUNTRY not fallback correctly for IndexBuilder. Thanks
to Shengjing Zhu.
* #4983: productionlist directive generates invalid IDs for the tokens
* #5132: (lualatex) PDF build fails if indexed word starts with Unicode character
* #5133: latex: index headings "Symbols" and "Numbers" not internationalized
Testing
--------
@ -207,6 +221,15 @@ Bugs fixed
* #5091: latex: curly braces in index entries are not handled correctly
* #5070: epub: Wrong internal href fragment links
* #5104: apidoc: Interface of ``sphinx.apidoc:main()`` has changed
* #5076: napoleon raises RuntimeError with python 3.7
* #5125: sphinx-build: Interface of ``sphinx:main()`` has changed
* sphinx-build: ``sphinx.cmd.build.main()`` refers ``sys.argv`` instead of given
argument
* #5146: autosummary: warning is emitted when the first line of docstring ends
with literal notation
* autosummary: warnings of autosummary indicates wrong location (refs: #5146)
* #5143: autodoc: crashed on inspecting dict like object which does not support
sorting
Testing
--------

View File

@ -67,6 +67,7 @@ latex_elements = {
'printindex': '\\footnotesize\\raggedright\\sphinxprintindex',
}
latex_show_urls = 'footnote'
latex_use_xindy = True
autodoc_member_order = 'groupwise'
todo_include_todos = True

View File

@ -50,6 +50,10 @@ Logging API
If true, the logger does not fold lines at the end of the log message.
The default is ``False``.
**location**
Where the message emitted. For more detail, see
:meth:`SphinxLoggerAdapter.warning`.
**color**
The color of logs. By default, debug level logs are
colored as ``"darkgray"``, and debug2 level ones are ``"lightgray"``.

View File

@ -1872,6 +1872,40 @@ macros and environments, see also :doc:`/latex`.
.. versionadded:: 1.6
.. confval:: latex_use_xindy
If ``True``, the PDF build from the LaTeX files created by Sphinx
will use :program:`xindy` (doc__) rather than :program:`makeindex`
for preparing the index of general terms (from :rst:dir:`index`
usage). This means that words with UTF-8 characters will get
ordered correctly for the :confval:`language`.
__ http://xindy.sourceforge.net/
- This option is ignored if :confval:`latex_engine` is ``'platex'``
(Japanese documents) as :program:`mendex` is used in that case.
- The default is ``True`` for ``'xelatex'`` or ``'lualatex'`` as
:program:`makeindex`, if any indexed term starts with a non-ascii
character, creates ``.ind`` file containing invalid bytes for
UTF-8 encoding. With ``'lualatex'`` this then breaks the PDF
build. Notice that :program:`xindy` supports most but not
all European languages.
- The default is ``False`` for ``'pdflatex'`` but ``True`` is
recommended for non-English documents as soon as some indexed
terms use non-ascii characters from the language script.
Cyrillic scripts are (transparently) supported with
``'pdflatex'`` thanks to a specific Sphinx-contributed ``xindy``
style file :file:`cyrLICRutf8.xdy`.
As :program:`xindy` does not support the same range of languages
as ``LaTeX/babel`` does, the default :program:`makeindex` for
``'pdflatex'`` may be preferred in some circumstances, although
the index will be ill-formed probably.
.. versionadded:: 1.8
.. confval:: latex_elements
.. versionadded:: 0.5

View File

@ -127,7 +127,7 @@ directory in which you want to place the built documentation.
The :option:`-b <sphinx-build -b>` option selects a builder; in this example
Sphinx will build HTML files.
|more| Refer to the :program:`sphinx-build man page <sphinx-build>` for all
|more| Refer to the :doc:`sphinx-build man page </man/sphinx-build>` for all
options that :program:`sphinx-build` supports.
However, :program:`sphinx-quickstart` script creates a :file:`Makefile` and a
@ -331,7 +331,7 @@ More topics to be covered
.. [#] This is the usual layout. However, :file:`conf.py` can also live in
another directory, the :term:`configuration directory`. Refer to the
:program:`sphinx-build man page <sphinx-build>` for more information.
:doc:`sphinx-build man page </man/sphinx-build>` for more information.
.. |more| image:: /_static/more.png
:align: middle

View File

@ -71,16 +71,16 @@ if __version__.endswith('+'):
pass
def main(*args, **kwargs):
# type: (Any, Any) -> int
def main(argv=sys.argv): # type: ignore
# type: (List[unicode]) -> int
from .cmd import build
warnings.warn(
'`sphinx.main()` has moved to `sphinx.cmd.build.main()`.',
RemovedInSphinx20Warning,
stacklevel=2,
)
args = args[1:] # skip first argument to adjust arguments (refs: #4615)
return build.main(*args, **kwargs)
argv = argv[1:] # skip first argument to adjust arguments (refs: #4615)
return build.main(argv)
def build_main(argv=sys.argv):

View File

@ -44,6 +44,67 @@ if False:
from sphinx.config import Config # NOQA
XINDY_LANG_OPTIONS = {
# language codes from docutils.writers.latex2e.Babel
# ! xindy language names may differ from those in use by LaTeX/babel
# ! xindy does not support all Latin scripts as recognized by LaTeX/babel
# ! not all xindy-supported languages appear in Babel.language_codes
# cd /usr/local/texlive/2018/texmf-dist/xindy/modules/lang
# find . -name '*utf8.xdy'
# LATIN
'sq': '-L albanian -C utf8 ',
'hr': '-L croatian -C utf8 ',
'cs': '-L czech -C utf8 ',
'da': '-L danish -C utf8 ',
'nl': '-L dutch -C ij-as-ij-utf8 ',
'en': '-L english -C utf8 ',
'eo': '-L esperanto -C utf8 ',
'et': '-L estonian -C utf8 ',
'fi': '-L finnish -C utf8 ',
'fr': '-L french -C utf8 ',
'de': '-L german -C din5007-utf8 ',
'is': '-L icelandic -C utf8 ',
'it': '-L italian -C utf8 ',
'la': '-L latin -C utf8 ',
'lv': '-L latvian -C utf8 ',
'lt': '-L lithuanian -C utf8 ',
'dsb': '-L lower-sorbian -C utf8 ',
'ds': '-L lower-sorbian -C utf8 ', # trick, no conflict
'nb': '-L norwegian -C utf8 ',
'no': '-L norwegian -C utf8 ', # and what about nynorsk?
'pl': '-L polish -C utf8 ',
'pt': '-L portuguese -C utf8 ',
'ro': '-L romanian -C utf8 ',
'sk': '-L slovak -C small-utf8 ', # there is also slovak-large
'sl': '-L slovenian -C utf8 ',
'es': '-L spanish -C modern-utf8 ', # there is also spanish-traditional
'sv': '-L swedish -C utf8 ',
'tr': '-L turkish -C utf8 ',
'hsb': '-L upper-sorbian -C utf8 ',
'hs': '-L upper-sorbian -C utf8 ', # trick, no conflict
'vi': '-L vietnamese -C utf8 ',
# CYRILLIC
# for usage with pdflatex, needs also cyrLICRutf8.xdy module
'be': '-L belarusian -C utf8 ',
'bg': '-L bulgarian -C utf8 ',
'mk': '-L macedonian -C utf8 ',
'mn': '-L mongolian -C cyrillic-utf8 ',
'ru': '-L russian -C utf8 ',
'sr': '-L serbian -C utf8 ',
'sh-cyrl': '-L serbian -C utf8 ',
'sh': '-L serbian -C utf8 ', # trick, no conflict
'uk': '-L ukrainian -C utf8 ',
# GREEK
# can work only with xelatex/lualatex, not supported by texindy+pdflatex
'el': '-L greek -C utf8 ',
# FIXME, not compatible with [:2] slice but does Sphinx support Greek ?
'el-polyton': '-L greek -C polytonic-utf8 ',
} # type: Dict[unicode, unicode]
XINDY_CYRILLIC_SCRIPTS = [
'be', 'bg', 'mk', 'mn', 'ru', 'sr', 'sh', 'uk',
] # type: List[unicode]
logger = logging.getLogger(__name__)
@ -232,7 +293,23 @@ class LaTeXBuilder(Builder):
self.copy_image_files()
# copy TeX support files from texinputs
context = {'latex_engine': self.config.latex_engine}
# configure usage of xindy (impacts Makefile and latexmkrc)
# FIXME: convert this rather to a confval with suitable default
# according to language ? but would require extra documentation
if self.config.language:
xindy_lang_option = \
XINDY_LANG_OPTIONS.get(self.config.language[:2],
'-L general -C utf8 ')
xindy_cyrillic = self.config.language[:2] in XINDY_CYRILLIC_SCRIPTS
else:
xindy_lang_option = '-L english -C utf8 '
xindy_cyrillic = False
context = {
'latex_engine': self.config.latex_engine,
'xindy_use': self.config.latex_use_xindy,
'xindy_lang_option': xindy_lang_option,
'xindy_cyrillic': xindy_cyrillic,
}
logger.info(bold(__('copying TeX support files...')))
staticdirname = path.join(package_dir, 'texinputs')
for filename in os.listdir(staticdirname):
@ -317,6 +394,12 @@ def default_latex_docclass(config):
return {}
def default_latex_use_xindy(config):
# type: (Config) -> bool
""" Better default latex_use_xindy settings for specific engines. """
return config.latex_engine in {'xelatex', 'lualatex'}
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_builder(LaTeXBuilder)
@ -334,6 +417,7 @@ def setup(app):
app.add_config_value('latex_logo', None, None, string_classes)
app.add_config_value('latex_appendices', [], None)
app.add_config_value('latex_use_latex_multicolumn', False, None)
app.add_config_value('latex_use_xindy', default_latex_use_xindy, None)
app.add_config_value('latex_toplevel_sectioning', None, None,
ENUM(None, 'part', 'chapter', 'section'))
app.add_config_value('latex_domain_indices', True, None, [list])

View File

@ -311,7 +311,7 @@ def main(argv=sys.argv[1:]): # type: ignore
locale.setlocale(locale.LC_ALL, '')
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
if sys.argv[1:2] == ['-M']:
if argv[:1] == ['-M']:
return make_main(argv)
else:
return build_main(argv)

View File

@ -129,7 +129,7 @@ class Config(object):
rst_epilog = (None, 'env', string_classes),
rst_prolog = (None, 'env', string_classes),
trim_doctest_flags = (True, 'env', []),
primary_domain = ('py', 'env', [NoneType]),
primary_domain = ('py', 'env', [NoneType]), # type: ignore
needs_sphinx = (None, None, string_classes),
needs_extensions = ({}, None, []),
manpages_url = (None, 'env', []),

View File

@ -78,7 +78,9 @@ from sphinx.ext.autodoc.importer import import_module
from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import import_object, rst, logging
from sphinx.util.docutils import NullReporter, SphinxDirective, new_document
from sphinx.util.docutils import (
NullReporter, SphinxDirective, new_document, switch_source_input
)
if False:
# For type annotation
@ -92,6 +94,7 @@ logger = logging.getLogger(__name__)
periods_re = re.compile(r'\.(?:\s+)')
literal_re = re.compile(r'::\s*$')
# -- autosummary_toc node ------------------------------------------------------
@ -373,17 +376,19 @@ class Autosummary(SphinxDirective):
def append_row(*column_texts):
# type: (unicode) -> None
row = nodes.row('')
source, line = self.state_machine.get_source_and_line()
for text in column_texts:
node = nodes.paragraph('')
vl = ViewList()
vl.append(text, '<autosummary>')
self.state.nested_parse(vl, 0, node)
try:
if isinstance(node[0], nodes.paragraph):
node = node[0]
except IndexError:
pass
row.append(nodes.entry('', node))
vl.append(text, '%s:%d:<autosummary>' % (source, line))
with switch_source_input(self.state, vl):
self.state.nested_parse(vl, 0, node)
try:
if isinstance(node[0], nodes.paragraph):
node = node[0]
except IndexError:
pass
row.append(nodes.entry('', node))
body.append(row)
for name, sig, summary, real_name in items:
@ -484,6 +489,9 @@ def extract_summary(doc, document):
# considered as that splitting by period does not break inline markups
break
# strip literal notation mark ``::`` from tail of summary
summary = literal_re.sub('.', summary)
return summary

View File

@ -124,7 +124,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) # type: ignore
template_loader = FileSystemLoader(template_dirs)
template_env = SandboxedEnvironment(loader=template_loader)
template_env.filters['underline'] = _underline

View File

@ -556,7 +556,14 @@ class GoogleDocstring(UnicodeMixin):
self._parsed_lines = self._consume_empty()
if self._name and (self._what == 'attribute' or self._what == 'data'):
self._parsed_lines.extend(self._parse_attribute_docstring())
# Implicit stop using StopIteration no longer allowed in
# Python 3.7; see PEP 479
res = [] # type: List[unicode]
try:
res = self._parse_attribute_docstring()
except StopIteration:
pass
self._parsed_lines.extend(res)
return
while self._line_iter.has_next():

View File

@ -64,7 +64,7 @@ class _TranslationProxy(UserString, object):
# replace function from UserString; it instantiates a self.__class__
# for the encoding result
def encode(self, encoding=None, errors=None):
def encode(self, encoding=None, errors=None): # type: ignore
# type: (unicode, unicode) -> str
if encoding:
if errors:
@ -88,7 +88,7 @@ class _TranslationProxy(UserString, object):
return dir(text_type)
def __iter__(self):
# type: () -> Iterator[unicode]
# type: () -> Iterator
return iter(self.data)
def __len__(self):
@ -103,15 +103,15 @@ class _TranslationProxy(UserString, object):
# type: () -> unicode
return text_type(self.data)
def __add__(self, other):
def __add__(self, other): # type: ignore
# type: (unicode) -> unicode
return self.data + other
def __radd__(self, other):
def __radd__(self, other): # type: ignore
# type: (unicode) -> unicode
return other + self.data
def __mod__(self, other):
def __mod__(self, other): # type: ignore
# type: (unicode) -> unicode
return self.data % other
@ -119,11 +119,11 @@ class _TranslationProxy(UserString, object):
# type: (unicode) -> unicode
return other % self.data
def __mul__(self, other):
def __mul__(self, other): # type: ignore
# type: (Any) -> unicode
return self.data * other
def __rmul__(self, other):
def __rmul__(self, other): # type: ignore
# type: (Any) -> unicode
return other * self.data
@ -165,7 +165,7 @@ class _TranslationProxy(UserString, object):
# type: (Tuple[Callable, Tuple[unicode]]) -> None
self._func, self._args = tup
def __getitem__(self, key):
def __getitem__(self, key): # type: ignore
# type: (Any) -> unicode
return self.data[key]

View File

@ -13,6 +13,11 @@
\ifdefined\pdfpxdimen
\let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen
\fi \sphinxpxdimen=<%= pxunit %>\relax
<% if use_xindy -%>
%% turn off hyperref patch of \index as sphinx.xdy xindy module takes care of
%% suitable \hyperpage mark-up, working around hyperref-xindy incompatibility
\PassOptionsToPackage{hyperindex=false}{hyperref}
<% endif -%>
<%= passoptionstopackages %>
\PassOptionsToPackage{warn}{textcomp}
<%= inputenc %>
@ -31,7 +36,7 @@
<%= hyperref %>
<%= contentsname %>
<%= numfig_format %>
<%= literalblockpto %>
<%= translatablestrings %>
<%= pageautorefname %>
<%= tocdepth %>
<%= secnumdepth %>

View File

@ -223,7 +223,7 @@ class path(text_type):
"""
Joins the path with the argument given and returns the result.
"""
return self.__class__(os.path.join(self, *map(self.__class__, args))) # type: ignore # NOQA
return self.__class__(os.path.join(self, *map(self.__class__, args)))
def listdir(self):
# type: () -> List[unicode]

View File

@ -22,6 +22,15 @@ export LATEXOPTS =
# or on command line for faster builds.
{% endif -%}
LATEXMKOPTS =
{% if xindy_use -%}
export XINDYOPTS = {{ xindy_lang_option }} -M sphinx.xdy
{% if latex_engine == 'pdflatex' and xindy_cyrillic -%}
XINDYOPTS += -M cyrLICRutf8.xdy
{% endif -%}
{% if latex_engine == 'xelatex' or latex_engine == 'lualatex' -%}
XINDYOPTS += -I xelatex
{% endif -%}
{% endif -%}
# format: pdf or dvi (used only by archive targets)
FMT = pdf

View File

@ -0,0 +1,101 @@
;; -*- coding: utf-8; mode: Lisp; -*-
;; style file for xindy
;; filename: cyrLICRutf8.xdy
;; description: style file for xindy which maps back LaTeX Internal
;; Character Representation of Cyrillic to utf-8
;; usage: for use with pdflatex produced .idx files.
;; Contributed by the Sphinx team, July 2018.
(merge-rule "\IeC {\'\CYRG }" "Ѓ" :string)
(merge-rule "\IeC {\'\CYRK }" "Ќ" :string)
(merge-rule "\IeC {\'\cyrg }" "ѓ" :string)
(merge-rule "\IeC {\'\cyrk }" "ќ" :string)
(merge-rule "\IeC {\CYRA }" "А" :string)
(merge-rule "\IeC {\CYRB }" "Б" :string)
(merge-rule "\IeC {\CYRC }" "Ц" :string)
(merge-rule "\IeC {\CYRCH }" "Ч" :string)
(merge-rule "\IeC {\CYRD }" "Д" :string)
(merge-rule "\IeC {\CYRDJE }" "Ђ" :string)
(merge-rule "\IeC {\CYRDZE }" "Ѕ" :string)
(merge-rule "\IeC {\CYRDZHE }" "Џ" :string)
(merge-rule "\IeC {\CYRE }" "Е" :string)
(merge-rule "\IeC {\CYREREV }" "Э" :string)
(merge-rule "\IeC {\CYRERY }" "Ы" :string)
(merge-rule "\IeC {\CYRF }" "Ф" :string)
(merge-rule "\IeC {\CYRG }" "Г" :string)
(merge-rule "\IeC {\CYRGUP }" "Ґ" :string)
(merge-rule "\IeC {\CYRH }" "Х" :string)
(merge-rule "\IeC {\CYRHRDSN }" "Ъ" :string)
(merge-rule "\IeC {\CYRI }" "И" :string)
(merge-rule "\IeC {\CYRIE }" "Є" :string)
(merge-rule "\IeC {\CYRII }" "І" :string)
(merge-rule "\IeC {\CYRISHRT }" "Й" :string)
(merge-rule "\IeC {\CYRJE }" "Ј" :string)
(merge-rule "\IeC {\CYRK }" "К" :string)
(merge-rule "\IeC {\CYRL }" "Л" :string)
(merge-rule "\IeC {\CYRLJE }" "Љ" :string)
(merge-rule "\IeC {\CYRM }" "М" :string)
(merge-rule "\IeC {\CYRN }" "Н" :string)
(merge-rule "\IeC {\CYRNJE }" "Њ" :string)
(merge-rule "\IeC {\CYRO }" "О" :string)
(merge-rule "\IeC {\CYRP }" "П" :string)
(merge-rule "\IeC {\CYRR }" "Р" :string)
(merge-rule "\IeC {\CYRS }" "С" :string)
(merge-rule "\IeC {\CYRSFTSN }" "Ь" :string)
(merge-rule "\IeC {\CYRSH }" "Ш" :string)
(merge-rule "\IeC {\CYRSHCH }" "Щ" :string)
(merge-rule "\IeC {\CYRT }" "Т" :string)
(merge-rule "\IeC {\CYRTSHE }" "Ћ" :string)
(merge-rule "\IeC {\CYRU }" "У" :string)
(merge-rule "\IeC {\CYRUSHRT }" "Ў" :string)
(merge-rule "\IeC {\CYRV }" "В" :string)
(merge-rule "\IeC {\CYRYA }" "Я" :string)
(merge-rule "\IeC {\CYRYI }" "Ї" :string)
(merge-rule "\IeC {\CYRYO }" "Ё" :string)
(merge-rule "\IeC {\CYRYU }" "Ю" :string)
(merge-rule "\IeC {\CYRZ }" "З" :string)
(merge-rule "\IeC {\CYRZH }" "Ж" :string)
(merge-rule "\IeC {\cyra }" "а" :string)
(merge-rule "\IeC {\cyrb }" "б" :string)
(merge-rule "\IeC {\cyrc }" "ц" :string)
(merge-rule "\IeC {\cyrch }" "ч" :string)
(merge-rule "\IeC {\cyrd }" "д" :string)
(merge-rule "\IeC {\cyrdje }" "ђ" :string)
(merge-rule "\IeC {\cyrdze }" "ѕ" :string)
(merge-rule "\IeC {\cyrdzhe }" "џ" :string)
(merge-rule "\IeC {\cyre }" "е" :string)
(merge-rule "\IeC {\cyrerev }" "э" :string)
(merge-rule "\IeC {\cyrery }" "ы" :string)
(merge-rule "\IeC {\cyrf }" "ф" :string)
(merge-rule "\IeC {\cyrg }" "г" :string)
(merge-rule "\IeC {\cyrgup }" "ґ" :string)
(merge-rule "\IeC {\cyrh }" "х" :string)
(merge-rule "\IeC {\cyrhrdsn }" "ъ" :string)
(merge-rule "\IeC {\cyri }" "и" :string)
(merge-rule "\IeC {\cyrie }" "є" :string)
(merge-rule "\IeC {\cyrii }" "і" :string)
(merge-rule "\IeC {\cyrishrt }" "й" :string)
(merge-rule "\IeC {\cyrje }" "ј" :string)
(merge-rule "\IeC {\cyrk }" "к" :string)
(merge-rule "\IeC {\cyrl }" "л" :string)
(merge-rule "\IeC {\cyrlje }" "љ" :string)
(merge-rule "\IeC {\cyrm }" "м" :string)
(merge-rule "\IeC {\cyrn }" "н" :string)
(merge-rule "\IeC {\cyrnje }" "њ" :string)
(merge-rule "\IeC {\cyro }" "о" :string)
(merge-rule "\IeC {\cyrp }" "п" :string)
(merge-rule "\IeC {\cyrr }" "р" :string)
(merge-rule "\IeC {\cyrs }" "с" :string)
(merge-rule "\IeC {\cyrsftsn }" "ь" :string)
(merge-rule "\IeC {\cyrsh }" "ш" :string)
(merge-rule "\IeC {\cyrshch }" "щ" :string)
(merge-rule "\IeC {\cyrt }" "т" :string)
(merge-rule "\IeC {\cyrtshe }" "ћ" :string)
(merge-rule "\IeC {\cyru }" "у" :string)
(merge-rule "\IeC {\cyrushrt }" "ў" :string)
(merge-rule "\IeC {\cyrv }" "в" :string)
(merge-rule "\IeC {\cyrya }" "я" :string)
(merge-rule "\IeC {\cyryi }" "ї" :string)
(merge-rule "\IeC {\cyryo }" "ё" :string)
(merge-rule "\IeC {\cyryu }" "ю" :string)
(merge-rule "\IeC {\cyrz }" "з" :string)
(merge-rule "\IeC {\cyrzh }" "ж" :string)

View File

@ -10,7 +10,11 @@ $pdflatex = 'xelatex ' . $ENV{'LATEXOPTS'} . ' %O %S';
{% endif -%}
$lualatex = 'lualatex ' . $ENV{'LATEXOPTS'} . ' %O %S';
$xelatex = 'xelatex --no-pdf ' . $ENV{'LATEXOPTS'} . ' %O %S';
{% if xindy_use -%}
$makeindex = 'xindy ' . $ENV{'XINDYOPTS'} . ' %O -o %D %S';
{% else -%}
$makeindex = 'makeindex -s python.ist %O -o %D %S';
{% endif -%}
add_cus_dep( "glo", "gls", 0, "makeglo" );
sub makeglo {
return system( "makeindex -s gglo.ist -o '$_[0].gls' '$_[0].glo'" );

View File

@ -3,11 +3,11 @@ headings_flag 1
heading_prefix " \\bigletter "
preamble "\\begin{sphinxtheindex}
\\def\\bigletter#1{{\\Large\\sffamily#1}\\nopagebreak\\vspace{1mm}}
\\let\\bigletter\\sphinxstyleindexlettergroup
"
postamble "\n\n\\end{sphinxtheindex}\n"
symhead_positive "{Symbols}"
numhead_positive "{Numbers}"
symhead_positive "{\\sphinxsymbolsname}"
numhead_positive "{\\sphinxnumbersname}"

View File

@ -6,7 +6,7 @@
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{sphinx}[2018/03/28 v1.8 LaTeX package (Sphinx markup)]
\ProvidesPackage{sphinx}[2018/06/30 v1.8 LaTeX package (Sphinx markup)]
% provides \ltx@ifundefined
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
@ -478,6 +478,11 @@
{\newenvironment{sphinxtheindex}{\begin{theindex}}{\end{theindex}}}%
{}% else clause of \ltx@ifundefined
% for usage with xindy: this string gets internationalized in preamble
\newcommand*{\sphinxnonalphabeticalgroupname}{}
% redefined in preamble, headings for makeindex produced index
\newcommand*{\sphinxsymbolsname}{}
\newcommand*{\sphinxnumbersname}{}
%% COLOR (general)
%
@ -1599,10 +1604,13 @@
{\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}}
% additional customizable styling
% FIXME: convert this to package options ?
\protected\def\sphinxstyleindexentry #1{\texttt{#1}}
\protected\def\sphinxstyleindexextra #1{ \emph{(#1)}}
\protected\def\sphinxstyleindexpageref #1{, \pageref{#1}}
\def\sphinxstyleindexentry #1{\texttt{#1}}
\def\sphinxstyleindexextra #1{ \emph{(#1)}}
\def\sphinxstyleindexpageref #1{, \pageref{#1}}
\def\sphinxstyleindexlettergroup #1%
{{\Large\sffamily#1}\nopagebreak\vspace{1mm}}
\def\sphinxstyleindexlettergroupDefault #1%
{{\Large\sffamily\sphinxnonalphabeticalgroupname}\nopagebreak\vspace{1mm}}
\protected\def\sphinxstyletopictitle #1{\textbf{#1}\par\medskip}
\let\sphinxstylesidebartitle\sphinxstyletopictitle
\protected\def\sphinxstyleothertitle #1{\textbf{#1}}

134
sphinx/texinputs/sphinx.xdy Normal file
View File

@ -0,0 +1,134 @@
;;; -*- mode: lisp; coding: utf-8; -*-
;; Unfortunately xindy is out-of-the-box hyperref-incompatible. This
;; configuration is a workaround, which requires to pass option
;; hyperindex=false to hyperref.
;; textit and emph not currently used by Sphinx LaTeX writer.
(define-attributes (("textbf" "textit" "emph" "default")))
(markup-locref :open "\textbf{\hyperpage{" :close "}}" :attr "textbf")
(markup-locref :open "\textit{\hyperpage{" :close "}}" :attr "textit")
(markup-locref :open "\emph{\hyperpage{" :close "}}" :attr "emph")
(markup-locref :open "\hyperpage{" :close "}" :attr "default")
(require "numeric-sort.xdy")
;; xindy base module latex.xdy loads tex.xdy and the latter instructs
;; xindy to ignore **all** TeX macros in .idx entries, except those
;; explicitely described in merge rule. But when after applying all
;; merge rules an empty string results, xindy raises an error:
;; ERROR: CHAR: index 0 should be less than the length of the string
;; For example when using pdflatex with utf-8 characters the index
;; file will contain \IeC macros and they will get ignored except if
;; suitable merge rules are loaded early. The texindy script coming
;; with xindy provides this, but only for Latin scripts. The texindy
;; man page says to use rather xelatex or lualatex in case of Cyrillic
;; scripts.
;; Sphinx contributes cyrLICRutf8.xdy to provide support for Cyrillic
;; scripts for the pdflatex engine.
;; Another issue caused by xindy ignoring all TeX macros except those
;; explicitely declared reveals itself when attempting to index ">>>",
;; as the ">" is converted to "\textgreater{}" by Sphinx's LaTeX
;; escaping.
;; To fix this, Sphinx does **not** use texindy, and does not even
;; load the xindy latex.xdy base module.
;(require "latex.xdy")
;; Rather it incorporates some suitable extracts from latex.xdy and
;; tex.xdy with additional Sphinx contributed rules.
;;;;;;;; extracts from tex.xdy (discarding most original comments):
;;;
;;; TeX conventions
;;;
;; Discard leading and trailing white space. Collapse multiple white
;; space characters to blank.
(merge-rule "^ +" "" :eregexp)
(merge-rule " +$" "" :eregexp)
(merge-rule " +" " " :eregexp)
;; Handle TeX markup
(merge-rule "\\([{}$%&#])" "\1" :eregexp)
;;;;;;;; end of extracts from xindy's tex.xdy
;;;;;;;; extracts from latex.xdy:
;; Standard location classes: arabic and roman numbers, and alphabets.
(define-location-class "arabic-page-numbers" ("arabic-numbers"))
(define-location-class "roman-page-numbers" ("roman-numbers-lowercase"))
(define-location-class "Roman-page-numbers" ("roman-numbers-uppercase"))
(define-location-class "alpha-page-numbers" ("alpha"))
(define-location-class "Alpha-page-numbers" ("ALPHA"))
;; Output Markup
(markup-letter-group-list :sep "~n~n \indexspace~n")
(markup-indexentry :open "~n \item " :depth 0)
(markup-indexentry :open "~n \subitem " :depth 1)
(markup-indexentry :open "~n \subsubitem " :depth 2)
(markup-locclass-list :open ", " :sep ", ")
(markup-locref-list :sep ", ")
;;;;;;;; end of extracts from latex.xdy
;; Sphinx additions, cf sphinx.util.texescape for rationale
;;
;; The LaTeX \index command turns \ into normal character so the TeX macros
;; written to .idx files are not followed by a blank. This is different
;; from non-ascii letters which end up (with pdflatex) as \IeC macros in .idx
;; file, with a blank.
(merge-rule "\\sphinxleftcurlybrace\{\}" "{")
(merge-rule "\\sphinxrightcurlybrace\{\}" "}")
(merge-rule "\\_" "_")
(merge-rule "\{\[\}" "[")
(merge-rule "\{\]\}" "]")
(merge-rule "\{\}`" "`")
(merge-rule "\\textbackslash\{\}" "\\")
(merge-rule "\\textasciitilde\{\}" "~~")
(merge-rule "\\textless\{\}" "<")
(merge-rule "\\textgreater\{\}" ">")
(merge-rule "\\textasciicircum\{\}" "^")
(merge-rule "\\P\{\}" "¶")
(merge-rule "\\S\{\}" "§")
(merge-rule "\\texteuro\{\}" "€")
(merge-rule "\\\(\\infty\\\)" "∞")
(merge-rule "\\\(\\pm\\\)" "±")
(merge-rule "\\\(\\rightarrow\\\)" "→")
(merge-rule "\\\(\\checkmark\\\)" "✓")
(merge-rule "\\textendash\{\}" "")
(merge-rule "\\textbar\{\}" "|")
;; This xindy module provides some basic support for "see"
(require "makeindex.xdy")
;; This creates one-letter headings and works fine with utf-8 letters.
;; For Cyrillic and pdflatex necessitates cyrLICRutf8.xdy to be loaded too.
(require "latin-lettergroups.xdy")
;; currently we don't (know how to easily) separate "Numbers" from
;; "Symbols" with xindy as is the case with makeindex.
(markup-index :open "\begin{sphinxtheindex}
\let\lettergroup\sphinxstyleindexlettergroup
\let\lettergroupDefault\sphinxstyleindexlettergroupDefault
"
:close "
\end{sphinxtheindex}
"
:tree)

View File

@ -241,7 +241,7 @@ def object_description(object):
if isinstance(object, dict):
try:
sorted_keys = sorted(object)
except TypeError:
except Exception:
pass # Cannot sort dict keys, fall back to generic repr
else:
items = ("%s: %s" %

View File

@ -24,7 +24,7 @@ from sphinx.util.console import colorize
if False:
# For type annotation
from typing import Any, Dict, Generator, IO, List, Tuple, Union # NOQA
from typing import Any, Dict, Generator, IO, List, Tuple, Type, Union # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
@ -94,22 +94,33 @@ def convert_serializable(records):
r.location = get_node_location(location) # type: ignore
class SphinxWarningLogRecord(logging.LogRecord):
class SphinxLogRecord(logging.LogRecord):
"""Log record class supporting location"""
prefix = ''
location = None # type: Any
def getMessage(self):
# type: () -> str
message = super(SphinxWarningLogRecord, self).getMessage()
message = super(SphinxLogRecord, self).getMessage()
location = getattr(self, 'location', None)
if location:
message = '%s: WARNING: %s' % (location, message)
elif 'WARNING:' not in message:
message = 'WARNING: %s' % message
message = '%s: %s%s' % (location, self.prefix, message)
elif self.prefix not in message:
message = self.prefix + message
return message
class SphinxInfoLogRecord(SphinxLogRecord):
"""Info log record class supporting location"""
prefix = '' # do not show any prefix for INFO messages
class SphinxWarningLogRecord(SphinxLogRecord):
"""Warning log record class supporting location"""
prefix = 'WARNING: '
class SphinxLoggerAdapter(logging.LoggerAdapter):
"""LoggerAdapter allowing ``type`` and ``subtype`` keywords."""
@ -412,21 +423,23 @@ class DisableWarningIsErrorFilter(logging.Filter):
return True
class WarningLogRecordTranslator(logging.Filter):
class SphinxLogRecordTranslator(logging.Filter):
"""Converts a log record to one Sphinx expects
* Make a instance of SphinxWarningLogRecord
* Make a instance of SphinxLogRecord
* docname to path if location given
"""
LogRecordClass = None # type: Type[logging.LogRecord]
def __init__(self, app):
# type: (Sphinx) -> None
self.app = app
super(WarningLogRecordTranslator, self).__init__()
super(SphinxLogRecordTranslator, self).__init__()
def filter(self, record): # type: ignore
# type: (SphinxWarningLogRecord) -> bool
if isinstance(record, logging.LogRecord):
record.__class__ = SphinxWarningLogRecord # force subclassing to handle location
record.__class__ = self.LogRecordClass # force subclassing to handle location
location = getattr(record, 'location', None)
if isinstance(location, tuple):
@ -445,6 +458,16 @@ class WarningLogRecordTranslator(logging.Filter):
return True
class InfoLogRecordTranslator(SphinxLogRecordTranslator):
"""LogRecordTranslator for INFO level log records."""
LogRecordClass = SphinxInfoLogRecord
class WarningLogRecordTranslator(SphinxLogRecordTranslator):
"""LogRecordTranslator for WARNING level log records."""
LogRecordClass = SphinxWarningLogRecord
def get_node_location(node):
# type: (nodes.Node) -> str
(source, line) = get_source_line(node)
@ -518,6 +541,7 @@ def setup(app, status, warning):
info_handler = NewLineStreamHandler(SafeEncodingWriter(status)) # type: ignore
info_handler.addFilter(InfoFilter())
info_handler.addFilter(InfoLogRecordTranslator(app))
info_handler.setLevel(VERBOSITY_MAP[app.verbosity])
info_handler.setFormatter(ColorizeFormatter())

View File

@ -130,7 +130,7 @@ DEFAULT_SETTINGS = {
'tocdepth': '',
'secnumdepth': '',
'pageautorefname': '',
'literalblockpto': '',
'translatablestrings': '',
} # type: Dict[unicode, unicode]
ADDITIONAL_SETTINGS = {
@ -497,6 +497,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'release': self.encode(builder.config.release),
'author': document.settings.author, # treat as a raw LaTeX code
'indexname': _('Index'),
'use_xindy': builder.config.latex_use_xindy,
})
if not self.elements['releasename'] and self.elements['release']:
self.elements.update({
@ -667,12 +668,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.elements['extraclassoptions']:
self.elements['classoptions'] += ',' + \
self.elements['extraclassoptions']
self.elements['literalblockpto'] = (
self.elements['translatablestrings'] = (
self.babel_renewcommand(
'\\literalblockcontinuedname', self.encode(_('continued from previous page'))
) +
self.babel_renewcommand(
'\\literalblockcontinuesname', self.encode(_('continues on next page'))
) +
self.babel_renewcommand(
'\\sphinxnonalphabeticalgroupname', self.encode(_('Non-alphabetical'))
) +
self.babel_renewcommand(
'\\sphinxsymbolsname', self.encode(_('Symbols'))
) +
self.babel_renewcommand(
'\\sphinxnumbersname', self.encode(_('Numbers'))
)
)
self.elements['pageautorefname'] = \
@ -856,8 +866,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def generate(content, collapsed):
# type: (List[Tuple[unicode, List[Tuple[unicode, unicode, unicode, unicode, unicode]]]], bool) -> None # NOQA
ret.append('\\begin{sphinxtheindex}\n')
ret.append('\\def\\bigletter#1{{\\Large\\sffamily#1}'
'\\nopagebreak\\vspace{1mm}}\n')
ret.append('\\let\\bigletter\\sphinxstyleindexlettergroup\n')
for i, (letter, entries) in enumerate(content):
if i > 0:
ret.append('\\indexspace\n')
@ -866,7 +875,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
for entry in entries:
if not entry[3]:
continue
ret.append('\\item {\\sphinxstyleindexentry{%s}}' % self.encode(entry[0]))
ret.append('\\item\\relax\\sphinxstyleindexentry{%s}' %
self.encode(entry[0]))
if entry[4]:
# add "extra" info
ret.append('\\sphinxstyleindexextra{%s}' % self.encode(entry[4]))
@ -1919,8 +1929,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
# type: (nodes.Node, Pattern) -> None
def escape(value):
value = self.encode(value)
value = value.replace(r'\{', r'{\sphinxleftcurlybrace}')
value = value.replace(r'\}', r'{\sphinxrightcurlybrace}')
value = value.replace(r'\{', r'\sphinxleftcurlybrace{}')
value = value.replace(r'\}', r'\sphinxrightcurlybrace{}')
value = value.replace('"', '""')
value = value.replace('@', '"@')
value = value.replace('!', '"!')
return value
if not node.get('inline', True):

View File

@ -1201,7 +1201,7 @@ def test_latex_index(app, status, warning):
result = (app.outdir / 'Python.tex').text(encoding='utf8')
assert 'A \\index{famous}famous \\index{equation}equation:\n' in result
assert '\n\\index{Einstein}\\index{relativity}\\ignorespaces \nand' in result
assert '\n\\index{main {\\sphinxleftcurlybrace}}\\ignorespaces ' in result
assert '\n\\index{main \\sphinxleftcurlybrace{}}\\ignorespaces ' in result
@pytest.mark.sphinx('latex', testroot='latex-equations')

View File

@ -81,6 +81,10 @@ def test_extract_summary(capsys):
doc = ['Blabla, i.e. bla.']
assert extract_summary(doc, document) == 'Blabla, i.e.'
# literal
doc = ['blah blah::']
assert extract_summary(doc, document) == 'blah blah.'
_, err = capsys.readouterr()
assert err == ''

View File

@ -171,6 +171,37 @@ def test_warningiserror(app, status, warning):
logger.warning('%s')
def test_info_location(app, status, warning):
logging.setup(app, status, warning)
logger = logging.getLogger(__name__)
logger.info('message1', location='index')
assert 'index.txt: message1' in status.getvalue()
logger.info('message2', location=('index', 10))
assert 'index.txt:10: message2' in status.getvalue()
logger.info('message3', location=None)
assert '\nmessage3' in status.getvalue()
node = nodes.Node()
node.source, node.line = ('index.txt', 10)
logger.info('message4', location=node)
assert 'index.txt:10: message4' in status.getvalue()
node.source, node.line = ('index.txt', None)
logger.info('message5', location=node)
assert 'index.txt:: message5' in status.getvalue()
node.source, node.line = (None, 10)
logger.info('message6', location=node)
assert '<unknown>:10: message6' in status.getvalue()
node.source, node.line = (None, None)
logger.info('message7', location=node)
assert '\nmessage7' in status.getvalue()
def test_warning_location(app, status, warning):
logging.setup(app, status, warning)
logger = logging.getLogger(__name__)