Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Turner <9087854+aa-turner@users.noreply.github.com>2022-09-30 18:15:24 +0300
committerAdam Turner <9087854+aa-turner@users.noreply.github.com>2022-09-30 18:15:24 +0300
commit63dea6172aa3d5351a80085b9bebf02985c267e5 (patch)
tree658c42a4e346db41f464ebdd68990dc8d977fc97
parentbb62d2a2ee7d6d4b79b269c837ebf8d57aee0795 (diff)
parent29060711984f9ad6be210d9a237a5f5cd5bce5c9 (diff)
Merge branch '5.x'
# Conflicts: # sphinx/locale/__init__.py
-rw-r--r--.circleci/config.yml16
-rw-r--r--.github/workflows/latex.yml24
-rw-r--r--CHANGES10
-rw-r--r--doc/latex.rst2
-rw-r--r--doc/usage/configuration.rst5
-rw-r--r--doc/usage/restructuredtext/domains.rst46
-rw-r--r--pyproject.toml9
-rw-r--r--sphinx/builders/linkcheck.py8
-rw-r--r--sphinx/cmd/build.py4
-rw-r--r--sphinx/cmd/quickstart.py2
-rw-r--r--sphinx/config.py1
-rw-r--r--sphinx/deprecation.py8
-rw-r--r--sphinx/directives/__init__.py11
-rw-r--r--sphinx/domains/c.py2
-rw-r--r--sphinx/domains/cpp.py2
-rw-r--r--sphinx/domains/javascript.py4
-rw-r--r--sphinx/domains/python.py2
-rw-r--r--sphinx/domains/rst.py1
-rw-r--r--sphinx/domains/std.py8
-rw-r--r--sphinx/environment/collectors/toctree.py5
-rw-r--r--sphinx/ext/imgmath.py4
-rw-r--r--sphinx/locale/__init__.py39
-rw-r--r--sphinx/pycode/__init__.py14
-rw-r--r--sphinx/pycode/parser.py2
-rw-r--r--tests/roots/test-root/objects.txt13
-rw-r--r--tests/test_build_html.py9
-rw-r--r--tests/test_ext_math.py6
27 files changed, 184 insertions, 73 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 841260c69..000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-version: 2
-jobs:
- build:
- docker:
- - image: sphinxdoc/docker-ci
- environment:
- DO_EPUBCHECK: 1
- working_directory: /sphinx
- steps:
- - checkout
- - run: /python3.8/bin/pip install -U pip setuptools
- - run: /python3.8/bin/pip install -U .[test]
- - run: mkdir -p test-reports/pytest
- - run: make test PYTHON=/python3.8/bin/python TEST="--junitxml=test-reports/pytest/results.xml -vv"
- - store_test_results:
- path: test-reports
diff --git a/.github/workflows/latex.yml b/.github/workflows/latex.yml
new file mode 100644
index 000000000..12e39f54f
--- /dev/null
+++ b/.github/workflows/latex.yml
@@ -0,0 +1,24 @@
+name: CI (LaTeX)
+
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ runs-on: ubuntu-18.04
+ name: Python 3.8
+ container:
+ image: sphinxdoc/docker-ci
+ env:
+ DO_EPUBCHECK: 1
+ PATH: /python3.8/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ steps:
+ - uses: actions/checkout@v3
+ - name: Check Python version
+ run: python --version
+ - name: Install dependencies
+ run: pip install -U pip tox
+ - name: Run Tox
+ run: tox -e py -- -vv
diff --git a/CHANGES b/CHANGES
index 545bfb768..de0f7b11d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -38,12 +38,22 @@ Deprecated
Features added
--------------
+* #10840: One can cross-reference including an option value: ``:option:`--module=foobar```.
+ Patch by Martin Liska.
+
Bugs fixed
----------
Testing
--------
+Release 5.2.3 (released Sep 30, 2022)
+=====================================
+
+* #10878: Fix base64 image embedding in ``sphinx.ext.imgmath``
+* #10886: Add ``:nocontentsentry:`` flag and global domain table of contents
+ entry control option. Patch by Adam Turner
+
Release 5.2.2 (released Sep 27, 2022)
=====================================
diff --git a/doc/latex.rst b/doc/latex.rst
index 7fa90fe89..ae8c256cb 100644
--- a/doc/latex.rst
+++ b/doc/latex.rst
@@ -233,7 +233,7 @@ Keys that you may want to override include:
.. code-block:: python
latex_elements = {
- 'packages': r'\usepackage{isodate}'
+ 'extrapackages': r'\usepackage{isodate}'
}
The specified LaTeX packages will be loaded before
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 1071c9377..c18c2d317 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -678,6 +678,11 @@ General configuration
:term:`object` names (for object types where a "module" of some kind is
defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``.
+.. confval:: toc_object_entries
+
+ Create table of contents entries for domain objects (e.g. functions, classes,
+ attributes, etc.). Default is ``True``.
+
.. confval:: toc_object_entries_show_parents
A string that determines how domain objects (e.g. functions, classes,
diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst
index d5720877b..cd7091763 100644
--- a/doc/usage/restructuredtext/domains.rst
+++ b/doc/usage/restructuredtext/domains.rst
@@ -42,11 +42,15 @@ Basic Markup
Most domains provide a number of :dfn:`object description directives`, used to
describe specific objects provided by modules. Each directive requires one or
more signatures to provide basic information about what is being described, and
-the content should be the description. A domain will typically keep an
-internal index of all entities to aid cross-referencing. Typically it will
-also add entries in the shown general index.
+the content should be the description.
+
+A domain will typically keep an internal index of all entities to aid
+cross-referencing.
+Typically it will also add entries in the shown general index.
If you want to suppress the addition of an entry in the shown index, you can
give the directive option flag ``:noindexentry:``.
+If you want to exclude the object description from the table of contents, you
+can give the directive option flag ``:nocontentsentry:``.
If you want to typeset an object description, without even making it available
for cross-referencing, you can give the directive option flag ``:noindex:``
(which implies ``:noindexentry:``).
@@ -57,6 +61,10 @@ options.
The directive option ``noindexentry`` in the Python, C, C++, and Javascript
domains.
+.. versionadded:: 5.2.3
+ The directive option ``:nocontentsentry:`` in the Python, C, C++, Javascript,
+ and reStructuredText domains.
+
An example using a Python domain directive::
.. py:function:: spam(eggs)
@@ -851,15 +859,19 @@ Example::
This will be rendered as:
.. c:struct:: Data
+ :nocontentsentry:
:noindexentry:
.. c:union:: @data
+ :nocontentsentry:
:noindexentry:
.. c:var:: int a
+ :nocontentsentry:
:noindexentry:
.. c:var:: double b
+ :nocontentsentry:
:noindexentry:
Explicit ref: :c:var:`Data.@data.a`. Short-hand ref: :c:var:`Data.a`.
@@ -942,9 +954,11 @@ Inline Expressions and Types
will be rendered as follows:
.. c:var:: int a = 42
+ :nocontentsentry:
:noindexentry:
.. c:function:: int f(int i)
+ :nocontentsentry:
:noindexentry:
An expression: :c:expr:`a * f(a)` (or as text: :c:texpr:`a * f(a)`).
@@ -1155,23 +1169,27 @@ visibility statement (``public``, ``private`` or ``protected``).
The example are rendered as follows.
.. cpp:type:: std::vector<int> MyList
- :noindex:
+ :nocontentsentry:
+ :noindexentry:
A typedef-like declaration of a type.
.. cpp:type:: MyContainer::const_iterator
- :noindex:
+ :nocontentsentry:
+ :noindexentry:
Declaration of a type alias with unspecified type.
.. cpp:type:: MyType = std::unordered_map<int, std::string>
- :noindex:
+ :nocontentsentry:
+ :noindexentry:
Declaration of a type alias.
.. cpp:type:: template<typename T> \
MyContainer = std::vector<T>
- :noindex:
+ :nocontentsentry:
+ :noindexentry:
.. rst:directive:: .. cpp:enum:: unscoped enum declaration
.. cpp:enum-struct:: scoped enum declaration
@@ -1266,7 +1284,7 @@ Options
Some directives support options:
-- ``:noindexentry:``, see :ref:`basic-domain-markup`.
+- ``:noindexentry:`` and ``:nocontentsentry:``, see :ref:`basic-domain-markup`.
- ``:tparam-line-spec:``, for templated declarations.
If specified, each template parameter will be rendered on a separate line.
@@ -1298,15 +1316,19 @@ Example::
This will be rendered as:
.. cpp:class:: Data
+ :nocontentsentry:
:noindexentry:
.. cpp:union:: @data
+ :nocontentsentry:
:noindexentry:
.. cpp:var:: int a
+ :nocontentsentry:
:noindexentry:
.. cpp:var:: double b
+ :nocontentsentry:
:noindexentry:
Explicit ref: :cpp:var:`Data::@data::a`. Short-hand ref: :cpp:var:`Data::a`.
@@ -1413,11 +1435,13 @@ introduction` instead of a template parameter list::
They are rendered as follows.
.. cpp:function:: std::Iterator{It} void advance(It &it)
+ :nocontentsentry:
:noindexentry:
A function template with a template parameter constrained to be an Iterator.
.. cpp:class:: std::LessThanComparable{T} MySortedContainer
+ :nocontentsentry:
:noindexentry:
A class template with a template parameter constrained to be
@@ -1448,9 +1472,11 @@ Inline Expressions and Types
will be rendered as follows:
.. cpp:var:: int a = 42
+ :nocontentsentry:
:noindexentry:
.. cpp:function:: int f(int i)
+ :nocontentsentry:
:noindexentry:
An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
@@ -1764,6 +1790,10 @@ There is a set of directives allowing documenting command-line programs:
referenceable by :rst:role:`option` (in the example case, you'd use something
like ``:option:`dest_dir```, ``:option:`-m```, or ``:option:`--module```).
+ .. versionchanged:: 5.3
+
+ One can cross-reference including an option value: ``:option:`--module=foobar```.
+
Use :confval:`option_emphasise_placeholders` for parsing of
"variable part" of a literal text (similarly to the :rst:role:`samp` role).
diff --git a/pyproject.toml b/pyproject.toml
index dd11048a1..4a04ada4b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -183,18 +183,17 @@ strict_optional = false
[[tool.mypy.overrides]]
module = [
"sphinx.application",
- "sphinx.builders.*",
- "sphinx.cmd.*",
+ "sphinx.builders._epub_base",
+ "sphinx.builders.html",
+ "sphinx.builders.linkcheck",
+ "sphinx.cmd.quickstart",
"sphinx.config",
- "sphinx.deprecation",
"sphinx.domains.*",
"sphinx.environment.*",
"sphinx.events",
"sphinx.ext.*",
"sphinx.highlighting",
"sphinx.jinja2glue",
- "sphinx.locale",
- "sphinx.pycode.*",
"sphinx.registry",
"sphinx.roles",
"sphinx.search.*",
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 71e391378..a036aeefb 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -201,7 +201,7 @@ class HyperlinkAvailabilityChecker:
self.config = config
self.env = env
self.rate_limits: Dict[str, RateLimit] = {}
- self.rqueue: Queue = Queue()
+ self.rqueue: Queue[CheckResult] = Queue()
self.workers: List[Thread] = []
self.wqueue: PriorityQueue[CheckRequest] = PriorityQueue()
@@ -246,8 +246,8 @@ class HyperlinkAvailabilityChecker:
class HyperlinkAvailabilityCheckWorker(Thread):
"""A worker class for checking the availability of hyperlinks."""
- def __init__(self, env: BuildEnvironment, config: Config, rqueue: Queue,
- wqueue: Queue, rate_limits: Dict[str, RateLimit]) -> None:
+ def __init__(self, env: BuildEnvironment, config: Config, rqueue: 'Queue[CheckResult]',
+ wqueue: 'Queue[CheckRequest]', rate_limits: Dict[str, RateLimit]) -> None:
self.config = config
self.env = env
self.rate_limits = rate_limits
@@ -428,7 +428,7 @@ class HyperlinkAvailabilityCheckWorker(Thread):
uri, docname, lineno = hyperlink
except ValueError:
# old styled check_request (will be deprecated in Sphinx-5.0)
- next_check, uri, docname, lineno = check_request
+ next_check, uri, docname, lineno = check_request # type: ignore[misc]
if uri is None:
break
diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py
index 6cadefe3b..cab282bd1 100644
--- a/sphinx/cmd/build.py
+++ b/sphinx/cmd/build.py
@@ -9,7 +9,7 @@ import pdb
import sys
import traceback
from os import path
-from typing import IO, Any, List, Optional, TextIO
+from typing import Any, List, Optional, TextIO
from docutils.utils import SystemMessage
@@ -25,7 +25,7 @@ from sphinx.util.osutil import abspath, ensuredir
def handle_exception(
- app: Optional[Sphinx], args: Any, exception: BaseException, stderr: IO = sys.stderr
+ app: Optional[Sphinx], args: Any, exception: BaseException, stderr: TextIO = sys.stderr
) -> None:
if isinstance(exception, bdb.BdbQuit):
return
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index f9e3546bc..62c551c14 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -177,7 +177,7 @@ class QuickstartRenderer(SphinxRenderer):
else:
return False
- def render(self, template_name: str, context: Dict) -> str:
+ def render(self, template_name: str, context: Dict[str, Any]) -> str:
if self._has_custom_template(template_name):
custom_template = path.join(self.templatedir, path.basename(template_name))
return self.render_from_file(custom_template, context)
diff --git a/sphinx/config.py b/sphinx/config.py
index 45df6bb00..2906a3285 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -106,6 +106,7 @@ class Config:
'default_role': (None, 'env', [str]),
'add_function_parentheses': (True, 'env', []),
'add_module_names': (True, 'env', []),
+ 'toc_object_entries': (True, 'env', [bool]),
'toc_object_entries_show_parents': ('domain', 'env',
ENUM('domain', 'all', 'hide')),
'trim_footnote_reference_space': (False, 'env', []),
diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py
index b532b2b38..89fad5657 100644
--- a/sphinx/deprecation.py
+++ b/sphinx/deprecation.py
@@ -52,10 +52,10 @@ class _ModuleWrapper:
return self._objects[name]
-class DeprecatedDict(dict):
+class DeprecatedDict(Dict[str, Any]):
"""A deprecated dict which warns on each access."""
- def __init__(self, data: Dict, message: str, warning: Type[Warning]) -> None:
+ def __init__(self, data: Dict[str, Any], message: str, warning: Type[Warning]) -> None:
self.message = message
self.warning = warning
super().__init__(data)
@@ -68,7 +68,7 @@ class DeprecatedDict(dict):
warnings.warn(self.message, self.warning, stacklevel=2)
return super().setdefault(key, default)
- def __getitem__(self, key: str) -> None:
+ def __getitem__(self, key: str) -> Any:
warnings.warn(self.message, self.warning, stacklevel=2)
return super().__getitem__(key)
@@ -76,6 +76,6 @@ class DeprecatedDict(dict):
warnings.warn(self.message, self.warning, stacklevel=2)
return super().get(key, default)
- def update(self, other: Dict) -> None: # type: ignore
+ def update(self, other: Dict[str, Any]) -> None: # type: ignore
warnings.warn(self.message, self.warning, stacklevel=2)
super().update(other)
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index b6838a6fd..e59cb1295 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -51,6 +51,8 @@ class ObjectDescription(SphinxDirective, Generic[T]):
final_argument_whitespace = True
option_spec: OptionSpec = {
'noindex': directives.flag,
+ 'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
}
# types of doc fields that this directive handles, see sphinx.util.docfields
@@ -211,6 +213,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = noindex = ('noindex' in self.options)
node['noindexentry'] = ('noindexentry' in self.options)
+ node['nocontentsentry'] = ('nocontentsentry' in self.options)
if self.domain:
node['classes'].append(self.domain)
node['classes'].append(node['objtype'])
@@ -236,8 +239,12 @@ class ObjectDescription(SphinxDirective, Generic[T]):
finally:
# Private attributes for ToC generation. Will be modified or removed
# without notice.
- signode['_toc_parts'] = self._object_hierarchy_parts(signode)
- signode['_toc_name'] = self._toc_entry_name(signode)
+ if self.env.app.config.toc_object_entries:
+ signode['_toc_parts'] = self._object_hierarchy_parts(signode)
+ signode['_toc_name'] = self._toc_entry_name(signode)
+ else:
+ signode['_toc_parts'] = ()
+ signode['_toc_name'] = ''
if name not in self.names:
self.names.append(name)
if not noindex:
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 14c52e050..3aa951f1c 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -3142,8 +3142,8 @@ class CObject(ObjectDescription[ASTDeclaration]):
"""
option_spec: OptionSpec = {
- 'noindex': directives.flag,
'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
}
def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index b448449b7..b509b3489 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -7186,8 +7186,8 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
]
option_spec: OptionSpec = {
- 'noindex': directives.flag,
'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
'tparam-line-spec': directives.flag,
}
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index b78dfd30e..391cebf33 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -40,6 +40,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
option_spec: OptionSpec = {
'noindex': directives.flag,
'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
}
def get_display_prefix(self) -> List[Node]:
@@ -284,7 +285,8 @@ class JSModule(SphinxDirective):
optional_arguments = 0
final_argument_whitespace = False
option_spec: OptionSpec = {
- 'noindex': directives.flag
+ 'noindex': directives.flag,
+ 'nocontentsentry': directives.flag,
}
def run(self) -> List[Node]:
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index fe2d52eb1..72a15433d 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -412,6 +412,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
option_spec: OptionSpec = {
'noindex': directives.flag,
'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
'module': directives.unchanged,
'canonical': directives.unchanged,
'annotation': directives.unchanged,
@@ -978,6 +979,7 @@ class PyModule(SphinxDirective):
'platform': lambda x: x,
'synopsis': lambda x: x,
'noindex': directives.flag,
+ 'nocontentsentry': directives.flag,
'deprecated': directives.flag,
}
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index fc7f2e551..8f49fcaa0 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -31,6 +31,7 @@ class ReSTMarkup(ObjectDescription[str]):
option_spec: OptionSpec = {
'noindex': directives.flag,
'noindexentry': directives.flag,
+ 'nocontentsentry': directives.flag,
}
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index aa6045f66..f6d1c4dc4 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -780,7 +780,9 @@ class StandardDomain(Domain):
self.labels[name] = docname, labelid, sectname
def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
- self.progoptions[program, name] = (docname, labelid)
+ # prefer first command option entry
+ if (program, name) not in self.progoptions:
+ self.progoptions[program, name] = (docname, labelid)
def build_reference_node(self, fromdocname: str, builder: "Builder", docname: str,
labelid: str, sectname: str, rolename: str, **options: Any
@@ -941,6 +943,10 @@ class StandardDomain(Domain):
progname = node.get('std:program')
target = target.strip()
docname, labelid = self.progoptions.get((progname, target), ('', ''))
+ # for :option:`-foo=bar` search for -foo option directive
+ if not docname and '=' in target:
+ target2 = target[:target.find('=')]
+ docname, labelid = self.progoptions.get((progname, target2), ('', ''))
if not docname:
commands = []
while ws_re.search(target):
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index 628f4a444..74af40928 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -112,9 +112,12 @@ class TocTreeCollector(EnvironmentCollector):
# Skip if no name set
if not sig_node.get('_toc_name', ''):
continue
+ # Skip if explicitly disabled
+ if sig_node.parent.get('nocontentsentry'):
+ continue
# Skip entries with no ID (e.g. with :noindex: set)
ids = sig_node['ids']
- if not ids or sig_node.parent.get('noindexentry'):
+ if not ids:
continue
anchorname = _make_anchor_name(ids, numentries)
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index 5bfca8d4d..ddef58187 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -308,7 +308,7 @@ def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
raise nodes.SkipNode from exc
if self.builder.config.imgmath_embed:
image_format = self.builder.config.imgmath_image_format.lower()
- img_src = render_maths_to_base64(image_format, outfn)
+ img_src = render_maths_to_base64(image_format, imgpath)
else:
# Move generated image on tempdir to build dir
if imgpath is not None:
@@ -350,7 +350,7 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
self.body.append('</span>')
if self.builder.config.imgmath_embed:
image_format = self.builder.config.imgmath_image_format.lower()
- img_src = render_maths_to_base64(image_format, outfn)
+ img_src = render_maths_to_base64(image_format, imgpath)
else:
# Move generated image on tempdir to build dir
if imgpath is not None:
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 7942079c8..61e3ae812 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -1,9 +1,8 @@
"""Locale utilities."""
-import gettext
import locale
-from collections import defaultdict
-from gettext import NullTranslations
+from gettext import NullTranslations, translation
+from os import path
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
@@ -17,7 +16,7 @@ class _TranslationProxy:
"""
__slots__ = ('_func', '_args')
- def __new__(cls, func: Callable, *args: str) -> "_TranslationProxy":
+ def __new__(cls, func: Callable[..., str], *args: str) -> '_TranslationProxy':
if not args:
# not called with "function" and "arguments", but a plain string
return str(func) # type: ignore[return-value]
@@ -26,7 +25,7 @@ class _TranslationProxy:
def __getnewargs__(self) -> Tuple[str]:
return (self._func,) + self._args # type: ignore
- def __init__(self, func: Callable, *args: str) -> None:
+ def __init__(self, func: Callable[..., str], *args: str) -> None:
self._func = func
self._args = args
@@ -39,13 +38,13 @@ class _TranslationProxy:
def __getattr__(self, name: str) -> Any:
return getattr(self.__str__(), name)
- def __getstate__(self) -> Tuple[Callable, Tuple[str, ...]]:
+ def __getstate__(self) -> Tuple[Callable[..., str], Tuple[str, ...]]:
return self._func, self._args
- def __setstate__(self, tup: Tuple[Callable, Tuple[str]]) -> None:
+ def __setstate__(self, tup: Tuple[Callable[..., str], Tuple[str]]) -> None:
self._func, self._args = tup
- def __copy__(self) -> "_TranslationProxy":
+ def __copy__(self) -> '_TranslationProxy':
return _TranslationProxy(self._func, *self._args)
def __repr__(self) -> str:
@@ -91,11 +90,15 @@ class _TranslationProxy:
return self.__str__()[index]
-translators: Dict[Tuple[str, str], NullTranslations] = defaultdict(NullTranslations)
+translators: Dict[Tuple[str, str], NullTranslations] = {}
-def init(locale_dirs: List[Optional[str]], language: Optional[str],
- catalog: str = 'sphinx', namespace: str = 'general') -> Tuple[NullTranslations, bool]:
+def init(
+ locale_dirs: List[Optional[str]],
+ language: Optional[str],
+ catalog: str = 'sphinx',
+ namespace: str = 'general',
+) -> Tuple[NullTranslations, bool]:
"""Look for message catalogs in `locale_dirs` and *ensure* that there is at
least a NullTranslations catalog set in `translators`. If called multiple
times or if several ``.mo`` files are found, their contents are merged
@@ -120,7 +123,7 @@ def init(locale_dirs: List[Optional[str]], language: Optional[str],
# loading
for dir_ in locale_dirs:
try:
- trans = gettext.translation(catalog, localedir=dir_, languages=languages)
+ trans = translation(catalog, localedir=dir_, languages=languages)
if translator is None:
translator = trans
else:
@@ -148,7 +151,7 @@ def setlocale(category: int, value: Union[str, Iterable[str], None] = None) -> N
* https://bugs.python.org/issue18378#msg215215
.. note:: Only for internal use. Please don't call this method from extensions.
- This will be removed in future.
+ This will be removed in Sphinx 6.0.
"""
try:
locale.setlocale(category, value)
@@ -156,7 +159,13 @@ def setlocale(category: int, value: Union[str, Iterable[str], None] = None) -> N
pass
-def init_console(locale_dir: str, catalog: str) -> Tuple[NullTranslations, bool]:
+_LOCALE_DIR = path.abspath(path.dirname(__file__))
+
+
+def init_console(
+ locale_dir: str = _LOCALE_DIR,
+ catalog: str = 'sphinx',
+) -> Tuple[NullTranslations, bool]:
"""Initialize locale for console.
.. versionadded:: 1.8
@@ -172,7 +181,7 @@ def init_console(locale_dir: str, catalog: str) -> Tuple[NullTranslations, bool]
def get_translator(catalog: str = 'sphinx', namespace: str = 'general') -> NullTranslations:
- return translators[(namespace, catalog)]
+ return translators.get((namespace, catalog), NullTranslations())
def is_translator_registered(catalog: str = 'sphinx', namespace: str = 'general') -> bool:
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index ac3f7673c..67b17d21b 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -5,9 +5,8 @@ import tokenize
from collections import OrderedDict
from importlib import import_module
from inspect import Signature
-from io import StringIO
from os import path
-from typing import IO, Any, Dict, List, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
from zipfile import ZipFile
from sphinx.errors import PycodeError
@@ -76,7 +75,7 @@ class ModuleAnalyzer:
@classmethod
def for_string(cls, string: str, modname: str, srcname: str = '<string>'
) -> "ModuleAnalyzer":
- return cls(StringIO(string), modname, srcname)
+ return cls(string, modname, srcname)
@classmethod
def for_file(cls, filename: str, modname: str) -> "ModuleAnalyzer":
@@ -84,8 +83,9 @@ class ModuleAnalyzer:
return cls.cache['file', filename]
try:
with tokenize.open(filename) as f:
- obj = cls(f, modname, filename)
- cls.cache['file', filename] = obj
+ string = f.read()
+ obj = cls(string, modname, filename)
+ cls.cache['file', filename] = obj
except Exception as err:
if '.egg' + path.sep in filename:
obj = cls.cache['file', filename] = cls.for_egg(filename, modname)
@@ -124,12 +124,12 @@ class ModuleAnalyzer:
cls.cache['module', modname] = obj
return obj
- def __init__(self, source: IO, modname: str, srcname: str) -> None:
+ def __init__(self, source: str, modname: str, srcname: str) -> None:
self.modname = modname # name of the module
self.srcname = srcname # name of the source file
# cache the source code as well
- self.code = source.read()
+ self.code = source
self._analyzed = False
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index 133748cb8..e537a7726 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -463,7 +463,7 @@ class DefinitionFinder(TokenProcessor):
super().__init__(lines)
self.decorator: Optional[Token] = None
self.context: List[str] = []
- self.indents: List = []
+ self.indents: List[Tuple[str, Optional[str], Optional[int]]] = []
self.definitions: Dict[str, Tuple[str, int, int]] = {}
def add_definition(self, name: str, entry: Tuple[str, int, int]) -> None:
diff --git a/tests/roots/test-root/objects.txt b/tests/roots/test-root/objects.txt
index a4a5c667c..fa9e475e5 100644
--- a/tests/roots/test-root/objects.txt
+++ b/tests/roots/test-root/objects.txt
@@ -204,6 +204,19 @@ Link to :option:`hg commit` and :option:`git commit -p`.
Foo bar.
+Test repeated option directive.
+
+.. option:: -mapi
+
+ My API.
+
+.. option:: -mapi=secret
+
+ My secret API.
+
+Reference the first option :option:`-mapi=secret`.
+
+
User markup
===========
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 6b18bac1c..9a31a4b8a 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -1709,6 +1709,15 @@ def test_option_emphasise_placeholders_default(app, status, warning):
'<a class="headerlink" href="#cmdoption-perl-plugin.option" title="Permalink to this definition">ΒΆ</a></dt>') in content
+@pytest.mark.sphinx('html', testroot='root')
+def test_option_reference_with_value(app, status, warning):
+ app.build()
+ content = (app.outdir / 'objects.html').read_text()
+ assert ('<span class="pre">-mapi</span></span><span class="sig-prename descclassname">'
+ '</span><a class="headerlink" href="#cmdoption-git-commit-mapi"') in content
+ assert 'first option <a class="reference internal" href="#cmdoption-git-commit-mapi">' in content
+
+
@pytest.mark.sphinx('html', testroot='theming')
def test_theme_options(app, status, warning):
app.build()
diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py
index 7b11ea3be..206c36ca9 100644
--- a/tests/test_ext_math.py
+++ b/tests/test_ext_math.py
@@ -1,6 +1,7 @@
"""Test math extensions."""
import re
+import shutil
import subprocess
import warnings
@@ -33,6 +34,7 @@ def test_imgmath_png(app, status, warning):
raise pytest.skip.Exception('dvipng command "dvipng" is not available')
content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ shutil.rmtree(app.outdir)
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.png"'
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
assert re.search(html, content, re.S)
@@ -51,6 +53,7 @@ def test_imgmath_svg(app, status, warning):
raise pytest.skip.Exception('dvisvgm command "dvisvgm" is not available')
content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ shutil.rmtree(app.outdir)
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.svg"'
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
assert re.search(html, content, re.S)
@@ -70,6 +73,7 @@ def test_imgmath_svg_embed(app, status, warning):
pytest.skip('dvisvgm command "dvisvgm" is not available')
content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ shutil.rmtree(app.outdir)
html = r'<img src="data:image/svg\+xml;base64,[\w\+/=]+"'
assert re.search(html, content, re.DOTALL)
@@ -81,6 +85,7 @@ def test_mathjax_options(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ shutil.rmtree(app.outdir)
assert ('<script async="async" integrity="sha384-0123456789" '
'src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">'
'</script>' in content)
@@ -92,6 +97,7 @@ def test_mathjax_align(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ shutil.rmtree(app.outdir)
html = (r'<div class="math notranslate nohighlight">\s*'
r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&amp;= \\pi r\^2\\\\'
r'V \&amp;= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]</div>')