Merge branch 'master' into 5163_hlist_aligned_to_top

This commit is contained in:
Takeshi KOMIYA 2018-07-19 00:32:34 +09:00 committed by GitHub
commit f84df710e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 791 additions and 184 deletions

43
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.
* #5163: html: hlist items are now aligned to top
Deprecated
@ -144,6 +153,14 @@ Features added
for mathjax
* #4362: latex: Don't overwrite .tex file if document not changed
* #1431: latex: Add alphanumeric enumerated list support
* Extend to all projects not using the Sphinx LaTeX Makefile a workaround for
`issue rtfd/readthedocs.org#2857`__ about insufficient number of
``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
----------
@ -153,6 +170,9 @@ 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
* #5114: sphinx-build: Handle errors on scanning documents
Testing
--------
@ -168,7 +188,7 @@ Documentation
* #5083: Fix wrong make.bat option for internationalization.
* #5115: napoleon: add admonitions added by #4613 to the docs.
Release 1.7.6 (in development)
Release 1.7.7 (in development)
==============================
Dependencies
@ -186,6 +206,15 @@ Features added
Bugs fixed
----------
Testing
--------
Release 1.7.6 (released Jul 17, 2018)
=====================================
Bugs fixed
----------
* #5037: LaTeX ``\sphinxupquote{}`` breaks in Russian
* sphinx.testing uses deprecated pytest API; ``Node.get_marker(name)``
* #5016: crashed when recommonmark.AutoStrictify is enabled
@ -203,6 +232,7 @@ 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
* #4272: PDF builds of French projects have issues with XeTeX
* #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
@ -212,9 +242,14 @@ Bugs fixed
* autosummary: warnings of autosummary indicates wrong location (refs: #5146)
* #5143: autodoc: crashed on inspecting dict like object which does not support
sorting
Testing
--------
* #5139: autodoc: Enum argument missing if it shares value with another
* #4946: py domain: rtype field could not handle "None" as a type
* #5176: LaTeX: indexing of terms containing ``@``, ``!``, or ``"`` fails
* #5161: html: crashes if copying static files are failed
* #5167: autodoc: Fix formatting type annotations for tuples with more than two
arguments
* #3329: i18n: crashed by auto-symbol footnote references
* #5158: autosummary: module summary has been broken when it starts with heading
Release 1.7.5 (released May 29, 2018)
=====================================

View File

@ -64,14 +64,10 @@ latex_elements = {
'passoptionstopackages': '\\PassOptionsToPackage{svgnames}{xcolor}',
'preamble': '\\DeclareUnicodeCharacter{229E}{\\ensuremath{\\boxplus}}',
'fvset': '\\fvset{fontsize=auto}',
# fix missing index entry due to RTD doing only once pdflatex after makeindex
'printindex': r'''
\IfFileExists{\jobname.ind}
{\footnotesize\raggedright\printindex}
{\begin{sphinxtheindex}\end{sphinxtheindex}}
''',
'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

@ -12,7 +12,7 @@ __ http://docutils.sourceforge.net/docs/dev/hacking.html#parsing-the-document
In Sphinx, the parser modules works as same as docutils. The parsers are
registered to Sphinx by extensions using Application APIs;
:meth:`.Sphinx.add_source_suffix()` and :meth:`.Sphinx.add_source_parsers()`.
:meth:`.Sphinx.add_source_suffix()` and :meth:`.Sphinx.add_source_parser()`.
The *source suffix* is a mapping from file suffix to file type. For example,
``.rst`` file is mapped to ``'restructuredtext'`` type. Sphinx uses the

View File

@ -55,7 +55,7 @@ example::
\setlength{\cftsecnumwidth}{1.25cm}
''',
'fncychap': r'\usepackage[Bjornstrup]{fncychap}',
'printindex': r'\footnotesize\raggedright\printindex',
'printindex': r'\footnotesize\raggedright\sphinxprintindex',
}
latex_show_urls = 'footnote'
@ -399,6 +399,23 @@ Macros
:file:`sphinxmanual.cls` and :file:`sphinxhowto.cls`.
- the citation reference is typeset via ``\sphinxcite`` which is a wrapper
of standard ``\cite``.
- regarding the general index, the :confval:`latex_elements` dictionary has a
``'printindex'`` key which defaults to ``'\\sphinxprintindex'``. It often
proves advantageous to use::
'printindex': '\\footnotesize\\raggedright\\sphinxprintindex',
especially if the index contains long entries. The LaTeX class for Japanese
``'manual'``-type documents already does the font size and text
justification change, so the above is not needed then.
.. tip::
Advanced LaTeX users can also, via :confval:`latex_additional_files`, use
a custom :program:`makeindex` style file :file:`python.ist`, or a custom
:program:`xindy` style file :file:`sphinx.xdy` (see
:confval:`latex_use_xindy`) in order to modify how the general index is
typeset.
Environments
~~~~~~~~~~~~
@ -453,7 +470,8 @@ Environments
- the bibliography uses ``sphinxthebibliography`` and the Python Module index
as well as the general index both use ``sphinxtheindex``; these environments
are wrappers of the ``thebibliography`` and respectively ``theindex``
environments as provided by the document class (or packages).
environments, needed mainly to insert a corresponding entry in the PDF
bookmarks and table of contents.
.. versionchanged:: 1.5
formerly, the original environments were modified by Sphinx.

View File

@ -1727,8 +1727,8 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
Options for LaTeX output
------------------------
These options influence LaTeX output. Refer to :doc:`/latex` for more
information.
These options influence LaTeX output. For customization of LaTeX
macros and environments, see also :doc:`/latex`.
.. confval:: latex_engine
@ -1872,6 +1872,40 @@ information.
.. 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
@ -2108,11 +2142,14 @@ information.
Remove unneeded ``{}`` after ``\\hrule``.
``'printindex'``
"printindex" call, the last thing in the file, default
``'\\printindex'``. Override if you want to generate the index
differently or append some content after the index. For example
``'\\footnotesize\\raggedright\\printindex'`` is advisable when the
index is full of long entries.
"printindex" call, the last thing in the file.
.. versionchanged:: 1.8
Former default ``'\\printindex'`` now ``'\\sphinxprintindex'``.
This macro works around an issue__ with PDF builds at RTD doing too
few ``pdflatex`` runs.
__ https://github.com/rtfd/readthedocs.org/issues/2857
``'fvset'``
Customization of ``fancyvrb`` LaTeX package. Defaults to

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

@ -31,7 +31,7 @@ directory = sphinx/locale/
[flake8]
max-line-length = 95
ignore = E116,E241,E251,E741,I101
ignore = E116,E241,E251,E741,W504,I101
exclude = .git,.tox,.venv,tests/*,build/*,doc/_build/*,sphinx/search/*,doc/usage/extensions/example*.py
application-import-names = sphinx
import-order-style = smarkets

View File

@ -848,85 +848,94 @@ class StandaloneHTMLBuilder(Builder):
try:
copyfile(path.join(self.srcdir, src),
path.join(self.outdir, '_downloads', dest))
except Exception as err:
except EnvironmentError as err:
logger.warning(__('cannot copy downloadable file %r: %s'),
path.join(self.srcdir, src), err)
def copy_static_files(self):
# type: () -> None
# copy static files
logger.info(bold(__('copying static files... ')), nonl=True)
ensuredir(path.join(self.outdir, '_static'))
# first, create pygments style file
with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
f.write(self.highlighter.get_stylesheet()) # type: ignore
# then, copy translations JavaScript file
if self.config.language is not None:
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static',
'translations.js'))
try:
# copy static files
logger.info(bold(__('copying static files... ')), nonl=True)
ensuredir(path.join(self.outdir, '_static'))
# first, create pygments style file
with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
f.write(self.highlighter.get_stylesheet()) # type: ignore
# then, copy translations JavaScript file
if self.config.language is not None:
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static',
'translations.js'))
# copy non-minified stemmer JavaScript file
if self.indexer is not None:
jsfile = self.indexer.get_js_stemmer_rawcode()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
# copy non-minified stemmer JavaScript file
if self.indexer is not None:
jsfile = self.indexer.get_js_stemmer_rawcode()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
ctx = self.globalcontext.copy()
ctx = self.globalcontext.copy()
# add context items for search function used in searchtools.js_t
if self.indexer is not None:
ctx.update(self.indexer.context_for_searchtool())
# add context items for search function used in searchtools.js_t
if self.indexer is not None:
ctx.update(self.indexer.context_for_searchtool())
# then, copy over theme-supplied static files
if self.theme:
for theme_path in self.theme.get_theme_dirs()[::-1]:
entry = path.join(theme_path, 'static')
copy_asset(entry, path.join(self.outdir, '_static'), excluded=DOTFILES,
# then, copy over theme-supplied static files
if self.theme:
for theme_path in self.theme.get_theme_dirs()[::-1]:
entry = path.join(theme_path, 'static')
copy_asset(entry, path.join(self.outdir, '_static'), excluded=DOTFILES,
context=ctx, renderer=self.templates)
# then, copy over all user-supplied static files
excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
for static_path in self.config.html_static_path:
entry = path.join(self.confdir, static_path)
if not path.exists(entry):
logger.warning(__('html_static_path entry %r does not exist'), entry)
continue
copy_asset(entry, path.join(self.outdir, '_static'), excluded,
context=ctx, renderer=self.templates)
# then, copy over all user-supplied static files
excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
for static_path in self.config.html_static_path:
entry = path.join(self.confdir, static_path)
if not path.exists(entry):
logger.warning(__('html_static_path entry %r does not exist'), entry)
continue
copy_asset(entry, path.join(self.outdir, '_static'), excluded,
context=ctx, renderer=self.templates)
# copy logo and favicon files if not already in static path
if self.config.html_logo:
logobase = path.basename(self.config.html_logo)
logotarget = path.join(self.outdir, '_static', logobase)
if not path.isfile(path.join(self.confdir, self.config.html_logo)):
logger.warning(__('logo file %r does not exist'), self.config.html_logo)
elif not path.isfile(logotarget):
copyfile(path.join(self.confdir, self.config.html_logo),
logotarget)
if self.config.html_favicon:
iconbase = path.basename(self.config.html_favicon)
icontarget = path.join(self.outdir, '_static', iconbase)
if not path.isfile(path.join(self.confdir, self.config.html_favicon)):
logger.warning(__('favicon file %r does not exist'), self.config.html_favicon)
elif not path.isfile(icontarget):
copyfile(path.join(self.confdir, self.config.html_favicon),
icontarget)
logger.info('done')
# copy logo and favicon files if not already in static path
if self.config.html_logo:
logobase = path.basename(self.config.html_logo)
logotarget = path.join(self.outdir, '_static', logobase)
if not path.isfile(path.join(self.confdir, self.config.html_logo)):
logger.warning(__('logo file %r does not exist'), self.config.html_logo)
elif not path.isfile(logotarget):
copyfile(path.join(self.confdir, self.config.html_logo),
logotarget)
if self.config.html_favicon:
iconbase = path.basename(self.config.html_favicon)
icontarget = path.join(self.outdir, '_static', iconbase)
if not path.isfile(path.join(self.confdir, self.config.html_favicon)):
logger.warning(__('favicon file %r does not exist'),
self.config.html_favicon)
elif not path.isfile(icontarget):
copyfile(path.join(self.confdir, self.config.html_favicon),
icontarget)
logger.info('done')
except EnvironmentError as err:
# TODO: In py3, EnvironmentError (and IOError) was merged into OSError.
# So it should be replaced by IOError on dropping py2 support
logger.warning(__('cannot copy static file %r'), err)
def copy_extra_files(self):
# type: () -> None
# copy html_extra_path files
logger.info(bold(__('copying extra files... ')), nonl=True)
excluded = Matcher(self.config.exclude_patterns)
try:
# copy html_extra_path files
logger.info(bold(__('copying extra files... ')), nonl=True)
excluded = Matcher(self.config.exclude_patterns)
for extra_path in self.config.html_extra_path:
entry = path.join(self.confdir, extra_path)
if not path.exists(entry):
logger.warning(__('html_extra_path entry %r does not exist'), entry)
continue
for extra_path in self.config.html_extra_path:
entry = path.join(self.confdir, extra_path)
if not path.exists(entry):
logger.warning(__('html_extra_path entry %r does not exist'), entry)
continue
copy_asset(entry, self.outdir, excluded)
logger.info(__('done'))
copy_asset(entry, self.outdir, excluded)
logger.info(__('done'))
except EnvironmentError as err:
logger.warning(__('cannot copy extra file %r'), err)
def write_buildinfo(self):
# type: () -> None

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

@ -160,7 +160,7 @@ class CheckExternalLinksBuilder(Builder):
# the server and the network
response = requests.head(req_url, config=self.app.config, **kwargs)
response.raise_for_status()
except HTTPError as err:
except HTTPError:
# retry with GET request if that fails, some servers
# don't like HEAD requests.
response = requests.get(req_url, stream=True, config=self.app.config,

View File

@ -168,7 +168,15 @@ class PyXrefMixin(object):
class PyField(PyXrefMixin, Field):
pass
def make_xref(self, rolename, domain, target,
innernode=nodes.emphasis, contnode=None, env=None):
# type: (unicode, unicode, unicode, nodes.Node, nodes.Node, BuildEnvironment) -> nodes.Node # NOQA
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
rolename = 'obj'
return super(PyField, self).make_xref(rolename, domain, target,
innernode, contnode, env)
class PyGroupedField(PyXrefMixin, GroupedField):

View File

@ -25,7 +25,7 @@ from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, BuildEnvironmentError, ExtensionError
from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
from sphinx.locale import __
from sphinx.transforms import SphinxTransformer
from sphinx.util import get_matching_docs, FilenameUniqDict
@ -416,37 +416,40 @@ class BuildEnvironment(object):
"""Find all source files in the source dir and put them in
self.found_docs.
"""
matchers = compile_matchers(
config.exclude_patterns[:] +
config.templates_path +
builder.get_asset_paths() +
['**/_sources', '.#*', '**/.#*', '*.lproj/**']
)
self.found_docs = set()
for docname in get_matching_docs(self.srcdir, config.source_suffix, # type: ignore
exclude_matchers=matchers):
if os.access(self.doc2path(docname), os.R_OK):
self.found_docs.add(docname)
else:
logger.warning(__("document not readable. Ignored."), location=docname)
try:
matchers = compile_matchers(
config.exclude_patterns[:] +
config.templates_path +
builder.get_asset_paths() +
['**/_sources', '.#*', '**/.#*', '*.lproj/**']
)
self.found_docs = set()
for docname in get_matching_docs(self.srcdir, config.source_suffix, # type: ignore
exclude_matchers=matchers):
if os.access(self.doc2path(docname), os.R_OK):
self.found_docs.add(docname)
else:
logger.warning(__("document not readable. Ignored."), location=docname)
# Current implementation is applying translated messages in the reading
# phase.Therefore, in order to apply the updated message catalog, it is
# necessary to re-process from the reading phase. Here, if dependency
# is set for the doc source and the mo file, it is processed again from
# the reading phase when mo is updated. In the future, we would like to
# move i18n process into the writing phase, and remove these lines.
if builder.use_message_catalog:
# add catalog mo file dependency
for docname in self.found_docs:
catalog_files = find_catalog_files(
docname,
self.srcdir,
self.config.locale_dirs,
self.config.language,
self.config.gettext_compact)
for filename in catalog_files:
self.dependencies[docname].add(filename)
# Current implementation is applying translated messages in the reading
# phase.Therefore, in order to apply the updated message catalog, it is
# necessary to re-process from the reading phase. Here, if dependency
# is set for the doc source and the mo file, it is processed again from
# the reading phase when mo is updated. In the future, we would like to
# move i18n process into the writing phase, and remove these lines.
if builder.use_message_catalog:
# add catalog mo file dependency
for docname in self.found_docs:
catalog_files = find_catalog_files(
docname,
self.srcdir,
self.config.locale_dirs,
self.config.language,
self.config.gettext_compact)
for filename in catalog_files:
self.dependencies[docname].add(filename)
except EnvironmentError as exc:
raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc))
def get_outdated_files(self, config_changed):
# type: (bool) -> Tuple[Set[unicode], Set[unicode], Set[unicode]]

View File

@ -82,6 +82,11 @@ class ConfigError(SphinxError):
category = 'Configuration error'
class DocumentError(SphinxError):
"""Document error."""
category = 'Document error'
class ThemeError(SphinxError):
"""Theme error."""
category = 'Theme error'

View File

@ -221,12 +221,21 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None):
for name, value in subject.__members__.items():
obj_dict[name] = value
members = {}
members = {} # type: Dict[str, Attribute]
# enum members
if isenumclass(subject):
for name, value in subject.__members__.items():
if name not in members:
members[name] = Attribute(name, True, value)
# other members
for name in dir(subject):
try:
value = attrgetter(subject, name)
directly_defined = name in obj_dict
members[name] = Attribute(name, directly_defined, value)
if name not in members:
members[name] = Attribute(name, directly_defined, value)
except AttributeError:
continue

View File

@ -473,21 +473,32 @@ def extract_summary(doc, document):
doc = doc[:i]
break
# Try to find the "first sentence", which may span multiple lines
sentences = periods_re.split(" ".join(doc)) # type: ignore
if len(sentences) == 1:
summary = sentences[0].strip()
if doc == []:
return ''
# parse the docstring
state_machine = RSTStateMachine(state_classes, 'Body')
node = new_document('', document.settings)
node.reporter = NullReporter()
state_machine.run(doc, node)
if not isinstance(node[0], nodes.paragraph):
# document starts with non-paragraph: pick up the first line
summary = doc[0].strip()
else:
summary = ''
state_machine = RSTStateMachine(state_classes, 'Body')
while sentences:
summary += sentences.pop(0) + '.'
node = new_document('', document.settings)
node.reporter = NullReporter()
state_machine.run([summary], node)
if not node.traverse(nodes.system_message):
# considered as that splitting by period does not break inline markups
break
# Try to find the "first sentence", which may span multiple lines
sentences = periods_re.split(" ".join(doc)) # type: ignore
if len(sentences) == 1:
summary = sentences[0].strip()
else:
summary = ''
while sentences:
summary += sentences.pop(0) + '.'
node[:] = []
state_machine.run([summary], node)
if not node.traverse(nodes.system_message):
# 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)

View File

@ -269,7 +269,7 @@ def find_autosummary_in_docstring(name, module=None, filename=None):
pass
except ImportError as e:
print("Failed to import '%s': %s" % (name, e))
except SystemExit as e:
except SystemExit:
print("Failed to import '%s'; the module executes module level "
"statement and it might call sys.exit()." % name)
return []

View File

@ -39,6 +39,7 @@ if False:
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.config import Config # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc import Documenter # NOQA
@ -270,7 +271,7 @@ class SphinxComponentRegistry(object):
# type: (unicode, unicode, bool) -> None
logger.debug('[app] adding source_suffix: %r, %r', suffix, filetype)
if suffix in self.source_suffix and not override:
raise ExtensionError(__('source_parser for %r is already registered') % suffix)
raise ExtensionError(__('source_suffix %r is already registered') % suffix)
else:
self.source_suffix[suffix] = filetype
@ -290,7 +291,7 @@ class SphinxComponentRegistry(object):
parser = args[1]
if suffix:
self.add_source_suffix(suffix, suffix)
self.add_source_suffix(suffix, suffix, override=True)
if len(parser.supported) == 0:
warnings.warn('Old source_parser has been detected. Please fill Parser.supported '
@ -493,8 +494,8 @@ class SphinxComponentRegistry(object):
return envversion
def merge_source_suffix(app):
# type: (Sphinx) -> None
def merge_source_suffix(app, config):
# type: (Sphinx, Config) -> None
"""Merge source_suffix which specified by user and added by extensions."""
for suffix, filetype in iteritems(app.registry.source_suffix):
if suffix not in app.config.source_suffix:
@ -510,7 +511,7 @@ def merge_source_suffix(app):
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.connect('builder-inited', merge_source_suffix)
app.connect('config-inited', merge_source_suffix)
return {
'version': 'builtin',

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

@ -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
@ -459,6 +459,14 @@
}
\fi
% provide \sphinxprintindex as variant of \printindex designed to workaround
% rtfd/readthedocs.org#2857 regarding "Index" entry in PDF bookmarks
\newcommand*{\sphinxprintindex}{%
\IfFileExists{\jobname.ind}
{\printindex}
{\begin{sphinxtheindex}\end{sphinxtheindex}}%
}%
% make commands known to non-Sphinx document classes
\providecommand*{\sphinxtableofcontents}{\tableofcontents}
\ltx@ifundefined{sphinxthebibliography}
@ -470,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)
%
@ -1591,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}}

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

@ -0,0 +1,141 @@
;;; -*- 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.
;; Details of the syntax are explained at
;; http://xindy.sourceforge.net/doc/manual-3.html
;; In absence of :string, "xindy uses an auto-detection mechanism to decide,
;; if the pattern is a regular expression or not". But it is not obvious to
;; guess, for example "\\_" is not detected as RE but "\\P\{\}" is, so for
;; being sure we apply the :string switch everywhere and do not use \\ etc...
(merge-rule "\sphinxleftcurlybrace{}" "{" :string)
(merge-rule "\sphinxrightcurlybrace{}" "}" :string)
(merge-rule "\_" "_" :string)
(merge-rule "{[}" "[" :string)
(merge-rule "{]}" "]" :string)
(merge-rule "{}`" "`" :string)
(merge-rule "\textbackslash{}" "\" :string) ; " for Emacs syntax highlighting
(merge-rule "\textasciitilde{}" "~~" :string); the ~~ escape is needed here
(merge-rule "\textless{}" "<" :string)
(merge-rule "\textgreater{}" ">" :string)
(merge-rule "\textasciicircum{}" "^" :string)
(merge-rule "\P{}" "¶" :string)
(merge-rule "\S{}" "§" :string)
(merge-rule "\texteuro{}" "€" :string)
(merge-rule "\(\infty\)" "∞" :string)
(merge-rule "\(\pm\)" "±" :string)
(merge-rule "\(\rightarrow\)" "→" :string)
(merge-rule "\(\checkmark\)" "✓" :string)
(merge-rule "\textendash{}" "" :string)
(merge-rule "\textbar{}" "|" :string)
;; 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

@ -276,10 +276,9 @@ class Locale(SphinxTransform):
continue # skip
# auto-numbered foot note reference should use original 'ids'.
def is_autonumber_footnote_ref(node):
def is_autofootnote_ref(node):
# type: (nodes.Node) -> bool
return isinstance(node, nodes.footnote_reference) and \
node.get('auto') == 1
return isinstance(node, nodes.footnote_reference) and node.get('auto')
def list_replace_or_append(lst, old, new):
# type: (List, Any, Any) -> None
@ -287,8 +286,8 @@ class Locale(SphinxTransform):
lst[lst.index(old)] = new
else:
lst.append(new)
old_foot_refs = node.traverse(is_autonumber_footnote_ref)
new_foot_refs = patch.traverse(is_autonumber_footnote_ref)
old_foot_refs = node.traverse(is_autofootnote_ref)
new_foot_refs = patch.traverse(is_autofootnote_ref)
if len(old_foot_refs) != len(new_foot_refs):
old_foot_ref_rawsources = [ref.rawsource for ref in old_foot_refs]
new_foot_ref_rawsources = [ref.rawsource for ref in new_foot_refs]
@ -309,8 +308,14 @@ class Locale(SphinxTransform):
new['ids'] = old['ids']
for id in new['ids']:
self.document.ids[id] = new
list_replace_or_append(
self.document.autofootnote_refs, old, new)
if new['auto'] == 1:
# autofootnote_refs
list_replace_or_append(self.document.autofootnote_refs, old, new)
else:
# symbol_footnote_refs
list_replace_or_append(self.document.symbol_footnote_refs, old, new)
if refname:
list_replace_or_append(
self.document.footnote_refs.setdefault(refname, []),

View File

@ -532,6 +532,13 @@ class Signature(object):
if annotation.__module__ == 'builtins':
return annotation.__qualname__ # type: ignore
elif (hasattr(typing, 'TupleMeta') and
isinstance(annotation, typing.TupleMeta) and # type: ignore
not hasattr(annotation, '__tuple_params__')):
# This is for Python 3.6+, 3.5 case is handled below
params = annotation.__args__
param_str = ', '.join(self.format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str)
elif (hasattr(typing, 'GenericMeta') and # for py36 or below
isinstance(annotation, typing.GenericMeta)):
# In Python 3.5.2+, all arguments are stored in __args__,

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

@ -124,13 +124,13 @@ DEFAULT_SETTINGS = {
'maketitle': '\\maketitle',
'tableofcontents': '\\sphinxtableofcontents',
'atendofbody': '',
'printindex': '\\printindex',
'printindex': '\\sphinxprintindex',
'transition': '\n\n\\bigskip\\hrule\\bigskip\n\n',
'figure_align': 'htbp',
'tocdepth': '',
'secnumdepth': '',
'pageautorefname': '',
'literalblockpto': '',
'translatablestrings': '',
} # type: Dict[unicode, unicode]
ADDITIONAL_SETTINGS = {
@ -485,6 +485,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# sort out some elements
self.elements = DEFAULT_SETTINGS.copy()
self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {}))
# for xelatex+French, don't use polyglossia
if self.elements['latex_engine'] == 'xelatex':
if builder.config.language:
if builder.config.language[:2] == 'fr':
self.elements.update({
'polyglossia': '',
'babel': '\\usepackage{babel}',
})
# allow the user to override them all
self.check_latex_elements()
self.elements.update(builder.config.latex_elements)
@ -497,6 +505,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 +676,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 +874,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 +883,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 +1937,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

@ -233,3 +233,4 @@ class EnumCls(enum.Enum):
val2 = 23 #: doc for val2
val3 = 34
"""doc for val3"""
val4 = 34

View File

@ -19,8 +19,8 @@ msgstr ""
msgid "i18n with Footnote"
msgstr "I18N WITH FOOTNOTE"
msgid "[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_."
msgstr "`I18N WITH FOOTNOTE`_ INCLUDE THIS CONTENTS [#named]_ [ref]_ [#]_ [100]_."
msgid "[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_ [*]_."
msgstr "`I18N WITH FOOTNOTE`_ INCLUDE THIS CONTENTS [#named]_ [ref]_ [#]_ [100]_ [*]_."
msgid "This is a auto numbered footnote."
msgstr "THIS IS A AUTO NUMBERED FOOTNOTE."
@ -34,3 +34,5 @@ msgstr "THIS IS A NUMBERED FOOTNOTE."
msgid "This is a auto numbered named footnote."
msgstr "THIS IS A AUTO NUMBERED NAMED FOOTNOTE."
msgid "This is a auto symbol footnote."
msgstr "THIS IS A AUTO SYMBOL FOOTNOTE."

View File

@ -4,9 +4,10 @@ i18n with Footnote
==================
.. #955 cant-build-html-with-footnotes-when-using
[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_.
[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_ [#named]_ [*]_.
.. [#] This is a auto numbered footnote.
.. [ref] This is a named footnote.
.. [100] This is a numbered footnote.
.. [#named] This is a auto numbered named footnote.
.. [*] This is a auto symbol footnote.

View File

@ -873,13 +873,14 @@ def test_generate():
# test members with enum attributes
directive.env.ref_context['py:module'] = 'target'
options.inherited_members = False
options.undoc_members = False
options.undoc_members = True
options.members = ALL
assert_processes([
('class', 'target.EnumCls'),
('attribute', 'target.EnumCls.val1'),
('attribute', 'target.EnumCls.val2'),
('attribute', 'target.EnumCls.val3'),
('attribute', 'target.EnumCls.val4'),
], 'class', 'EnumCls')
assert_result_contains(
' :annotation: = 12', 'attribute', 'EnumCls.val1')

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

@ -85,6 +85,11 @@ def test_extract_summary(capsys):
doc = ['blah blah::']
assert extract_summary(doc, document) == 'blah blah.'
# heading
doc = ['blah blah',
'=========']
assert extract_summary(doc, document) == 'blah blah'
_, err = capsys.readouterr()
assert err == ''

View File

@ -794,7 +794,7 @@ def test_xml_footnotes(app, warning):
assert_elem(
para0[0],
['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS',
'2', '[ref]', '1', '100', '.'],
'2', '[ref]', '1', '100', '*', '.'],
['i18n-with-footnote', 'ref'])
footnote0 = secs[0].findall('footnote')
@ -813,6 +813,11 @@ def test_xml_footnotes(app, warning):
['2', 'THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'],
None,
['named'])
assert_elem(
footnote0[3],
['*', 'THIS IS A AUTO SYMBOL FOOTNOTE.'],
None,
None)
citation0 = secs[0].findall('citation')
assert_elem(

View File

@ -231,7 +231,7 @@ def test_Signature_partialmethod():
@pytest.mark.skipif(sys.version_info < (3, 5),
reason='type annotation test is available on py35 or above')
def test_Signature_annotations():
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12
# Class annotations
sig = inspect.Signature(f0).format_args()
@ -284,6 +284,10 @@ def test_Signature_annotations():
sig = inspect.Signature(f11, has_retval=False).format_args()
assert sig == '(x: CustomAnnotation, y: 123)'
# tuple with more than two items
sig = inspect.Signature(f12).format_args()
assert sig == '() -> Tuple[int, str, int]'
def test_safe_getattr_with_default():
class Foo(object):

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__)

View File

@ -62,3 +62,7 @@ class CustomAnnotation:
def f11(x: CustomAnnotation(), y: 123) -> None:
pass
def f12() -> Tuple[int, str, int]:
pass