mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into 6793_sidebar_in_texinfo
This commit is contained in:
commit
250ce7425a
2
AUTHORS
2
AUTHORS
@ -36,7 +36,7 @@ Other contributors, listed alphabetically, are:
|
|||||||
* Hernan Grecco -- search improvements
|
* Hernan Grecco -- search improvements
|
||||||
* Horst Gutmann -- internationalization support
|
* Horst Gutmann -- internationalization support
|
||||||
* Martin Hans -- autodoc improvements
|
* Martin Hans -- autodoc improvements
|
||||||
* Zac Hatfield-Dodds -- doctest reporting improvements
|
* Zac Hatfield-Dodds -- doctest reporting improvements, intersphinx performance
|
||||||
* Doug Hellmann -- graphviz improvements
|
* Doug Hellmann -- graphviz improvements
|
||||||
* Tim Hoffmann -- theme improvements
|
* Tim Hoffmann -- theme improvements
|
||||||
* Antti Kaihola -- doctest extension (skipif option)
|
* Antti Kaihola -- doctest extension (skipif option)
|
||||||
|
20
CHANGES
20
CHANGES
@ -9,10 +9,13 @@ Incompatible changes
|
|||||||
|
|
||||||
* #6742: ``end-before`` option of :rst:dir:`literalinclude` directive does not
|
* #6742: ``end-before`` option of :rst:dir:`literalinclude` directive does not
|
||||||
match the first line of the code block.
|
match the first line of the code block.
|
||||||
|
* #1331: Change default User-Agent header to ``"Sphinx/X.Y.Z requests/X.Y.Z
|
||||||
|
python/X.Y.Z"``. It can be changed via :confval:`user_agent`.
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* ``sphinx.builders.gettext.POHEADER``
|
||||||
* ``sphinx.io.SphinxStandaloneReader.app``
|
* ``sphinx.io.SphinxStandaloneReader.app``
|
||||||
* ``sphinx.io.SphinxStandaloneReader.env``
|
* ``sphinx.io.SphinxStandaloneReader.env``
|
||||||
|
|
||||||
@ -23,6 +26,13 @@ Features added
|
|||||||
* #267: html: Eliminate prompt characters of doctest block from copyable text
|
* #267: html: Eliminate prompt characters of doctest block from copyable text
|
||||||
* #6729: html theme: agogo theme now supports ``rightsidebar`` option
|
* #6729: html theme: agogo theme now supports ``rightsidebar`` option
|
||||||
* #6780: Add PEP-561 Support
|
* #6780: Add PEP-561 Support
|
||||||
|
* #6762: latex: Allow to load additonal LaTeX packages via ``extrapackages`` key
|
||||||
|
of :confval:`latex_elements`
|
||||||
|
* #1331: Add new config variable: :confval:`user_agent`
|
||||||
|
* #6000: LaTeX: have backslash also be an inline literal word wrap break
|
||||||
|
character
|
||||||
|
* #6812: Improve a warning message when extensions are not parallel safe
|
||||||
|
* #6818: Improve Intersphinx performance for multiple remote inventories.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
@ -32,6 +42,9 @@ Bugs fixed
|
|||||||
|
|
||||||
.. _latex3/latex2e#173: https://github.com/latex3/latex2e/issues/173
|
.. _latex3/latex2e#173: https://github.com/latex3/latex2e/issues/173
|
||||||
* #6618: LaTeX: Avoid section names at the end of a page
|
* #6618: LaTeX: Avoid section names at the end of a page
|
||||||
|
* #6738: LaTeX: Do not replace unicode characters by LaTeX macros on unicode
|
||||||
|
supported LaTeX engines: ¶, §, €, ∞, ±, →, ‣, –, superscript and subscript
|
||||||
|
digits go through "as is" (as default OpenType font supports them)
|
||||||
* #6704: linkcheck: Be defensive and handle newly defined HTTP error code
|
* #6704: linkcheck: Be defensive and handle newly defined HTTP error code
|
||||||
* #6655: image URLs containing ``data:`` causes gettext builder crashed
|
* #6655: image URLs containing ``data:`` causes gettext builder crashed
|
||||||
* #6584: i18n: Error when compiling message catalogs on Hindi
|
* #6584: i18n: Error when compiling message catalogs on Hindi
|
||||||
@ -42,6 +55,11 @@ Bugs fixed
|
|||||||
* #5070: epub: Wrong internal href fragment links
|
* #5070: epub: Wrong internal href fragment links
|
||||||
* #6712: Allow not to install sphinx.testing as runtime (mainly for ALT Linux)
|
* #6712: Allow not to install sphinx.testing as runtime (mainly for ALT Linux)
|
||||||
* #6741: html: search result was broken with empty :confval:`html_file_suffix`
|
* #6741: html: search result was broken with empty :confval:`html_file_suffix`
|
||||||
|
* #6001: LaTeX does not wrap long code lines at backslash character
|
||||||
|
* #6804: LaTeX: PDF build breaks if admonition of danger type contains
|
||||||
|
code-block long enough not to fit on one page
|
||||||
|
* #6809: LaTeX: code-block in a danger type admonition can easily spill over
|
||||||
|
bottom of page
|
||||||
* #6793: texinfo: Code examples broken following "sidebar"
|
* #6793: texinfo: Code examples broken following "sidebar"
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
@ -65,6 +83,8 @@ Features added
|
|||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* #6776: LaTeX: 2019-10-01 LaTeX release breaks :file:`sphinxcyrillic.sty`
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -26,6 +26,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- (will be) Removed
|
- (will be) Removed
|
||||||
- Alternatives
|
- Alternatives
|
||||||
|
|
||||||
|
* - ``sphinx.builders.gettext.POHEADER``
|
||||||
|
- 2.3
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx/templates/gettext/message.pot_t`` (template file)
|
||||||
|
|
||||||
* - ``sphinx.io.SphinxStandaloneReader.app``
|
* - ``sphinx.io.SphinxStandaloneReader.app``
|
||||||
- 2.3
|
- 2.3
|
||||||
- 4.0
|
- 4.0
|
||||||
|
@ -226,6 +226,25 @@ into the generated ``.tex`` files. Its ``'sphinxsetup'`` key is described
|
|||||||
|
|
||||||
.. versionadded:: 1.5
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
``'extrapackages'``
|
||||||
|
Additional LaTeX packages. For example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
'packages': r'\usepackage{isodate}'
|
||||||
|
}
|
||||||
|
|
||||||
|
It defaults to empty.
|
||||||
|
|
||||||
|
The specified LaTeX packages will be loaded before
|
||||||
|
hyperref package and packages loaded from Sphinx extensions.
|
||||||
|
|
||||||
|
.. hint:: If you'd like to load additional LaTeX packages after hyperref, use
|
||||||
|
``'preamble'`` key instead.
|
||||||
|
|
||||||
|
.. versionadded:: 2.3
|
||||||
|
|
||||||
``'footer'``
|
``'footer'``
|
||||||
Additional footer content (before the indices), default empty.
|
Additional footer content (before the indices), default empty.
|
||||||
|
|
||||||
@ -600,12 +619,15 @@ macros may be significant.
|
|||||||
default ``true``. Allows linebreaks inside inline literals: but extra
|
default ``true``. Allows linebreaks inside inline literals: but extra
|
||||||
potential break-points (additionally to those allowed by LaTeX at spaces
|
potential break-points (additionally to those allowed by LaTeX at spaces
|
||||||
or for hyphenation) are currently inserted only after the characters
|
or for hyphenation) are currently inserted only after the characters
|
||||||
``. , ; ? ! /``. Due to TeX internals, white space in the line will be
|
``. , ; ? ! /`` and ``\``. Due to TeX internals, white space in the line
|
||||||
stretched (or shrunk) in order to accomodate the linebreak.
|
will be stretched (or shrunk) in order to accomodate the linebreak.
|
||||||
|
|
||||||
.. versionadded:: 1.5
|
.. versionadded:: 1.5
|
||||||
set this option value to ``false`` to recover former behaviour.
|
set this option value to ``false`` to recover former behaviour.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3.0
|
||||||
|
added potential breakpoint at ``\`` characters.
|
||||||
|
|
||||||
``verbatimvisiblespace``
|
``verbatimvisiblespace``
|
||||||
default ``\textcolor{red}{\textvisiblespace}``. When a long code line is
|
default ``\textcolor{red}{\textvisiblespace}``. When a long code line is
|
||||||
split, the last space character from the source code line right before the
|
split, the last space character from the source code line right before the
|
||||||
|
@ -510,6 +510,14 @@ General configuration
|
|||||||
|
|
||||||
.. versionadded:: 1.6.6
|
.. versionadded:: 1.6.6
|
||||||
|
|
||||||
|
.. confval:: user_agent
|
||||||
|
|
||||||
|
A User-Agent of Sphinx. It is used for a header on HTTP access (ex.
|
||||||
|
linkcheck, intersphinx and so on). Default is ``"Sphinx/X.Y.Z
|
||||||
|
requests/X.Y.Z python/X.Y.Z"``.
|
||||||
|
|
||||||
|
.. versionadded:: 2.3
|
||||||
|
|
||||||
.. confval:: tls_verify
|
.. confval:: tls_verify
|
||||||
|
|
||||||
If true, Sphinx verifies server certifications. Default is ``True``.
|
If true, Sphinx verifies server certifications. Default is ``True``.
|
||||||
|
4
setup.py
4
setup.py
@ -176,6 +176,10 @@ setup(
|
|||||||
description='Python documentation generator',
|
description='Python documentation generator',
|
||||||
long_description=long_desc,
|
long_description=long_desc,
|
||||||
long_description_content_type='text/x-rst',
|
long_description_content_type='text/x-rst',
|
||||||
|
project_urls={
|
||||||
|
"Code": "https://github.com/sphinx-doc/sphinx",
|
||||||
|
"Issue tracker": "https://github.com/sphinx-doc/sphinx/issues",
|
||||||
|
},
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
@ -1192,26 +1192,30 @@ class Sphinx:
|
|||||||
"""
|
"""
|
||||||
if typ == 'read':
|
if typ == 'read':
|
||||||
attrname = 'parallel_read_safe'
|
attrname = 'parallel_read_safe'
|
||||||
message = __("the %s extension does not declare if it is safe "
|
message_not_declared = __("the %s extension does not declare if it "
|
||||||
"for parallel reading, assuming it isn't - please "
|
"is safe for parallel reading, assuming "
|
||||||
"ask the extension author to check and make it "
|
"it isn't - please ask the extension author "
|
||||||
"explicit")
|
"to check and make it explicit")
|
||||||
|
message_not_safe = __("the %s extension is not safe for parallel reading")
|
||||||
elif typ == 'write':
|
elif typ == 'write':
|
||||||
attrname = 'parallel_write_safe'
|
attrname = 'parallel_write_safe'
|
||||||
message = __("the %s extension does not declare if it is safe "
|
message_not_declared = __("the %s extension does not declare if it "
|
||||||
"for parallel writing, assuming it isn't - please "
|
"is safe for parallel writing, assuming "
|
||||||
"ask the extension author to check and make it "
|
"it isn't - please ask the extension author "
|
||||||
"explicit")
|
"to check and make it explicit")
|
||||||
|
message_not_safe = __("the %s extension is not safe for parallel writing")
|
||||||
else:
|
else:
|
||||||
raise ValueError('parallel type %s is not supported' % typ)
|
raise ValueError('parallel type %s is not supported' % typ)
|
||||||
|
|
||||||
for ext in self.extensions.values():
|
for ext in self.extensions.values():
|
||||||
allowed = getattr(ext, attrname, None)
|
allowed = getattr(ext, attrname, None)
|
||||||
if allowed is None:
|
if allowed is None:
|
||||||
logger.warning(message, ext.name)
|
logger.warning(message_not_declared, ext.name)
|
||||||
logger.warning(__('doing serial %s'), typ)
|
logger.warning(__('doing serial %s'), typ)
|
||||||
return False
|
return False
|
||||||
elif not allowed:
|
elif not allowed:
|
||||||
|
logger.warning(message_not_safe, ext.name)
|
||||||
|
logger.warning(__('doing serial %s'), typ)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
from codecs import open
|
from codecs import open
|
||||||
from collections import defaultdict, OrderedDict
|
from collections import defaultdict, OrderedDict
|
||||||
from datetime import datetime, tzinfo, timedelta
|
from datetime import datetime, tzinfo, timedelta
|
||||||
from io import StringIO
|
|
||||||
from os import path, walk, getenv
|
from os import path, walk, getenv
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Any, Dict, Iterable, List, Set, Tuple, Union
|
from typing import Any, Dict, Iterable, Generator, List, Set, Tuple, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.nodes import Element
|
from docutils.nodes import Element
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
|
from sphinx import package_dir
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.domains.python import pairindextypes
|
from sphinx.domains.python import pairindextypes
|
||||||
@ -30,8 +30,9 @@ from sphinx.util import split_index_msg, logging, status_iterator
|
|||||||
from sphinx.util.console import bold # type: ignore
|
from sphinx.util.console import bold # type: ignore
|
||||||
from sphinx.util.i18n import CatalogInfo, docname_to_domain
|
from sphinx.util.i18n import CatalogInfo, docname_to_domain
|
||||||
from sphinx.util.nodes import extract_messages, traverse_translatable_index
|
from sphinx.util.nodes import extract_messages, traverse_translatable_index
|
||||||
from sphinx.util.osutil import relpath, ensuredir, canon_path
|
from sphinx.util.osutil import ensuredir, canon_path
|
||||||
from sphinx.util.tags import Tags
|
from sphinx.util.tags import Tags
|
||||||
|
from sphinx.util.template import SphinxRenderer
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -58,7 +59,15 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
"""[1:]
|
"""[1:] # RemovedInSphinx40Warning
|
||||||
|
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
"""An entry of translatable message."""
|
||||||
|
def __init__(self, text: str, locations: List[Tuple[str, int]], uuids: List[str]):
|
||||||
|
self.text = text
|
||||||
|
self.locations = locations
|
||||||
|
self.uuids = uuids
|
||||||
|
|
||||||
|
|
||||||
class Catalog:
|
class Catalog:
|
||||||
@ -80,6 +89,12 @@ class Catalog:
|
|||||||
self.metadata[msg] = []
|
self.metadata[msg] = []
|
||||||
self.metadata[msg].append((origin.source, origin.line, origin.uid)) # type: ignore
|
self.metadata[msg].append((origin.source, origin.line, origin.uid)) # type: ignore
|
||||||
|
|
||||||
|
def __iter__(self) -> Generator[Message, None, None]:
|
||||||
|
for message in self.messages:
|
||||||
|
positions = [(source, line) for source, line, uuid in self.metadata[message]]
|
||||||
|
uuids = [uuid for source, line, uuid in self.metadata[message]]
|
||||||
|
yield Message(message, positions, uuids)
|
||||||
|
|
||||||
|
|
||||||
class MsgOrigin:
|
class MsgOrigin:
|
||||||
"""
|
"""
|
||||||
@ -92,6 +107,22 @@ class MsgOrigin:
|
|||||||
self.uid = uuid4().hex
|
self.uid = uuid4().hex
|
||||||
|
|
||||||
|
|
||||||
|
class GettextRenderer(SphinxRenderer):
|
||||||
|
def __init__(self, template_path: str = None) -> None:
|
||||||
|
if template_path is None:
|
||||||
|
template_path = path.join(package_dir, 'templates', 'gettext')
|
||||||
|
super().__init__(template_path)
|
||||||
|
|
||||||
|
def escape(s: str) -> str:
|
||||||
|
s = s.replace('\\', r'\\')
|
||||||
|
s = s.replace('"', r'\"')
|
||||||
|
return s.replace('\n', '\\n"\n"')
|
||||||
|
|
||||||
|
# use texescape as escape filter
|
||||||
|
self.env.filters['e'] = escape
|
||||||
|
self.env.filters['escape'] = escape
|
||||||
|
|
||||||
|
|
||||||
class I18nTags(Tags):
|
class I18nTags(Tags):
|
||||||
"""Dummy tags module for I18nBuilder.
|
"""Dummy tags module for I18nBuilder.
|
||||||
|
|
||||||
@ -247,12 +278,13 @@ class MessageCatalogBuilder(I18nBuilder):
|
|||||||
|
|
||||||
def finish(self) -> None:
|
def finish(self) -> None:
|
||||||
super().finish()
|
super().finish()
|
||||||
data = {
|
context = {
|
||||||
'version': self.config.version,
|
'version': self.config.version,
|
||||||
'copyright': self.config.copyright,
|
'copyright': self.config.copyright,
|
||||||
'project': self.config.project,
|
'project': self.config.project,
|
||||||
'ctime': datetime.fromtimestamp(
|
'ctime': datetime.fromtimestamp(timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'),
|
||||||
timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'),
|
'display_location': self.config.gettext_location,
|
||||||
|
'display_uuid': self.config.gettext_uuid,
|
||||||
}
|
}
|
||||||
for textdomain, catalog in status_iterator(self.catalogs.items(),
|
for textdomain, catalog in status_iterator(self.catalogs.items(),
|
||||||
__("writing message catalogs... "),
|
__("writing message catalogs... "),
|
||||||
@ -262,30 +294,10 @@ class MessageCatalogBuilder(I18nBuilder):
|
|||||||
# noop if config.gettext_compact is set
|
# noop if config.gettext_compact is set
|
||||||
ensuredir(path.join(self.outdir, path.dirname(textdomain)))
|
ensuredir(path.join(self.outdir, path.dirname(textdomain)))
|
||||||
|
|
||||||
|
context['messages'] = list(catalog)
|
||||||
|
content = GettextRenderer().render('message.pot_t', context)
|
||||||
|
|
||||||
pofn = path.join(self.outdir, textdomain + '.pot')
|
pofn = path.join(self.outdir, textdomain + '.pot')
|
||||||
output = StringIO()
|
|
||||||
output.write(POHEADER % data)
|
|
||||||
|
|
||||||
for message in catalog.messages:
|
|
||||||
positions = catalog.metadata[message]
|
|
||||||
|
|
||||||
if self.config.gettext_location:
|
|
||||||
# generate "#: file1:line1\n#: file2:line2 ..."
|
|
||||||
output.write("#: %s\n" % "\n#: ".join(
|
|
||||||
"%s:%s" % (canon_path(relpath(source, self.outdir)), line)
|
|
||||||
for source, line, _ in positions))
|
|
||||||
if self.config.gettext_uuid:
|
|
||||||
# generate "# uuid1\n# uuid2\n ..."
|
|
||||||
output.write("# %s\n" % "\n# ".join(uid for _, _, uid in positions))
|
|
||||||
|
|
||||||
# message contains *one* line of text ready for translation
|
|
||||||
message = message.replace('\\', r'\\'). \
|
|
||||||
replace('"', r'\"'). \
|
|
||||||
replace('\n', '\\n"\n"')
|
|
||||||
output.write('msgid "%s"\nmsgstr ""\n\n' % message)
|
|
||||||
|
|
||||||
content = output.getvalue()
|
|
||||||
|
|
||||||
if should_write(pofn, content):
|
if should_write(pofn, content):
|
||||||
with open(pofn, 'w', encoding='utf-8') as pofile:
|
with open(pofn, 'w', encoding='utf-8') as pofile:
|
||||||
pofile.write(content)
|
pofile.write(content)
|
||||||
|
@ -101,7 +101,6 @@ class CheckExternalLinksBuilder(Builder):
|
|||||||
'allow_redirects': True,
|
'allow_redirects': True,
|
||||||
'headers': {
|
'headers': {
|
||||||
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
|
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
|
||||||
'User-Agent': requests.useragent_header[0][1],
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if self.app.config.linkcheck_timeout:
|
if self.app.config.linkcheck_timeout:
|
||||||
|
@ -148,6 +148,7 @@ class Config:
|
|||||||
'math_numfig': (True, 'env', []),
|
'math_numfig': (True, 'env', []),
|
||||||
'tls_verify': (True, 'env', []),
|
'tls_verify': (True, 'env', []),
|
||||||
'tls_cacerts': (None, 'env', []),
|
'tls_cacerts': (None, 'env', []),
|
||||||
|
'user_agent': (None, 'env', [str]),
|
||||||
'smartquotes': (True, 'env', []),
|
'smartquotes': (True, 'env', []),
|
||||||
'smartquotes_action': ('qDe', 'env', []),
|
'smartquotes_action': ('qDe', 'env', []),
|
||||||
'smartquotes_excludes': ({'languages': ['ja'],
|
'smartquotes_excludes': ({'languages': ['ja'],
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import concurrent.futures
|
||||||
import functools
|
import functools
|
||||||
import posixpath
|
import posixpath
|
||||||
import sys
|
import sys
|
||||||
@ -187,21 +188,18 @@ def fetch_inventory(app: Sphinx, uri: str, inv: Any) -> Any:
|
|||||||
return invdata
|
return invdata
|
||||||
|
|
||||||
|
|
||||||
def load_mappings(app: Sphinx) -> None:
|
def fetch_inventory_group(
|
||||||
"""Load all intersphinx mappings into the environment."""
|
name: str, uri: str, invs: Any, cache: Any, app: Any, now: float
|
||||||
now = int(time.time())
|
) -> bool:
|
||||||
cache_time = now - app.config.intersphinx_cache_limit * 86400
|
cache_time = now - app.config.intersphinx_cache_limit * 86400
|
||||||
inventories = InventoryAdapter(app.builder.env)
|
failures = []
|
||||||
update = False
|
try:
|
||||||
for key, (name, (uri, invs)) in app.config.intersphinx_mapping.items():
|
|
||||||
failures = []
|
|
||||||
for inv in invs:
|
for inv in invs:
|
||||||
if not inv:
|
if not inv:
|
||||||
inv = posixpath.join(uri, INVENTORY_FILENAME)
|
inv = posixpath.join(uri, INVENTORY_FILENAME)
|
||||||
# decide whether the inventory must be read: always read local
|
# decide whether the inventory must be read: always read local
|
||||||
# files; remote ones only if the cache time is expired
|
# files; remote ones only if the cache time is expired
|
||||||
if '://' not in inv or uri not in inventories.cache \
|
if '://' not in inv or uri not in cache or cache[uri][1] < cache_time:
|
||||||
or inventories.cache[uri][1] < cache_time:
|
|
||||||
safe_inv_url = _get_safe_url(inv)
|
safe_inv_url = _get_safe_url(inv)
|
||||||
logger.info(__('loading intersphinx inventory from %s...'), safe_inv_url)
|
logger.info(__('loading intersphinx inventory from %s...'), safe_inv_url)
|
||||||
try:
|
try:
|
||||||
@ -209,12 +207,11 @@ def load_mappings(app: Sphinx) -> None:
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
failures.append(err.args)
|
failures.append(err.args)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if invdata:
|
if invdata:
|
||||||
inventories.cache[uri] = (name, now, invdata)
|
cache[uri] = (name, now, invdata)
|
||||||
update = True
|
return True
|
||||||
break
|
return False
|
||||||
|
finally:
|
||||||
if failures == []:
|
if failures == []:
|
||||||
pass
|
pass
|
||||||
elif len(failures) < len(invs):
|
elif len(failures) < len(invs):
|
||||||
@ -227,7 +224,21 @@ def load_mappings(app: Sphinx) -> None:
|
|||||||
logger.warning(__("failed to reach any of the inventories "
|
logger.warning(__("failed to reach any of the inventories "
|
||||||
"with the following issues:") + "\n" + issues)
|
"with the following issues:") + "\n" + issues)
|
||||||
|
|
||||||
if update:
|
|
||||||
|
def load_mappings(app: Sphinx) -> None:
|
||||||
|
"""Load all intersphinx mappings into the environment."""
|
||||||
|
now = int(time.time())
|
||||||
|
inventories = InventoryAdapter(app.builder.env)
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||||
|
futures = []
|
||||||
|
for name, (uri, invs) in app.config.intersphinx_mapping.values():
|
||||||
|
futures.append(pool.submit(
|
||||||
|
fetch_inventory_group, name, uri, invs, inventories.cache, app, now
|
||||||
|
))
|
||||||
|
updated = [f.result() for f in concurrent.futures.as_completed(futures)]
|
||||||
|
|
||||||
|
if any(updated):
|
||||||
inventories.clear()
|
inventories.clear()
|
||||||
|
|
||||||
# Duplicate values in different inventories will shadow each
|
# Duplicate values in different inventories will shadow each
|
||||||
@ -374,6 +385,7 @@ def inspect_main(argv: List[str]) -> None:
|
|||||||
class MockConfig:
|
class MockConfig:
|
||||||
intersphinx_timeout = None # type: int
|
intersphinx_timeout = None # type: int
|
||||||
tls_verify = False
|
tls_verify = False
|
||||||
|
user_agent = None
|
||||||
|
|
||||||
class MockApp:
|
class MockApp:
|
||||||
srcdir = ''
|
srcdir = ''
|
||||||
|
@ -30,7 +30,7 @@ from sphinx.locale import _, __
|
|||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.docutils import SphinxDirective
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
from sphinx.util.texescape import tex_escape_map
|
from sphinx.util.texescape import get_escape_func
|
||||||
from sphinx.writers.html import HTMLTranslator
|
from sphinx.writers.html import HTMLTranslator
|
||||||
from sphinx.writers.latex import LaTeXTranslator
|
from sphinx.writers.latex import LaTeXTranslator
|
||||||
|
|
||||||
@ -299,10 +299,11 @@ def depart_todo_node(self: HTMLTranslator, node: todo_node) -> None:
|
|||||||
|
|
||||||
def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
|
def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
|
||||||
if self.config.todo_include_todos:
|
if self.config.todo_include_todos:
|
||||||
|
escape = get_escape_func(self.config.latex_engine)
|
||||||
self.body.append('\n\\begin{sphinxadmonition}{note}{')
|
self.body.append('\n\\begin{sphinxadmonition}{note}{')
|
||||||
self.body.append(self.hypertarget_to(node))
|
self.body.append(self.hypertarget_to(node))
|
||||||
title_node = cast(nodes.title, node[0])
|
title_node = cast(nodes.title, node[0])
|
||||||
self.body.append('%s:}' % title_node.astext().translate(tex_escape_map))
|
self.body.append('%s:}' % escape(title_node.astext()))
|
||||||
node.pop(0)
|
node.pop(0)
|
||||||
else:
|
else:
|
||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
|
@ -28,7 +28,7 @@ from sphinx.ext import doctest
|
|||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.pygments_styles import SphinxStyle, NoneStyle
|
from sphinx.pygments_styles import SphinxStyle, NoneStyle
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.texescape import tex_hl_escape_map_new
|
from sphinx.util.texescape import get_hlescape_func, tex_hl_escape_map_new
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -68,9 +68,11 @@ class PygmentsBridge:
|
|||||||
html_formatter = HtmlFormatter
|
html_formatter = HtmlFormatter
|
||||||
latex_formatter = LatexFormatter
|
latex_formatter = LatexFormatter
|
||||||
|
|
||||||
def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None):
|
def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None,
|
||||||
# type: (str, str, bool) -> None
|
latex_engine=None):
|
||||||
|
# type: (str, str, bool, str) -> None
|
||||||
self.dest = dest
|
self.dest = dest
|
||||||
|
self.latex_engine = latex_engine
|
||||||
|
|
||||||
style = self.get_style(stylename)
|
style = self.get_style(stylename)
|
||||||
self.formatter_args = {'style': style} # type: Dict[str, Any]
|
self.formatter_args = {'style': style} # type: Dict[str, Any]
|
||||||
@ -192,7 +194,8 @@ class PygmentsBridge:
|
|||||||
if self.dest == 'html':
|
if self.dest == 'html':
|
||||||
return hlsource
|
return hlsource
|
||||||
else:
|
else:
|
||||||
return hlsource.translate(tex_hl_escape_map_new)
|
escape = get_hlescape_func(self.latex_engine)
|
||||||
|
return escape(hlsource)
|
||||||
|
|
||||||
def get_stylesheet(self):
|
def get_stylesheet(self):
|
||||||
# type: () -> str
|
# type: () -> str
|
||||||
|
33
sphinx/templates/gettext/message.pot_t
Normal file
33
sphinx/templates/gettext/message.pot_t
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) {{ copyright }}
|
||||||
|
# This file is distributed under the same license as the {{ project }} package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: {{ project|e }} {{ version|e }}\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: {{ ctime|e }}\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if display_location -%}
|
||||||
|
{% for source, line in message.locations -%}
|
||||||
|
#: {{ source }}:{{ line }}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{% if display_uuid -%}
|
||||||
|
{% for uuid in message.uuids -%}
|
||||||
|
#: {{ uuid }}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
msgid "{{ message.text|e }}"
|
||||||
|
msgstr ""
|
||||||
|
{% endfor -%}
|
@ -35,6 +35,7 @@
|
|||||||
<%= sphinxsetup %>
|
<%= sphinxsetup %>
|
||||||
<%= fvset %>
|
<%= fvset %>
|
||||||
<%= geometry %>
|
<%= geometry %>
|
||||||
|
<%= extrapackages %>
|
||||||
|
|
||||||
<%- for name, option in packages %>
|
<%- for name, option in packages %>
|
||||||
<%- if option %>
|
<%- if option %>
|
||||||
|
@ -1054,7 +1054,7 @@
|
|||||||
% Take advantage of the already applied Pygments mark-up to insert
|
% Take advantage of the already applied Pygments mark-up to insert
|
||||||
% potential linebreaks for TeX processing.
|
% potential linebreaks for TeX processing.
|
||||||
% {, <, #, %, $, ' and ": go to next line.
|
% {, <, #, %, $, ' and ": go to next line.
|
||||||
% _, }, ^, &, >, - and ~: stay at end of broken line.
|
% _, }, ^, &, >, -, ~, and \: stay at end of broken line.
|
||||||
% Use of \textquotesingle for straight quote.
|
% Use of \textquotesingle for straight quote.
|
||||||
% FIXME: convert this to package options ?
|
% FIXME: convert this to package options ?
|
||||||
\newcommand*\sphinxbreaksbeforelist {%
|
\newcommand*\sphinxbreaksbeforelist {%
|
||||||
@ -1066,6 +1066,7 @@
|
|||||||
\newcommand*\sphinxbreaksafterlist {%
|
\newcommand*\sphinxbreaksafterlist {%
|
||||||
\do\PYGZus\_\do\PYGZcb\}\do\PYGZca\^\do\PYGZam\&% _, }, ^, &,
|
\do\PYGZus\_\do\PYGZcb\}\do\PYGZca\^\do\PYGZam\&% _, }, ^, &,
|
||||||
\do\PYGZgt\>\do\PYGZhy\-\do\PYGZti\~% >, -, ~
|
\do\PYGZgt\>\do\PYGZhy\-\do\PYGZti\~% >, -, ~
|
||||||
|
\do\PYGZbs\\% \
|
||||||
}
|
}
|
||||||
\newcommand*\sphinxbreaksatspecials {%
|
\newcommand*\sphinxbreaksatspecials {%
|
||||||
\def\do##1##2%
|
\def\do##1##2%
|
||||||
@ -1110,6 +1111,9 @@
|
|||||||
\newcommand*\sphinxVerbatimTitle {}
|
\newcommand*\sphinxVerbatimTitle {}
|
||||||
% This box to typeset the caption before framed.sty multiple passes for framing.
|
% This box to typeset the caption before framed.sty multiple passes for framing.
|
||||||
\newbox\sphinxVerbatim@TitleBox
|
\newbox\sphinxVerbatim@TitleBox
|
||||||
|
% This box to measure contents if nested as inner \MakeFramed requires then
|
||||||
|
% minipage encapsulation but too long contents then break outer \MakeFramed
|
||||||
|
\newbox\sphinxVerbatim@ContentsBox
|
||||||
% This is a workaround to a "feature" of French lists, when literal block
|
% This is a workaround to a "feature" of French lists, when literal block
|
||||||
% follows immediately; usable generally (does only \par then), a priori...
|
% follows immediately; usable generally (does only \par then), a priori...
|
||||||
\newcommand*\sphinxvspacefixafterfrenchlists{%
|
\newcommand*\sphinxvspacefixafterfrenchlists{%
|
||||||
@ -1256,17 +1260,23 @@
|
|||||||
\itemsep \z@skip
|
\itemsep \z@skip
|
||||||
\topsep \z@skip
|
\topsep \z@skip
|
||||||
\partopsep \z@skip
|
\partopsep \z@skip
|
||||||
% trivlist will set \parsep to \parskip = zero
|
% trivlist will set \parsep to \parskip (which itself is set to zero above)
|
||||||
% \leftmargin will be set to zero by trivlist
|
% \leftmargin will be set to zero by trivlist
|
||||||
\rightmargin\z@
|
\rightmargin\z@
|
||||||
\parindent \z@% becomes \itemindent. Default zero, but perhaps overwritten.
|
\parindent \z@% becomes \itemindent. Default zero, but perhaps overwritten.
|
||||||
\trivlist\item\relax
|
\trivlist\item\relax
|
||||||
\ifsphinxverbatimwithminipage\spx@inframedtrue\fi
|
\ifspx@inframed\setbox\sphinxVerbatim@ContentsBox\vbox\bgroup
|
||||||
% use a minipage if we are already inside a framed environment
|
\@setminipage\hsize\linewidth
|
||||||
\ifspx@inframed\noindent\begin{minipage}{\linewidth}\fi
|
% use bulk of minipage paragraph shape restores (this is needed
|
||||||
\MakeFramed {% adapted over from framed.sty's snugshade environment
|
% in indented contexts, at least for some)
|
||||||
|
\textwidth\hsize \columnwidth\hsize \@totalleftmargin\z@
|
||||||
|
\leftskip\z@skip \rightskip\z@skip \@rightskip\z@skip
|
||||||
|
\else
|
||||||
|
\ifsphinxverbatimwithminipage\noindent\begin{minipage}{\linewidth}\fi
|
||||||
|
\MakeFramed {% adapted over from framed.sty's snugshade environment
|
||||||
\advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize\@setminipage
|
\advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize\@setminipage
|
||||||
}%
|
}%
|
||||||
|
\fi
|
||||||
% For grid placement from \strut's in \FancyVerbFormatLine
|
% For grid placement from \strut's in \FancyVerbFormatLine
|
||||||
\lineskip\z@skip
|
\lineskip\z@skip
|
||||||
% active comma should not be overwritten by \@noligs
|
% active comma should not be overwritten by \@noligs
|
||||||
@ -1278,8 +1288,49 @@
|
|||||||
}
|
}
|
||||||
{%
|
{%
|
||||||
\endOriginalVerbatim
|
\endOriginalVerbatim
|
||||||
\par\unskip\@minipagefalse\endMakeFramed % from framed.sty snugshade
|
\ifspx@inframed
|
||||||
\ifspx@inframed\end{minipage}\fi
|
\egroup % finish \sphinxVerbatim@ContentsBox vbox
|
||||||
|
\nobreak % update page totals
|
||||||
|
\ifdim\dimexpr\ht\sphinxVerbatim@ContentsBox+
|
||||||
|
\dp\sphinxVerbatim@ContentsBox+
|
||||||
|
\ht\sphinxVerbatim@TitleBox+
|
||||||
|
\dp\sphinxVerbatim@TitleBox+
|
||||||
|
2\fboxsep+2\fboxrule+
|
||||||
|
% try to account for external frame parameters
|
||||||
|
\FrameSep+\FrameRule+
|
||||||
|
% Usage here of 2 baseline distances is empirical.
|
||||||
|
% In border case where code-block fits barely in remaining space,
|
||||||
|
% it gets framed and looks good but the outer frame may continue
|
||||||
|
% on top of next page and give (if no contents after code-block)
|
||||||
|
% an empty framed line, as testing showed.
|
||||||
|
2\baselineskip+
|
||||||
|
% now add all to accumulated page totals and compare to \pagegoal
|
||||||
|
\pagetotal+\pagedepth>\pagegoal
|
||||||
|
% long contents: do not \MakeFramed. Do make a caption (either before or
|
||||||
|
% after) if title exists. Continuation hints across pagebreaks dropped.
|
||||||
|
% FIXME? a bottom caption may end up isolated at top of next page
|
||||||
|
% (no problem with a top caption, which is default)
|
||||||
|
\spx@opt@verbatimwithframefalse
|
||||||
|
\def\sphinxVerbatim@Title{\noindent\box\sphinxVerbatim@TitleBox\par}%
|
||||||
|
\sphinxVerbatim@Before
|
||||||
|
\noindent\unvbox\sphinxVerbatim@ContentsBox\par
|
||||||
|
\sphinxVerbatim@After
|
||||||
|
\else
|
||||||
|
% short enough contents: use \MakeFramed. As it is nested, this requires
|
||||||
|
% minipage encapsulation.
|
||||||
|
\noindent\begin{minipage}{\linewidth}%
|
||||||
|
\MakeFramed {% Use it now with the fetched contents
|
||||||
|
\advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize\@setminipage
|
||||||
|
}%
|
||||||
|
\unvbox\sphinxVerbatim@ContentsBox
|
||||||
|
% some of this may be superfluous:
|
||||||
|
\par\unskip\@minipagefalse\endMakeFramed
|
||||||
|
\end{minipage}%
|
||||||
|
\fi
|
||||||
|
\else % non-nested \MakeFramed
|
||||||
|
\par\unskip\@minipagefalse\endMakeFramed % from framed.sty snugshade
|
||||||
|
\ifsphinxverbatimwithminipage\end{minipage}\fi
|
||||||
|
\fi
|
||||||
\endtrivlist
|
\endtrivlist
|
||||||
}
|
}
|
||||||
\newenvironment {sphinxVerbatimNoFrame}
|
\newenvironment {sphinxVerbatimNoFrame}
|
||||||
@ -1314,6 +1365,7 @@
|
|||||||
{\def##1{\discretionary{\char`##2}{\sphinxafterbreak}{\char`##2}}}%
|
{\def##1{\discretionary{\char`##2}{\sphinxafterbreak}{\char`##2}}}%
|
||||||
\do\_\_\do\}\}\do\textasciicircum\^\do\&\&% _, }, ^, &,
|
\do\_\_\do\}\}\do\textasciicircum\^\do\&\&% _, }, ^, &,
|
||||||
\do\textgreater\>\do\textasciitilde\~% >, ~
|
\do\textgreater\>\do\textasciitilde\~% >, ~
|
||||||
|
\do\textbackslash\\% \
|
||||||
}
|
}
|
||||||
\newcommand*\sphinxbreaksviaactiveinparsedliteral{%
|
\newcommand*\sphinxbreaksviaactiveinparsedliteral{%
|
||||||
\sphinxbreaksviaactive % by default handles . , ; ? ! /
|
\sphinxbreaksviaactive % by default handles . , ; ? ! /
|
||||||
@ -1736,13 +1788,25 @@
|
|||||||
% to obtain straight quotes we execute \@noligs as patched by upquote, and
|
% to obtain straight quotes we execute \@noligs as patched by upquote, and
|
||||||
% \scantokens is needed in cases where it would be too late for the macro to
|
% \scantokens is needed in cases where it would be too late for the macro to
|
||||||
% first set catcodes and then fetch its argument. We also make the contents
|
% first set catcodes and then fetch its argument. We also make the contents
|
||||||
% breakable at non-escaped . , ; ? ! / using \sphinxbreaksviaactive.
|
% breakable at non-escaped . , ; ? ! / using \sphinxbreaksviaactive,
|
||||||
|
% and also at \ character (which is escaped to \textbackslash{}).
|
||||||
|
\protected\def\sphinxtextbackslashbreakbefore
|
||||||
|
{\discretionary{}{\sphinxafterbreak\sphinx@textbackslash}{\sphinx@textbackslash}}
|
||||||
|
\protected\def\sphinxtextbackslashbreakafter
|
||||||
|
{\discretionary{\sphinx@textbackslash}{\sphinxafterbreak}{\sphinx@textbackslash}}
|
||||||
|
\let\sphinxtextbackslash\sphinxtextbackslashbreakafter
|
||||||
% the macro must be protected if it ends up used in moving arguments,
|
% the macro must be protected if it ends up used in moving arguments,
|
||||||
% in 'alltt' \@noligs is done already, and the \scantokens must be avoided.
|
% in 'alltt' \@noligs is done already, and the \scantokens must be avoided.
|
||||||
\protected\def\sphinxupquote#1{{\def\@tempa{alltt}%
|
\protected\def\sphinxupquote#1{{\def\@tempa{alltt}%
|
||||||
\ifx\@tempa\@currenvir\else
|
\ifx\@tempa\@currenvir\else
|
||||||
\ifspx@opt@inlineliteralwraps
|
\ifspx@opt@inlineliteralwraps
|
||||||
\sphinxbreaksviaactive\let\sphinxafterbreak\empty
|
% break at . , ; ? ! /
|
||||||
|
\sphinxbreaksviaactive
|
||||||
|
% break also at \
|
||||||
|
\let\sphinx@textbackslash\textbackslash
|
||||||
|
\let\textbackslash\sphinxtextbackslash
|
||||||
|
% do not typeset a continuation symbol on next line
|
||||||
|
\let\sphinxafterbreak\sphinxafterbreakofinlineliteral
|
||||||
% do not overwrite the comma set-up
|
% do not overwrite the comma set-up
|
||||||
\let\verbatim@nolig@list\sphinx@literal@nolig@list
|
\let\verbatim@nolig@list\sphinx@literal@nolig@list
|
||||||
\fi
|
\fi
|
||||||
@ -1754,6 +1818,7 @@
|
|||||||
\def\sphinx@do@noligs #1{\catcode`#1\active\begingroup\lccode`\~`#1\relax
|
\def\sphinx@do@noligs #1{\catcode`#1\active\begingroup\lccode`\~`#1\relax
|
||||||
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
|
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
|
||||||
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
|
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
|
||||||
|
\let\sphinxafterbreakofinlineliteral\empty
|
||||||
|
|
||||||
% Some custom font markup commands.
|
% Some custom font markup commands.
|
||||||
\protected\def\sphinxstrong#1{\textbf{#1}}
|
\protected\def\sphinxstrong#1{\textbf{#1}}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
\ProcessLocalKeyvalOptions* % ignore class options
|
\ProcessLocalKeyvalOptions* % ignore class options
|
||||||
|
|
||||||
\ifspx@cyropt@Xtwo
|
\ifspx@cyropt@Xtwo
|
||||||
% original code by tex.sx user egreg:
|
% original code by tex.sx user egreg (updated 2019/10/28):
|
||||||
% https://tex.stackexchange.com/a/460325/
|
% https://tex.stackexchange.com/a/460325/
|
||||||
% 159 Cyrillic glyphs as available in X2 TeX 8bit font encoding
|
% 159 Cyrillic glyphs as available in X2 TeX 8bit font encoding
|
||||||
% This assumes inputenc loaded with utf8 option, or LaTeX release
|
% This assumes inputenc loaded with utf8 option, or LaTeX release
|
||||||
@ -27,7 +27,9 @@
|
|||||||
{Ӎ}{ӎ}{Ӕ}{ӕ}{Ә}{ә}{Ӡ}{ӡ}{Ө}{ө}\do
|
{Ӎ}{ӎ}{Ӕ}{ӕ}{Ә}{ә}{Ӡ}{ӡ}{Ө}{ө}\do
|
||||||
{%
|
{%
|
||||||
\begingroup\def\IeC{\protect\DeclareTextSymbolDefault}%
|
\begingroup\def\IeC{\protect\DeclareTextSymbolDefault}%
|
||||||
\protected@edef\@temp{\endgroup\next{X2}}\@temp
|
\protected@edef\@temp{\endgroup
|
||||||
|
\@ifl@t@r{\fmtversion}{2019/10/01}{\csname u8:\next\endcsname}{\next}}%
|
||||||
|
\@temp{X2}%
|
||||||
}%
|
}%
|
||||||
\else
|
\else
|
||||||
\ifspx@cyropt@TtwoA
|
\ifspx@cyropt@TtwoA
|
||||||
|
@ -187,9 +187,8 @@ class sphinx_domains:
|
|||||||
def __enter__(self) -> None:
|
def __enter__(self) -> None:
|
||||||
self.enable()
|
self.enable()
|
||||||
|
|
||||||
def __exit__(self, exc_type: "Type[Exception]", exc_value: Exception, traceback: Any) -> bool: # type: ignore # NOQA
|
def __exit__(self, exc_type: "Type[Exception]", exc_value: Exception, traceback: Any) -> None: # NOQA
|
||||||
self.disable()
|
self.disable()
|
||||||
return False
|
|
||||||
|
|
||||||
def enable(self) -> None:
|
def enable(self) -> None:
|
||||||
self.directive_func = directives.directive
|
self.directive_func = directives.directive
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Generator, Union
|
from typing import Generator, Union
|
||||||
@ -16,6 +17,7 @@ from urllib.parse import urlsplit
|
|||||||
import pkg_resources
|
import pkg_resources
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
import sphinx
|
||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -105,14 +107,28 @@ def _get_tls_cacert(url: str, config: Config) -> Union[str, bool]:
|
|||||||
return certs.get(hostname, True)
|
return certs.get(hostname, True)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_user_agent(config: Config) -> str:
|
||||||
|
if config.user_agent:
|
||||||
|
return config.user_agent
|
||||||
|
else:
|
||||||
|
return ' '.join([
|
||||||
|
'Sphinx/%s' % sphinx.__version__,
|
||||||
|
'requests/%s' % requests.__version__,
|
||||||
|
'python/%s' % '.'.join(map(str, sys.version_info[:3])),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def get(url: str, **kwargs) -> requests.Response:
|
def get(url: str, **kwargs) -> requests.Response:
|
||||||
"""Sends a GET request like requests.get().
|
"""Sends a GET request like requests.get().
|
||||||
|
|
||||||
This sets up User-Agent header and TLS verification automatically."""
|
This sets up User-Agent header and TLS verification automatically."""
|
||||||
kwargs.setdefault('headers', dict(useragent_header))
|
headers = kwargs.setdefault('headers', {})
|
||||||
config = kwargs.pop('config', None)
|
config = kwargs.pop('config', None)
|
||||||
if config:
|
if config:
|
||||||
kwargs.setdefault('verify', _get_tls_cacert(url, config))
|
kwargs.setdefault('verify', _get_tls_cacert(url, config))
|
||||||
|
headers.setdefault('User-Agent', _get_user_agent(config))
|
||||||
|
else:
|
||||||
|
headers.setdefault('User-Agent', useragent_header[0][1])
|
||||||
|
|
||||||
with ignore_insecure_warning(**kwargs):
|
with ignore_insecure_warning(**kwargs):
|
||||||
return requests.get(url, **kwargs)
|
return requests.get(url, **kwargs)
|
||||||
@ -122,10 +138,13 @@ def head(url: str, **kwargs) -> requests.Response:
|
|||||||
"""Sends a HEAD request like requests.head().
|
"""Sends a HEAD request like requests.head().
|
||||||
|
|
||||||
This sets up User-Agent header and TLS verification automatically."""
|
This sets up User-Agent header and TLS verification automatically."""
|
||||||
kwargs.setdefault('headers', dict(useragent_header))
|
headers = kwargs.setdefault('headers', {})
|
||||||
config = kwargs.pop('config', None)
|
config = kwargs.pop('config', None)
|
||||||
if config:
|
if config:
|
||||||
kwargs.setdefault('verify', _get_tls_cacert(url, config))
|
kwargs.setdefault('verify', _get_tls_cacert(url, config))
|
||||||
|
headers.setdefault('User-Agent', _get_user_agent(config))
|
||||||
|
else:
|
||||||
|
headers.setdefault('User-Agent', useragent_header[0][1])
|
||||||
|
|
||||||
with ignore_insecure_warning(**kwargs):
|
with ignore_insecure_warning(**kwargs):
|
||||||
return requests.get(url, **kwargs)
|
return requests.get(url, **kwargs)
|
||||||
|
@ -63,14 +63,14 @@ class SphinxRenderer(FileRenderer):
|
|||||||
|
|
||||||
|
|
||||||
class LaTeXRenderer(SphinxRenderer):
|
class LaTeXRenderer(SphinxRenderer):
|
||||||
def __init__(self, template_path: str = None) -> None:
|
def __init__(self, template_path: str = None, latex_engine: str = None) -> None:
|
||||||
if template_path is None:
|
if template_path is None:
|
||||||
template_path = os.path.join(package_dir, 'templates', 'latex')
|
template_path = os.path.join(package_dir, 'templates', 'latex')
|
||||||
super().__init__(template_path)
|
super().__init__(template_path)
|
||||||
|
|
||||||
# use texescape as escape filter
|
# use texescape as escape filter
|
||||||
self.env.filters['e'] = texescape.escape
|
self.env.filters['e'] = texescape.get_escape_func(latex_engine)
|
||||||
self.env.filters['escape'] = texescape.escape
|
self.env.filters['escape'] = texescape.get_escape_func(latex_engine)
|
||||||
self.env.filters['eabbr'] = texescape.escape_abbr
|
self.env.filters['eabbr'] = texescape.escape_abbr
|
||||||
|
|
||||||
# use JSP/eRuby like tagging instead because curly bracket; the default
|
# use JSP/eRuby like tagging instead because curly bracket; the default
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Dict
|
from typing import Callable, Dict
|
||||||
|
|
||||||
tex_replacements = [
|
tex_replacements = [
|
||||||
# map TeX special chars
|
# map TeX special chars
|
||||||
@ -20,15 +20,38 @@ tex_replacements = [
|
|||||||
('_', r'\_'),
|
('_', r'\_'),
|
||||||
('{', r'\{'),
|
('{', r'\{'),
|
||||||
('}', r'\}'),
|
('}', r'\}'),
|
||||||
('[', r'{[}'),
|
|
||||||
(']', r'{]}'),
|
|
||||||
('`', r'{}`'),
|
|
||||||
('\\', r'\textbackslash{}'),
|
('\\', r'\textbackslash{}'),
|
||||||
('~', r'\textasciitilde{}'),
|
('~', r'\textasciitilde{}'),
|
||||||
|
('^', r'\textasciicircum{}'),
|
||||||
|
# map chars to avoid mis-interpretation in LaTeX
|
||||||
|
('[', r'{[}'),
|
||||||
|
(']', r'{]}'),
|
||||||
|
# map chars to avoid TeX ligatures
|
||||||
|
# 1. ' - and , not here for some legacy reason
|
||||||
|
# 2. no effect with lualatex (done otherwise: #5790)
|
||||||
|
('`', r'{}`'),
|
||||||
('<', r'\textless{}'),
|
('<', r'\textless{}'),
|
||||||
('>', r'\textgreater{}'),
|
('>', r'\textgreater{}'),
|
||||||
('^', r'\textasciicircum{}'),
|
# map char for some unknown reason. TODO: remove this?
|
||||||
|
('|', r'\textbar{}'),
|
||||||
# map special Unicode characters to TeX commands
|
# map special Unicode characters to TeX commands
|
||||||
|
('✓', r'\(\checkmark\)'),
|
||||||
|
('✔', r'\(\pmb{\checkmark}\)'),
|
||||||
|
# used to separate -- in options
|
||||||
|
('', r'{}'),
|
||||||
|
# map some special Unicode characters to similar ASCII ones
|
||||||
|
# (even for Unicode LaTeX as may not be supported by OpenType font)
|
||||||
|
('⎽', r'\_'),
|
||||||
|
('ℯ', r'e'),
|
||||||
|
('ⅈ', r'i'),
|
||||||
|
# Greek alphabet not escaped: pdflatex handles it via textalpha and inputenc
|
||||||
|
# OHM SIGN U+2126 is handled by LaTeX textcomp package
|
||||||
|
]
|
||||||
|
|
||||||
|
# A map Unicode characters to LaTeX representation
|
||||||
|
# (for LaTeX engines which don't support unicode)
|
||||||
|
unicode_tex_replacements = [
|
||||||
|
# map some more common Unicode characters to TeX commands
|
||||||
('¶', r'\P{}'),
|
('¶', r'\P{}'),
|
||||||
('§', r'\S{}'),
|
('§', r'\S{}'),
|
||||||
('€', r'\texteuro{}'),
|
('€', r'\texteuro{}'),
|
||||||
@ -36,16 +59,8 @@ tex_replacements = [
|
|||||||
('±', r'\(\pm\)'),
|
('±', r'\(\pm\)'),
|
||||||
('→', r'\(\rightarrow\)'),
|
('→', r'\(\rightarrow\)'),
|
||||||
('‣', r'\(\rightarrow\)'),
|
('‣', r'\(\rightarrow\)'),
|
||||||
('✓', r'\(\checkmark\)'),
|
|
||||||
('✔', r'\(\pmb{\checkmark}\)'),
|
|
||||||
# used to separate -- in options
|
|
||||||
('', r'{}'),
|
|
||||||
# map some special Unicode characters to similar ASCII ones
|
|
||||||
('⎽', r'\_'),
|
|
||||||
('–', r'\textendash{}'),
|
('–', r'\textendash{}'),
|
||||||
('|', r'\textbar{}'),
|
# superscript
|
||||||
('ℯ', r'e'),
|
|
||||||
('ⅈ', r'i'),
|
|
||||||
('⁰', r'\(\sp{\text{0}}\)'),
|
('⁰', r'\(\sp{\text{0}}\)'),
|
||||||
('¹', r'\(\sp{\text{1}}\)'),
|
('¹', r'\(\sp{\text{1}}\)'),
|
||||||
('²', r'\(\sp{\text{2}}\)'),
|
('²', r'\(\sp{\text{2}}\)'),
|
||||||
@ -56,6 +71,7 @@ tex_replacements = [
|
|||||||
('⁷', r'\(\sp{\text{7}}\)'),
|
('⁷', r'\(\sp{\text{7}}\)'),
|
||||||
('⁸', r'\(\sp{\text{8}}\)'),
|
('⁸', r'\(\sp{\text{8}}\)'),
|
||||||
('⁹', r'\(\sp{\text{9}}\)'),
|
('⁹', r'\(\sp{\text{9}}\)'),
|
||||||
|
# subscript
|
||||||
('₀', r'\(\sb{\text{0}}\)'),
|
('₀', r'\(\sb{\text{0}}\)'),
|
||||||
('₁', r'\(\sb{\text{1}}\)'),
|
('₁', r'\(\sb{\text{1}}\)'),
|
||||||
('₂', r'\(\sb{\text{2}}\)'),
|
('₂', r'\(\sb{\text{2}}\)'),
|
||||||
@ -66,13 +82,21 @@ tex_replacements = [
|
|||||||
('₇', r'\(\sb{\text{7}}\)'),
|
('₇', r'\(\sb{\text{7}}\)'),
|
||||||
('₈', r'\(\sb{\text{8}}\)'),
|
('₈', r'\(\sb{\text{8}}\)'),
|
||||||
('₉', r'\(\sb{\text{9}}\)'),
|
('₉', r'\(\sb{\text{9}}\)'),
|
||||||
# Greek alphabet not escaped: pdflatex handles it via textalpha and inputenc
|
|
||||||
# OHM SIGN U+2126 is handled by LaTeX textcomp package
|
|
||||||
]
|
]
|
||||||
|
|
||||||
tex_escape_map = {} # type: Dict[int, str]
|
tex_escape_map = {} # type: Dict[int, str]
|
||||||
|
tex_escape_map_without_unicode = {} # type: Dict[int, str]
|
||||||
tex_replace_map = {}
|
tex_replace_map = {}
|
||||||
tex_hl_escape_map_new = {}
|
tex_hl_escape_map_new = {} # type: Dict[int, str]
|
||||||
|
tex_hl_escape_map_new_without_unicode = {} # type: Dict[int, str]
|
||||||
|
|
||||||
|
|
||||||
|
def get_escape_func(latex_engine: str) -> Callable[[str], str]:
|
||||||
|
"""Get escape() function for given latex_engine."""
|
||||||
|
if latex_engine in ('lualatex', 'xelatex'):
|
||||||
|
return escape_for_unicode_latex_engine
|
||||||
|
else:
|
||||||
|
return escape
|
||||||
|
|
||||||
|
|
||||||
def escape(s: str) -> str:
|
def escape(s: str) -> str:
|
||||||
@ -80,6 +104,29 @@ def escape(s: str) -> str:
|
|||||||
return s.translate(tex_escape_map)
|
return s.translate(tex_escape_map)
|
||||||
|
|
||||||
|
|
||||||
|
def escape_for_unicode_latex_engine(s: str) -> str:
|
||||||
|
"""Escape text for unicode supporting LaTeX engine."""
|
||||||
|
return s.translate(tex_escape_map_without_unicode)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hlescape_func(latex_engine: str) -> Callable[[str], str]:
|
||||||
|
"""Get hlescape() function for given latex_engine."""
|
||||||
|
if latex_engine in ('lualatex', 'xelatex'):
|
||||||
|
return hlescape_for_unicode_latex_engine
|
||||||
|
else:
|
||||||
|
return hlescape
|
||||||
|
|
||||||
|
|
||||||
|
def hlescape(s: str) -> str:
|
||||||
|
"""Escape text for LaTeX highlighter."""
|
||||||
|
return s.translate(tex_hl_escape_map_new)
|
||||||
|
|
||||||
|
|
||||||
|
def hlescape_for_unicode_latex_engine(s: str) -> str:
|
||||||
|
"""Escape text for unicode supporting LaTeX engine."""
|
||||||
|
return s.translate(tex_hl_escape_map_new_without_unicode)
|
||||||
|
|
||||||
|
|
||||||
def escape_abbr(text: str) -> str:
|
def escape_abbr(text: str) -> str:
|
||||||
"""Adjust spacing after abbreviations. Works with @ letter or other."""
|
"""Adjust spacing after abbreviations. Works with @ letter or other."""
|
||||||
return re.sub(r'\.(?=\s|$)', r'.\@{}', text)
|
return re.sub(r'\.(?=\s|$)', r'.\@{}', text)
|
||||||
@ -87,6 +134,11 @@ def escape_abbr(text: str) -> str:
|
|||||||
|
|
||||||
def init() -> None:
|
def init() -> None:
|
||||||
for a, b in tex_replacements:
|
for a, b in tex_replacements:
|
||||||
|
tex_escape_map[ord(a)] = b
|
||||||
|
tex_escape_map_without_unicode[ord(a)] = b
|
||||||
|
tex_replace_map[ord(a)] = '_'
|
||||||
|
|
||||||
|
for a, b in unicode_tex_replacements:
|
||||||
tex_escape_map[ord(a)] = b
|
tex_escape_map[ord(a)] = b
|
||||||
tex_replace_map[ord(a)] = '_'
|
tex_replace_map[ord(a)] = '_'
|
||||||
|
|
||||||
@ -94,3 +146,7 @@ def init() -> None:
|
|||||||
if a in '[]{}\\':
|
if a in '[]{}\\':
|
||||||
continue
|
continue
|
||||||
tex_hl_escape_map_new[ord(a)] = b
|
tex_hl_escape_map_new[ord(a)] = b
|
||||||
|
tex_hl_escape_map_new_without_unicode[ord(a)] = b
|
||||||
|
|
||||||
|
for a, b in unicode_tex_replacements:
|
||||||
|
tex_hl_escape_map_new[ord(a)] = b
|
||||||
|
@ -32,7 +32,7 @@ from sphinx.util import split_into, logging
|
|||||||
from sphinx.util.docutils import SphinxTranslator
|
from sphinx.util.docutils import SphinxTranslator
|
||||||
from sphinx.util.nodes import clean_astext, get_prev_node
|
from sphinx.util.nodes import clean_astext, get_prev_node
|
||||||
from sphinx.util.template import LaTeXRenderer
|
from sphinx.util.template import LaTeXRenderer
|
||||||
from sphinx.util.texescape import tex_escape_map, tex_replace_map
|
from sphinx.util.texescape import get_escape_func, tex_replace_map
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from docutils.utils.roman import toRoman
|
from docutils.utils.roman import toRoman
|
||||||
@ -155,6 +155,7 @@ DEFAULT_SETTINGS = {
|
|||||||
'% Set up styles of URL: it should be placed after hyperref.\n'
|
'% Set up styles of URL: it should be placed after hyperref.\n'
|
||||||
'\\urlstyle{same}'),
|
'\\urlstyle{same}'),
|
||||||
'contentsname': '',
|
'contentsname': '',
|
||||||
|
'extrapackages': '',
|
||||||
'preamble': '',
|
'preamble': '',
|
||||||
'title': '',
|
'title': '',
|
||||||
'release': '',
|
'release': '',
|
||||||
@ -499,6 +500,9 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.compact_list = 0
|
self.compact_list = 0
|
||||||
self.first_param = 0
|
self.first_param = 0
|
||||||
|
|
||||||
|
# escape helper
|
||||||
|
self.escape = get_escape_func(self.config.latex_engine)
|
||||||
|
|
||||||
# sort out some elements
|
# sort out some elements
|
||||||
self.elements = self.builder.context.copy()
|
self.elements = self.builder.context.copy()
|
||||||
|
|
||||||
@ -649,7 +653,8 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.elements['classoptions'] += ',' + \
|
self.elements['classoptions'] += ',' + \
|
||||||
self.elements['extraclassoptions']
|
self.elements['extraclassoptions']
|
||||||
|
|
||||||
self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style)
|
self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style,
|
||||||
|
latex_engine=self.config.latex_engine)
|
||||||
self.context = [] # type: List[Any]
|
self.context = [] # type: List[Any]
|
||||||
self.descstack = [] # type: List[str]
|
self.descstack = [] # type: List[str]
|
||||||
self.table = None # type: Table
|
self.table = None # type: Table
|
||||||
@ -757,8 +762,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
for i, (letter, entries) in enumerate(content):
|
for i, (letter, entries) in enumerate(content):
|
||||||
if i > 0:
|
if i > 0:
|
||||||
ret.append('\\indexspace\n')
|
ret.append('\\indexspace\n')
|
||||||
ret.append('\\bigletter{%s}\n' %
|
ret.append('\\bigletter{%s}\n' % self.escape(letter))
|
||||||
str(letter).translate(tex_escape_map))
|
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
if not entry[3]:
|
if not entry[3]:
|
||||||
continue
|
continue
|
||||||
@ -793,13 +797,14 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
|
|
||||||
def render(self, template_name, variables):
|
def render(self, template_name, variables):
|
||||||
# type: (str, Dict) -> str
|
# type: (str, Dict) -> str
|
||||||
|
renderer = LaTeXRenderer(latex_engine=self.config.latex_engine)
|
||||||
for template_dir in self.builder.config.templates_path:
|
for template_dir in self.builder.config.templates_path:
|
||||||
template = path.join(self.builder.confdir, template_dir,
|
template = path.join(self.builder.confdir, template_dir,
|
||||||
template_name)
|
template_name)
|
||||||
if path.exists(template):
|
if path.exists(template):
|
||||||
return LaTeXRenderer().render(template, variables)
|
return renderer.render(template, variables)
|
||||||
|
|
||||||
return LaTeXRenderer().render(template_name, variables)
|
return renderer.render(template_name, variables)
|
||||||
|
|
||||||
def visit_document(self, node):
|
def visit_document(self, node):
|
||||||
# type: (nodes.Element) -> None
|
# type: (nodes.Element) -> None
|
||||||
@ -913,14 +918,13 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
if not self.elements['title']:
|
if not self.elements['title']:
|
||||||
# text needs to be escaped since it is inserted into
|
# text needs to be escaped since it is inserted into
|
||||||
# the output literally
|
# the output literally
|
||||||
self.elements['title'] = node.astext().translate(tex_escape_map)
|
self.elements['title'] = self.escape(node.astext())
|
||||||
self.this_is_the_title = 0
|
self.this_is_the_title = 0
|
||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
else:
|
else:
|
||||||
short = ''
|
short = ''
|
||||||
if node.traverse(nodes.image):
|
if node.traverse(nodes.image):
|
||||||
short = ('[%s]' %
|
short = ('[%s]' % self.escape(' '.join(clean_astext(node).split())))
|
||||||
' '.join(clean_astext(node).split()).translate(tex_escape_map))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short))
|
self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short))
|
||||||
@ -1954,8 +1958,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
else:
|
else:
|
||||||
id = node.get('refuri', '')[1:].replace('#', ':')
|
id = node.get('refuri', '')[1:].replace('#', ':')
|
||||||
|
|
||||||
title = node.get('title', '%s')
|
title = self.escape(node.get('title', '%s')).replace('\\%s', '%s')
|
||||||
title = str(title).translate(tex_escape_map).replace('\\%s', '%s')
|
|
||||||
if '\\{name\\}' in title or '\\{number\\}' in title:
|
if '\\{name\\}' in title or '\\{number\\}' in title:
|
||||||
# new style format (cf. "Fig.%{number}")
|
# new style format (cf. "Fig.%{number}")
|
||||||
title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}')
|
title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}')
|
||||||
@ -2403,7 +2406,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
|
|
||||||
def encode(self, text):
|
def encode(self, text):
|
||||||
# type: (str) -> str
|
# type: (str) -> str
|
||||||
text = str(text).translate(tex_escape_map)
|
text = self.escape(text)
|
||||||
if self.literal_whitespace:
|
if self.literal_whitespace:
|
||||||
# Insert a blank before the newline, to avoid
|
# Insert a blank before the newline, to avoid
|
||||||
# ! LaTeX Error: There's no line here to end.
|
# ! LaTeX Error: There's no line here to end.
|
||||||
@ -2614,33 +2617,31 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
ret = [] # type: List[str]
|
ret = [] # type: List[str]
|
||||||
figure = self.builder.config.numfig_format['figure'].split('%s', 1)
|
figure = self.builder.config.numfig_format['figure'].split('%s', 1)
|
||||||
if len(figure) == 1:
|
if len(figure) == 1:
|
||||||
ret.append('\\def\\fnum@figure{%s}\n' %
|
ret.append('\\def\\fnum@figure{%s}\n' % self.escape(figure[0]).strip())
|
||||||
str(figure[0]).strip().translate(tex_escape_map))
|
|
||||||
else:
|
else:
|
||||||
definition = escape_abbr(str(figure[0]).translate(tex_escape_map))
|
definition = escape_abbr(self.escape(figure[0]))
|
||||||
ret.append(self.babel_renewcommand('\\figurename', definition))
|
ret.append(self.babel_renewcommand('\\figurename', definition))
|
||||||
ret.append('\\makeatletter\n')
|
ret.append('\\makeatletter\n')
|
||||||
ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' %
|
ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' %
|
||||||
str(figure[1]).translate(tex_escape_map))
|
self.escape(figure[1]))
|
||||||
ret.append('\\makeatother\n')
|
ret.append('\\makeatother\n')
|
||||||
|
|
||||||
table = self.builder.config.numfig_format['table'].split('%s', 1)
|
table = self.builder.config.numfig_format['table'].split('%s', 1)
|
||||||
if len(table) == 1:
|
if len(table) == 1:
|
||||||
ret.append('\\def\\fnum@table{%s}\n' %
|
ret.append('\\def\\fnum@table{%s}\n' % self.escape(table[0]).strip())
|
||||||
str(table[0]).strip().translate(tex_escape_map))
|
|
||||||
else:
|
else:
|
||||||
definition = escape_abbr(str(table[0]).translate(tex_escape_map))
|
definition = escape_abbr(self.escape(table[0]))
|
||||||
ret.append(self.babel_renewcommand('\\tablename', definition))
|
ret.append(self.babel_renewcommand('\\tablename', definition))
|
||||||
ret.append('\\makeatletter\n')
|
ret.append('\\makeatletter\n')
|
||||||
ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' %
|
ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' %
|
||||||
str(table[1]).translate(tex_escape_map))
|
self.escape(table[1]))
|
||||||
ret.append('\\makeatother\n')
|
ret.append('\\makeatother\n')
|
||||||
|
|
||||||
codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1)
|
codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1)
|
||||||
if len(codeblock) == 1:
|
if len(codeblock) == 1:
|
||||||
pass # FIXME
|
pass # FIXME
|
||||||
else:
|
else:
|
||||||
definition = str(codeblock[0]).strip().translate(tex_escape_map)
|
definition = self.escape(codeblock[0]).strip()
|
||||||
ret.append(self.babel_renewcommand('\\literalblockname', definition))
|
ret.append(self.babel_renewcommand('\\literalblockname', definition))
|
||||||
if codeblock[1]:
|
if codeblock[1]:
|
||||||
pass # FIXME
|
pass # FIXME
|
||||||
|
0
tests/roots/test-latex-unicode/conf.py
Normal file
0
tests/roots/test-latex-unicode/conf.py
Normal file
7
tests/roots/test-latex-unicode/index.rst
Normal file
7
tests/roots/test-latex-unicode/index.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
test-latex-unicode
|
||||||
|
==================
|
||||||
|
|
||||||
|
* script small e: ℯ
|
||||||
|
* double struck italic small i: ⅈ
|
||||||
|
* superscript: ⁰, ¹
|
||||||
|
* subscript: ₀, ₁
|
@ -105,6 +105,8 @@ def test_add_is_parallel_allowed(app, status, warning):
|
|||||||
|
|
||||||
app.setup_extension('read_serial')
|
app.setup_extension('read_serial')
|
||||||
assert app.is_parallel_allowed('read') is False
|
assert app.is_parallel_allowed('read') is False
|
||||||
|
assert "the read_serial extension is not safe for parallel reading" in warning.getvalue()
|
||||||
|
warning.truncate(0) # reset warnings
|
||||||
assert app.is_parallel_allowed('write') is True
|
assert app.is_parallel_allowed('write') is True
|
||||||
assert warning.getvalue() == ''
|
assert warning.getvalue() == ''
|
||||||
app.extensions.pop('read_serial')
|
app.extensions.pop('read_serial')
|
||||||
|
@ -1437,3 +1437,35 @@ def test_index_on_title(app, status, warning):
|
|||||||
'\\label{\\detokenize{contents:test-for-index-in-top-level-title}}'
|
'\\label{\\detokenize{contents:test-for-index-in-top-level-title}}'
|
||||||
'\\index{index@\\spxentry{index}}\n'
|
'\\index{index@\\spxentry{index}}\n'
|
||||||
in result)
|
in result)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('latex', testroot='latex-unicode',
|
||||||
|
confoverrides={'latex_engine': 'pdflatex'})
|
||||||
|
def test_texescape_for_non_unicode_supported_engine(app, status, warning):
|
||||||
|
app.builder.build_all()
|
||||||
|
result = (app.outdir / 'python.tex').text()
|
||||||
|
print(result)
|
||||||
|
assert 'script small e: e' in result
|
||||||
|
assert 'double struck italic small i: i' in result
|
||||||
|
assert r'superscript: \(\sp{\text{0}}\), \(\sp{\text{1}}\)' in result
|
||||||
|
assert r'subscript: \(\sb{\text{0}}\), \(\sb{\text{1}}\)' in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('latex', testroot='latex-unicode',
|
||||||
|
confoverrides={'latex_engine': 'xelatex'})
|
||||||
|
def test_texescape_for_unicode_supported_engine(app, status, warning):
|
||||||
|
app.builder.build_all()
|
||||||
|
result = (app.outdir / 'python.tex').text()
|
||||||
|
print(result)
|
||||||
|
assert 'script small e: e' in result
|
||||||
|
assert 'double struck italic small i: i' in result
|
||||||
|
assert 'superscript: ⁰, ¹' in result
|
||||||
|
assert 'subscript: ₀, ₁' in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('latex', testroot='basic',
|
||||||
|
confoverrides={'latex_elements': {'extrapackages': r'\usepackage{foo}'}})
|
||||||
|
def test_latex_elements_extrapackages(app, status, warning):
|
||||||
|
app.builder.build_all()
|
||||||
|
result = (app.outdir / 'test.tex').text()
|
||||||
|
assert r'\usepackage{foo}' in result
|
||||||
|
@ -312,6 +312,24 @@ def test_inline(get_verifier, type, rst, html_expected, latex_expected):
|
|||||||
verifier(rst, html_expected, latex_expected)
|
verifier(rst, html_expected, latex_expected)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
|
||||||
|
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
|
||||||
|
(
|
||||||
|
# in verbatim code fragments
|
||||||
|
'verify',
|
||||||
|
'::\n\n @Γ\\∞${}',
|
||||||
|
None,
|
||||||
|
('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
|
||||||
|
'@Γ\\PYGZbs{}∞\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
|
||||||
|
'\\end{sphinxVerbatim}'),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
def test_inline_for_unicode_latex_engine(get_verifier, type, rst,
|
||||||
|
html_expected, latex_expected):
|
||||||
|
verifier = get_verifier(type)
|
||||||
|
verifier(rst, html_expected, latex_expected)
|
||||||
|
|
||||||
|
|
||||||
def test_samp_role(parse):
|
def test_samp_role(parse):
|
||||||
# no braces
|
# no braces
|
||||||
text = ':samp:`a{b}c`'
|
text = ':samp:`a{b}c`'
|
||||||
|
Loading…
Reference in New Issue
Block a user