summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/__init__.py10
-rw-r--r--sphinx/addnodes.py56
-rw-r--r--sphinx/application.py125
-rw-r--r--sphinx/builders/html/__init__.py (renamed from sphinx/builders/html.py)66
-rw-r--r--sphinx/builders/latex/__init__.py10
-rw-r--r--sphinx/builders/latex/constants.py2
-rw-r--r--sphinx/builders/latex/transforms.py2
-rw-r--r--sphinx/builders/latex/util.py10
-rw-r--r--sphinx/cmd/quickstart.py156
-rw-r--r--sphinx/cmdline.py43
-rw-r--r--sphinx/config.py36
-rw-r--r--sphinx/deprecation.py6
-rw-r--r--sphinx/directives/__init__.py18
-rw-r--r--sphinx/directives/other.py4
-rw-r--r--sphinx/domains/__init__.py20
-rw-r--r--sphinx/domains/c.py1
-rw-r--r--sphinx/domains/changeset.py9
-rw-r--r--sphinx/domains/cpp.py495
-rw-r--r--sphinx/domains/index.py17
-rw-r--r--sphinx/domains/javascript.py120
-rw-r--r--sphinx/domains/math.py2
-rw-r--r--sphinx/domains/python.py86
-rw-r--r--sphinx/domains/rst.py6
-rw-r--r--sphinx/domains/std.py140
-rw-r--r--sphinx/environment/__init__.py128
-rw-r--r--sphinx/events.py28
-rw-r--r--sphinx/ext/autodoc/__init__.py103
-rw-r--r--sphinx/ext/autodoc/importer.py3
-rw-r--r--sphinx/ext/autodoc/mock.py46
-rw-r--r--sphinx/ext/autodoc/type_comment.py3
-rw-r--r--sphinx/ext/autosummary/__init__.py4
-rw-r--r--sphinx/ext/autosummary/generate.py35
-rw-r--r--sphinx/ext/coverage.py2
-rw-r--r--sphinx/ext/duration.py4
-rw-r--r--sphinx/ext/inheritance_diagram.py6
-rw-r--r--sphinx/ext/mathbase.py153
-rw-r--r--sphinx/ext/napoleon/docstring.py4
-rw-r--r--sphinx/ext/napoleon/iterators.py4
-rw-r--r--sphinx/ext/viewcode.py19
-rw-r--r--sphinx/highlighting.py36
-rw-r--r--sphinx/io.py99
-rw-r--r--sphinx/locale/__init__.py29
-rw-r--r--sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po473
-rw-r--r--sphinx/make_mode.py38
-rw-r--r--sphinx/parsers.py11
-rw-r--r--sphinx/pycode/ast.py68
-rw-r--r--sphinx/pycode/parser.py2
-rw-r--r--sphinx/registry.py65
-rw-r--r--sphinx/search/ja.py16
-rw-r--r--sphinx/templates/latex/longtable.tex_t2
-rw-r--r--sphinx/testing/comparer.py14
-rw-r--r--sphinx/testing/fixtures.py81
-rw-r--r--sphinx/testing/path.py19
-rw-r--r--sphinx/testing/util.py6
-rw-r--r--sphinx/texinputs/Makefile_t16
-rw-r--r--sphinx/texinputs/make.bat_t5
-rw-r--r--sphinx/themes/basic/search.html6
-rw-r--r--sphinx/themes/basic/searchresults.html36
-rw-r--r--sphinx/transforms/post_transforms/__init__.py2
-rw-r--r--sphinx/transforms/post_transforms/compat.py86
-rw-r--r--sphinx/util/__init__.py40
-rw-r--r--sphinx/util/compat.py16
-rw-r--r--sphinx/util/docstrings.py32
-rw-r--r--sphinx/util/docutils.py37
-rw-r--r--sphinx/util/i18n.py10
-rw-r--r--sphinx/util/images.py20
-rw-r--r--sphinx/util/inspect.py91
-rw-r--r--sphinx/util/nodes.py6
-rw-r--r--sphinx/util/osutil.py24
-rw-r--r--sphinx/util/pycompat.py2
-rw-r--r--sphinx/util/requests.py23
-rw-r--r--sphinx/versioning.py11
-rw-r--r--sphinx/writers/html.py33
-rw-r--r--sphinx/writers/html5.py33
-rw-r--r--sphinx/writers/latex.py119
-rw-r--r--sphinx/writers/texinfo.py12
-rw-r--r--sphinx/writers/text.py52
77 files changed, 1451 insertions, 2202 deletions
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index a7cf04ccf..4889f35d5 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
-__version__ = '2.4.4+'
-__released__ = '2.4.4' # used when Sphinx builds its own docs
+__version__ = '3.0.0+'
+__released__ = '3.0.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use.
#:
@@ -43,7 +43,7 @@ __released__ = '2.4.4' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: Before version 1.2, check the string ``sphinx.__version__``.
-version_info = (2, 4, 4, 'beta', 0)
+version_info = (3, 0, 0, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))
@@ -56,8 +56,8 @@ if __version__.endswith('+'):
__version__ = __version__[:-1] # remove '+' for PEP-440 version spec.
try:
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
- stdout=PIPE, stderr=PIPE, encoding='ascii')
+ stdout=PIPE, stderr=PIPE)
if ret.stdout:
- __display_version__ += '/' + ret.stdout.strip()
+ __display_version__ += '/' + ret.stdout.decode('ascii').strip()
except Exception:
pass
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 5dac63867..15d5fc46b 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -14,7 +14,7 @@ from typing import Any, Dict, List, Sequence
from docutils import nodes
from docutils.nodes import Node
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
if False:
# For type annotation
@@ -199,59 +199,6 @@ class production(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a single grammar production rule."""
-# math nodes
-
-
-class math(nodes.math):
- """Node for inline equations.
-
- .. warning:: This node is provided to keep compatibility only.
- It will be removed in nearly future. Don't use this from your extension.
-
- .. deprecated:: 1.8
- Use ``docutils.nodes.math`` instead.
- """
-
- def __getitem__(self, key):
- """Special accessor for supporting ``node['latex']``."""
- if key == 'latex' and 'latex' not in self.attributes:
- warnings.warn("math node for Sphinx was replaced by docutils'. "
- "Therefore please use ``node.astext()`` to get an equation instead.",
- RemovedInSphinx30Warning, stacklevel=2)
- return self.astext()
- else:
- return super().__getitem__(key)
-
-
-class math_block(nodes.math_block):
- """Node for block level equations.
-
- .. warning:: This node is provided to keep compatibility only.
- It will be removed in nearly future. Don't use this from your extension.
-
- .. deprecated:: 1.8
- """
-
- def __getitem__(self, key):
- if key == 'latex' and 'latex' not in self.attributes:
- warnings.warn("displaymath node for Sphinx was replaced by docutils'. "
- "Therefore please use ``node.astext()`` to get an equation instead.",
- RemovedInSphinx30Warning, stacklevel=2)
- return self.astext()
- else:
- return super().__getitem__(key)
-
-
-class displaymath(math_block):
- """Node for block level equations.
-
- .. warning:: This node is provided to keep compatibility only.
- It will be removed in nearly future. Don't use this from your extension.
-
- .. deprecated:: 1.8
- """
-
-
# other directive-level nodes
class index(nodes.Invisible, nodes.Inline, nodes.TextElement):
@@ -389,7 +336,6 @@ def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_node(seealso)
app.add_node(productionlist)
app.add_node(production)
- app.add_node(displaymath)
app.add_node(index)
app.add_node(centered)
app.add_node(acks)
diff --git a/sphinx/application.py b/sphinx/application.py
index 744e62a4e..fbc637e60 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -16,7 +16,6 @@ import platform
import sys
import warnings
from collections import deque
-from inspect import isclass
from io import StringIO
from os import path
from typing import Any, Callable, Dict, IO, List, Tuple, Union
@@ -30,9 +29,7 @@ from pygments.lexer import Lexer
import sphinx
from sphinx import package_dir, locale
from sphinx.config import Config
-from sphinx.deprecation import (
- RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
-)
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain, Index
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
@@ -46,11 +43,10 @@ from sphinx.registry import SphinxComponentRegistry
from sphinx.roles import XRefRole
from sphinx.theming import Theme
from sphinx.util import docutils
-from sphinx.util import import_object, progress_message
from sphinx.util import logging
+from sphinx.util import progress_message
from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold # type: ignore
-from sphinx.util.docutils import directive_helper
from sphinx.util.i18n import CatalogRepository
from sphinx.util.logging import prefixed_warnings
from sphinx.util.osutil import abspath, ensuredir, relpath
@@ -105,7 +101,6 @@ builtin_extensions = (
'sphinx.transforms.post_transforms',
'sphinx.transforms.post_transforms.code',
'sphinx.transforms.post_transforms.images',
- 'sphinx.transforms.post_transforms.compat',
'sphinx.util.compat',
'sphinx.versioning',
# collectors should be loaded by specific order
@@ -408,29 +403,26 @@ class Sphinx:
if version > sphinx.__display_version__[:3]:
raise VersionRequirementError(version)
- def import_object(self, objname: str, source: str = None) -> Any:
- """Import an object from a ``module.name`` string.
-
- .. deprecated:: 1.8
- Use ``sphinx.util.import_object()`` instead.
- """
- warnings.warn('app.import_object() is deprecated. '
- 'Use sphinx.util.add_object_type() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return import_object(objname, source=None)
-
# event interface
- def connect(self, event: str, callback: Callable) -> int:
+ def connect(self, event: str, callback: Callable, priority: int = 500) -> int:
"""Register *callback* to be called when *event* is emitted.
For details on available core events and the arguments of callback
functions, please see :ref:`events`.
+ Registered callbacks will be invoked on event in the order of *priority* and
+ registration. The priority is ascending order.
+
The method returns a "listener ID" that can be used as an argument to
:meth:`disconnect`.
+
+ .. versionchanged:: 3.0
+
+ Support *priority*
"""
- listener_id = self.events.connect(event, callback)
- logger.debug('[app] connecting event %r: %r [id=%s]', event, callback, listener_id)
+ listener_id = self.events.connect(event, callback, priority)
+ logger.debug('[app] connecting event %r (%d): %r [id=%s]',
+ event, priority, callback, listener_id)
return listener_id
def disconnect(self, listener_id: int) -> None:
@@ -592,36 +584,13 @@ class Sphinx:
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
self.add_node(node, override=override, **kwargs)
- @property
- def enumerable_nodes(self) -> Dict["Type[Node]", Tuple[str, TitleGetter]]:
- warnings.warn('app.enumerable_nodes() is deprecated. '
- 'Use app.get_domain("std").enumerable_nodes instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.registry.enumerable_nodes
-
- def add_directive(self, name: str, obj: Any, content: bool = None,
- arguments: Tuple[int, int, bool] = None, override: bool = False,
- **options: Any) -> None:
+ def add_directive(self, name: str, cls: "Type[Directive]", override: bool = False) -> None:
"""Register a Docutils directive.
- *name* must be the prospective directive name. There are two possible
- ways to write a directive:
-
- - In the docutils 0.4 style, *obj* is the directive function.
- *content*, *arguments* and *options* are set as attributes on the
- function and determine whether the directive has content, arguments
- and options, respectively. **This style is deprecated.**
-
- - In the docutils 0.5 style, *obj* is the directive class.
- It must already have attributes named *has_content*,
- *required_arguments*, *optional_arguments*,
- *final_argument_whitespace* and *option_spec* that correspond to the
- options for the function way. See `the Docutils docs
- <http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_
- for details.
-
- The directive class must inherit from the class
- ``docutils.parsers.rst.Directive``.
+ *name* must be the prospective directive name. *cls* is a directive
+ class which inherits ``docutils.parsers.rst.Directive``. For more
+ details, see `the Docutils docs
+ <http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_ .
For example, the (already existing) :rst:dir:`literalinclude` directive
would be added like this:
@@ -652,17 +621,12 @@ class Sphinx:
.. versionchanged:: 1.8
Add *override* keyword.
"""
- logger.debug('[app] adding directive: %r',
- (name, obj, content, arguments, options))
+ logger.debug('[app] adding directive: %r', (name, cls))
if not override and docutils.is_directive_registered(name):
logger.warning(__('directive %r is already registered, it will be overridden'),
name, type='app', subtype='add_directive')
- if not isclass(obj) or not issubclass(obj, Directive):
- directive = directive_helper(obj, content, arguments, **options)
- docutils.register_directive(name, directive)
- else:
- docutils.register_directive(name, obj)
+ docutils.register_directive(name, cls)
def add_role(self, name: str, role: Any, override: bool = False) -> None:
"""Register a Docutils role.
@@ -712,25 +676,8 @@ class Sphinx:
"""
self.registry.add_domain(domain, override=override)
- def override_domain(self, domain: "Type[Domain]") -> None:
- """Override a registered domain.
-
- Make the given *domain* class known to Sphinx, assuming that there is
- already a domain with its ``.name``. The new domain must be a subclass
- of the existing one.
-
- .. versionadded:: 1.0
- .. deprecated:: 1.8
- Integrated to :meth:`add_domain`.
- """
- warnings.warn('app.override_domain() is deprecated. '
- 'Use app.add_domain() with override option instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.registry.add_domain(domain, override=True)
-
- def add_directive_to_domain(self, domain: str, name: str, obj: Any,
- has_content: bool = None, argument_spec: Any = None,
- override: bool = False, **option_spec: Any) -> None:
+ def add_directive_to_domain(self, domain: str, name: str,
+ cls: "Type[Directive]", override: bool = False) -> None:
"""Register a Docutils directive in a domain.
Like :meth:`add_directive`, but the directive is added to the domain
@@ -740,9 +687,7 @@ class Sphinx:
.. versionchanged:: 1.8
Add *override* keyword.
"""
- self.registry.add_directive_to_domain(domain, name, obj,
- has_content, argument_spec, override=override,
- **option_spec)
+ self.registry.add_directive_to_domain(domain, name, cls, override=override)
def add_role_to_domain(self, domain: str, name: str, role: Union[RoleFunction, XRefRole],
override: bool = False) -> None:
@@ -924,8 +869,10 @@ class Sphinx:
Add *filename* to the list of JavaScript files that the default HTML
template will include. The filename must be relative to the HTML
- static path , or a full URI with scheme. The keyword arguments are
- also accepted for attributes of ``<script>`` tag.
+ static path , or a full URI with scheme. If the keyword argument
+ ``body`` is given, its value will be added between the
+ ``<script>`` tags. Extra keyword arguments are included as
+ attributes of the ``<script>`` tag.
Example::
@@ -935,6 +882,9 @@ class Sphinx:
app.add_js_file('example.js', async="async")
# => <script src="_static/example.js" async="async"></script>
+ app.add_js_file(None, body="var myVariable = 'foo';")
+ # => <script>var myVariable = 'foo';</script>
+
.. versionadded:: 0.5
.. versionchanged:: 1.8
@@ -1197,12 +1147,6 @@ class Sphinx:
return True
- @property
- def _setting_up_extension(self) -> List[str]:
- warnings.warn('app._setting_up_extension is deprecated.',
- RemovedInSphinx30Warning)
- return ['?']
-
class TemplateBridge:
"""
@@ -1239,12 +1183,3 @@ class TemplateBridge:
specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')
-
-
-from sphinx.config import CONFIG_FILENAME # NOQA
-
-deprecated_alias('sphinx.application',
- {
- 'CONFIG_FILENAME': CONFIG_FILENAME,
- },
- RemovedInSphinx30Warning)
diff --git a/sphinx/builders/html.py b/sphinx/builders/html/__init__.py
index b3d2f1da2..cf8cd56ce 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html/__init__.py
@@ -28,7 +28,7 @@ from sphinx import package_dir, __display_version__
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain, Index, IndexEntry
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
@@ -53,7 +53,7 @@ if False:
from typing import Type # for python3.5.1
-# HTML5 Writer is avialable or not
+# HTML5 Writer is available or not
if is_html5_writer_available():
from sphinx.writers.html5 import HTML5Translator
html5_ready = True
@@ -103,35 +103,6 @@ class Stylesheet(str):
return self
-class JSContainer(list):
- """The container for JavaScript scripts."""
- def insert(self, index: int, obj: str) -> None:
- warnings.warn('To modify script_files in the theme is deprecated. '
- 'Please insert a <script> tag directly in your theme instead.',
- RemovedInSphinx30Warning, stacklevel=3)
- super().insert(index, obj)
-
- def extend(self, other: List[str]) -> None: # type: ignore
- warnings.warn('To modify script_files in the theme is deprecated. '
- 'Please insert a <script> tag directly in your theme instead.',
- RemovedInSphinx30Warning, stacklevel=3)
- for item in other:
- self.append(item)
-
- def __iadd__(self, other: List[str]) -> "JSContainer": # type: ignore
- warnings.warn('To modify script_files in the theme is deprecated. '
- 'Please insert a <script> tag directly in your theme instead.',
- RemovedInSphinx30Warning, stacklevel=3)
- for item in other:
- self.append(item)
- return self
-
- def __add__(self, other: List[str]) -> "JSContainer":
- ret = JSContainer(self)
- ret += other
- return ret
-
-
class JavaScript(str):
"""A metadata of javascript file.
@@ -234,7 +205,7 @@ class StandaloneHTMLBuilder(Builder):
self.css_files = [] # type: List[Dict[str, str]]
# JS files
- self.script_files = JSContainer() # type: List[JavaScript]
+ self.script_files = [] # type: List[JavaScript]
def init(self) -> None:
self.build_info = self.create_build_info()
@@ -836,13 +807,17 @@ class StandaloneHTMLBuilder(Builder):
if self.config.html_scaled_image_link and self.html_scaled_image_link:
for node in doctree.traverse(nodes.image):
- scale_keys = ('scale', 'width', 'height')
- if not any((key in node) for key in scale_keys) or \
- isinstance(node.parent, nodes.reference):
- # docutils does unfortunately not preserve the
- # ``target`` attribute on images, so we need to check
- # the parent node here.
+ if not any((key in node) for key in ['scale', 'width', 'height']):
+ # resizing options are not given. scaled image link is available
+ # only for resized images.
+ continue
+ elif isinstance(node.parent, nodes.reference):
+ # A image having hyperlink target
continue
+ elif 'no-scaled-link' in node['classes']:
+ # scaled image link is disabled for this node
+ continue
+
uri = node['uri']
reference = nodes.reference('', '', internal=True)
if uri in self.images:
@@ -876,7 +851,11 @@ class StandaloneHTMLBuilder(Builder):
if self.indexer is not None and title:
filename = self.env.doc2path(pagename, base=None)
try:
- self.indexer.feed(pagename, filename, title, doctree)
+ metadata = self.env.metadata.get(pagename, {})
+ if 'nosearch' in metadata:
+ self.indexer.feed(pagename, filename, '', new_document(''))
+ else:
+ self.indexer.feed(pagename, filename, title, doctree)
except TypeError:
# fallback for old search-adapters
self.indexer.feed(pagename, title, doctree) # type: ignore
@@ -1000,15 +979,6 @@ class StandaloneHTMLBuilder(Builder):
return False
ctx['hasdoc'] = hasdoc
- def warn(*args: Any, **kwargs: Any) -> str:
- """Simple warn() wrapper for themes."""
- warnings.warn('The template function warn() was deprecated. '
- 'Use warning() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- logger.warning(*args, **kwargs)
- return '' # return empty string
- ctx['warn'] = warn
-
ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index 6712ffc25..96e6d13f4 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -408,31 +408,31 @@ def patch_settings(settings: Any) -> Any:
class Values(type(settings)): # type: ignore
@property
- def author(self):
+ def author(self) -> str:
warnings.warn('settings.author is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._author
@property
- def title(self):
+ def title(self) -> str:
warnings.warn('settings.title is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._title
@property
- def contentsname(self):
+ def contentsname(self) -> str:
warnings.warn('settings.contentsname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._contentsname
@property
- def docname(self):
+ def docname(self) -> str:
warnings.warn('settings.docname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._docname
@property
- def docclass(self):
+ def docclass(self) -> str:
warnings.warn('settings.docclass is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._docclass
diff --git a/sphinx/builders/latex/constants.py b/sphinx/builders/latex/constants.py
index 39fbe0195..70fd0b1d1 100644
--- a/sphinx/builders/latex/constants.py
+++ b/sphinx/builders/latex/constants.py
@@ -184,6 +184,8 @@ ADDITIONAL_SETTINGS = {
'babel': '\\usepackage{babel}',
},
('xelatex', 'zh'): {
+ 'polyglossia': '',
+ 'babel': '\\usepackage{babel}',
'fontenc': '\\usepackage{xeCJK}',
},
('xelatex', 'el'): {
diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py
index 3ef0ff5d9..28841ad77 100644
--- a/sphinx/builders/latex/transforms.py
+++ b/sphinx/builders/latex/transforms.py
@@ -591,7 +591,7 @@ class IndexInSectionTitleTransform(SphinxTransform):
"""
default_priority = 400
- def apply(self):
+ def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(nodes.title):
if isinstance(node.parent, nodes.section):
for i, index in enumerate(node.traverse(addnodes.index)):
diff --git a/sphinx/builders/latex/util.py b/sphinx/builders/latex/util.py
index 8155d1fd7..b7d79121c 100644
--- a/sphinx/builders/latex/util.py
+++ b/sphinx/builders/latex/util.py
@@ -8,12 +8,8 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
-
from docutils.writers.latex2e import Babel
-from sphinx.deprecation import RemovedInSphinx30Warning
-
class ExtBabel(Babel):
cyrillic_languages = ('bulgarian', 'kazakh', 'mongolian', 'russian', 'ukrainian')
@@ -24,12 +20,6 @@ class ExtBabel(Babel):
self.supported = True
super().__init__(language_code or '')
- def get_shorthandoff(self) -> str:
- warnings.warn('ExtBabel.get_shorthandoff() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- from sphinx.writers.latex import SHORTHANDOFF
- return SHORTHANDOFF
-
def uses_cyrillic(self) -> bool:
return self.language in self.cyrillic_languages
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index eb7e9eb02..8f8ae58a1 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -54,10 +54,8 @@ EXTENSIONS = OrderedDict([
('imgmath', __('include math, rendered as PNG or SVG images')),
('mathjax', __('include math, rendered in the browser by MathJax')),
('ifconfig', __('conditional inclusion of content based on config values')),
- ('viewcode',
- __('include links to the source code of documented Python objects')),
- ('githubpages',
- __('create .nojekyll file to publish the document on GitHub pages')),
+ ('viewcode', __('include links to the source code of documented Python objects')),
+ ('githubpages', __('create .nojekyll file to publish the document on GitHub pages')),
])
DEFAULTS = {
@@ -129,8 +127,7 @@ def boolean(x: str) -> bool:
def suffix(x: str) -> str:
if not (x[0:1] == '.' and len(x) > 1):
- raise ValidationError(__("Please enter a file suffix, "
- "e.g. '.rst' or '.txt'."))
+ raise ValidationError(__("Please enter a file suffix, e.g. '.rst' or '.txt'."))
return x
@@ -228,16 +225,16 @@ def ask_user(d: Dict) -> None:
"""
print(bold(__('Welcome to the Sphinx %s quickstart utility.')) % __display_version__)
- print(__('''
-Please enter values for the following settings (just press Enter to
-accept a default value, if one is given in brackets).'''))
+ print()
+ print(__('Please enter values for the following settings (just press Enter to\n'
+ 'accept a default value, if one is given in brackets).'))
if 'path' in d:
- print(bold(__('''
-Selected root path: %s''') % d['path']))
+ print()
+ print(bold(__('Selected root path: %s')) % d['path'])
else:
- print(__('''
-Enter the root path for documentation.'''))
+ print()
+ print(__('Enter the root path for documentation.'))
d['path'] = do_prompt(__('Root path for the documentation'), '.', is_path)
while path.isfile(path.join(d['path'], 'conf.py')) or \
@@ -247,70 +244,68 @@ Enter the root path for documentation.'''))
'selected root path.')))
print(__('sphinx-quickstart will not overwrite existing Sphinx projects.'))
print()
- d['path'] = do_prompt(__('Please enter a new root path (or just Enter '
- 'to exit)'), '', is_path)
+ d['path'] = do_prompt(__('Please enter a new root path (or just Enter to exit)'),
+ '', is_path)
if not d['path']:
sys.exit(1)
if 'sep' not in d:
- print(__('''
-You have two options for placing the build directory for Sphinx output.
-Either, you use a directory "_build" within the root path, or you separate
-"source" and "build" directories within the root path.'''))
- d['sep'] = do_prompt(__('Separate source and build directories (y/n)'),
- 'n', boolean)
+ print()
+ print(__('You have two options for placing the build directory for Sphinx output.\n'
+ 'Either, you use a directory "_build" within the root path, or you separate\n'
+ '"source" and "build" directories within the root path.'))
+ d['sep'] = do_prompt(__('Separate source and build directories (y/n)'), 'n', boolean)
if 'dot' not in d:
- print(__('''
-Inside the root directory, two more directories will be created; "_templates"
-for custom HTML templates and "_static" for custom stylesheets and other static
-files. You can enter another prefix (such as ".") to replace the underscore.'''))
+ print()
+ print(__('Inside the root directory, two more directories will be created; "_templates"\n' # NOQA
+ 'for custom HTML templates and "_static" for custom stylesheets and other static\n' # NOQA
+ 'files. You can enter another prefix (such as ".") to replace the underscore.')) # NOQA
d['dot'] = do_prompt(__('Name prefix for templates and static dir'), '_', ok)
if 'project' not in d:
- print(__('''
-The project name will occur in several places in the built documentation.'''))
+ print()
+ print(__('The project name will occur in several places in the built documentation.'))
d['project'] = do_prompt(__('Project name'))
if 'author' not in d:
d['author'] = do_prompt(__('Author name(s)'))
if 'version' not in d:
- print(__('''
-Sphinx has the notion of a "version" and a "release" for the
-software. Each version can have multiple releases. For example, for
-Python the version is something like 2.5 or 3.0, while the release is
-something like 2.5.1 or 3.0a1. If you don't need this dual structure,
-just set both to the same value.'''))
+ print()
+ print(__('Sphinx has the notion of a "version" and a "release" for the\n'
+ 'software. Each version can have multiple releases. For example, for\n'
+ 'Python the version is something like 2.5 or 3.0, while the release is\n'
+ 'something like 2.5.1 or 3.0a1. If you don\'t need this dual structure,\n'
+ 'just set both to the same value.'))
d['version'] = do_prompt(__('Project version'), '', allow_empty)
if 'release' not in d:
d['release'] = do_prompt(__('Project release'), d['version'], allow_empty)
if 'language' not in d:
- print(__('''
-If the documents are to be written in a language other than English,
-you can select a language here by its language code. Sphinx will then
-translate text that it generates into that language.
-
-For a list of supported codes, see
-https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.'''))
+ print()
+ print(__('If the documents are to be written in a language other than English,\n'
+ 'you can select a language here by its language code. Sphinx will then\n'
+ 'translate text that it generates into that language.\n'
+ '\n'
+ 'For a list of supported codes, see\n'
+ 'https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.')) # NOQA
d['language'] = do_prompt(__('Project language'), 'en')
if d['language'] == 'en':
d['language'] = None
if 'suffix' not in d:
- print(__('''
-The file name suffix for source files. Commonly, this is either ".txt"
-or ".rst". Only files with this suffix are considered documents.'''))
+ print()
+ print(__('The file name suffix for source files. Commonly, this is either ".txt"\n'
+ 'or ".rst". Only files with this suffix are considered documents.'))
d['suffix'] = do_prompt(__('Source file suffix'), '.rst', suffix)
if 'master' not in d:
- print(__('''
-One document is special in that it is considered the top node of the
-"contents tree", that is, it is the root of the hierarchical structure
-of the documents. Normally, this is "index", but if your "index"
-document is a custom template, you can also set this to another filename.'''))
- d['master'] = do_prompt(__('Name of your master document (without suffix)'),
- 'index')
+ print()
+ print(__('One document is special in that it is considered the top node of the\n'
+ '"contents tree", that is, it is the root of the hierarchical structure\n'
+ 'of the documents. Normally, this is "index", but if your "index"\n'
+ 'document is a custom template, you can also set this to another filename.'))
+ d['master'] = do_prompt(__('Name of your master document (without suffix)'), 'index')
while path.isfile(path.join(d['path'], d['master'] + d['suffix'])) or \
path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix'])):
@@ -323,8 +318,7 @@ document is a custom template, you can also set this to another filename.'''))
'existing file and press Enter'), d['master'])
if 'extensions' not in d:
- print(__('Indicate which of the following Sphinx extensions should be '
- 'enabled:'))
+ print(__('Indicate which of the following Sphinx extensions should be enabled:'))
d['extensions'] = []
for name, description in EXTENSIONS.items():
if do_prompt('%s: %s (y/n)' % (name, description), 'n', boolean):
@@ -332,20 +326,19 @@ document is a custom template, you can also set this to another filename.'''))
# Handle conflicting options
if {'sphinx.ext.imgmath', 'sphinx.ext.mathjax'}.issubset(d['extensions']):
- print(__('Note: imgmath and mathjax cannot be enabled at the same '
- 'time. imgmath has been deselected.'))
+ print(__('Note: imgmath and mathjax cannot be enabled at the same time. '
+ 'imgmath has been deselected.'))
d['extensions'].remove('sphinx.ext.imgmath')
if 'makefile' not in d:
- print(__('''
-A Makefile and a Windows command file can be generated for you so that you
-only have to run e.g. `make html' instead of invoking sphinx-build
-directly.'''))
+ print()
+ print(__('A Makefile and a Windows command file can be generated for you so that you\n'
+ 'only have to run e.g. `make html\' instead of invoking sphinx-build\n'
+ 'directly.'))
d['makefile'] = do_prompt(__('Create Makefile? (y/n)'), 'y', boolean)
if 'batchfile' not in d:
- d['batchfile'] = do_prompt(__('Create Windows command file? (y/n)'),
- 'y', boolean)
+ d['batchfile'] = do_prompt(__('Create Windows command file? (y/n)'), 'y', boolean)
print()
@@ -428,17 +421,18 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
return
print()
print(bold(__('Finished: An initial directory structure has been created.')))
- print(__('''
-You should now populate your master file %s and create other documentation
-source files. ''') % masterfile + ((d['makefile'] or d['batchfile']) and __('''\
-Use the Makefile to build the docs, like so:
- make builder
-''') or __('''\
-Use the sphinx-build command to build the docs, like so:
- sphinx-build -b builder %s %s
-''') % (srcdir, builddir)) + __('''\
-where "builder" is one of the supported builders, e.g. html, latex or linkcheck.
-'''))
+ print()
+ print(__('You should now populate your master file %s and create other documentation\n'
+ 'source files. ') % masterfile, end='')
+ if d['makefile'] or d['batchfile']:
+ print(__('Use the Makefile to build the docs, like so:\n'
+ ' make builder'))
+ else:
+ print(__('Use the sphinx-build command to build the docs, like so:\n'
+ ' sphinx-build -b builder %s %s') % (srcdir, builddir))
+ print(__('where "builder" is one of the supported builders, '
+ 'e.g. html, latex or linkcheck.'))
+ print()
def valid_dir(d: Dict) -> bool:
@@ -471,16 +465,18 @@ def valid_dir(d: Dict) -> bool:
def get_parser() -> argparse.ArgumentParser:
+ description = __(
+ "\n"
+ "Generate required files for a Sphinx project.\n"
+ "\n"
+ "sphinx-quickstart is an interactive tool that asks some questions about your\n"
+ "project and then generates a complete documentation directory and sample\n"
+ "Makefile to be used with sphinx-build.\n"
+ )
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] <PROJECT_DIR>',
epilog=__("For more information, visit <http://sphinx-doc.org/>."),
- description=__("""
-Generate required files for a Sphinx project.
-
-sphinx-quickstart is an interactive tool that asks some questions about your
-project and then generates a complete documentation directory and sample
-Makefile to be used with sphinx-build.
-"""))
+ description=description)
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet',
default=None,
@@ -579,8 +575,8 @@ def main(argv: List[str] = sys.argv[1:]) -> int:
try:
if 'quiet' in d:
if not {'project', 'author'}.issubset(d):
- print(__('''"quiet" is specified, but any of "project" or \
-"author" is not specified.'''))
+ print(__('"quiet" is specified, but any of "project" or '
+ '"author" is not specified.'))
return 1
if {'quiet', 'project', 'author'}.issubset(d):
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
deleted file mode 100644
index f938f8234..000000000
--- a/sphinx/cmdline.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""
- sphinx.cmdline
- ~~~~~~~~~~~~~~
-
- sphinx-build command-line handling.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import argparse
-import sys
-import warnings
-from typing import Any, IO, List, Union
-
-from sphinx.application import Sphinx
-from sphinx.cmd import build
-from sphinx.deprecation import RemovedInSphinx30Warning
-
-
-def handle_exception(app: Sphinx, args: Any, exception: Union[Exception, KeyboardInterrupt],
- stderr: IO = sys.stderr) -> None:
- warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- build.handle_exception(app, args, exception, stderr)
-
-
-def jobs_argument(value: str) -> int:
- warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return build.jobs_argument(value)
-
-
-def get_parser() -> argparse.ArgumentParser:
- warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return build.get_parser()
-
-
-def main(argv: List[str] = sys.argv[1:]) -> int:
- warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return build.main(argv)
diff --git a/sphinx/config.py b/sphinx/config.py
index bba994389..87007c33d 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -18,7 +18,7 @@ from typing import (
Any, Callable, Dict, Generator, Iterator, List, NamedTuple, Set, Tuple, Union
)
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ConfigError, ExtensionError
from sphinx.locale import _, __
from sphinx.util import logging
@@ -154,26 +154,7 @@ class Config:
'env', []),
} # type: Dict[str, Tuple]
- def __init__(self, *args: Any) -> None:
- if len(args) == 4:
- # old style arguments: (dirname, filename, overrides, tags)
- warnings.warn('The argument of Config() class has been changed. '
- 'Use Config.read() to read configuration from conf.py.',
- RemovedInSphinx30Warning, stacklevel=2)
- dirname, filename, overrides, tags = args
- if dirname is None:
- config = {} # type: Dict[str, Any]
- else:
- config = eval_config_file(path.join(dirname, filename), tags)
- else:
- # new style arguments: (config={}, overrides={})
- if len(args) == 0:
- config, overrides = {}, {}
- elif len(args) == 1:
- config, overrides = args[0], {}
- else:
- config, overrides = args[:2]
-
+ def __init__(self, config: Dict[str, Any] = {}, overrides: Dict[str, Any] = {}) -> None:
self.overrides = dict(overrides)
self.values = Config.config_values.copy()
self._raw_config = config
@@ -193,16 +174,6 @@ class Config:
namespace = eval_config_file(filename, tags)
return cls(namespace, overrides or {})
- def check_types(self) -> None:
- warnings.warn('Config.check_types() is deprecated. Use check_confval_types() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- check_confval_types(None, self)
-
- def check_unicode(self) -> None:
- warnings.warn('Config.check_unicode() is deprecated. Use check_unicode() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- check_unicode(self)
-
def convert_overrides(self, name: str, value: Any) -> Any:
if not isinstance(value, str):
return value
@@ -353,6 +324,9 @@ def eval_config_file(filename: str, tags: Tags) -> Dict[str, Any]:
msg = __("The configuration file (or one of the modules it imports) "
"called sys.exit()")
raise ConfigError(msg)
+ except ConfigError:
+ # pass through ConfigError from conf.py as is. It will be shown in console.
+ raise
except Exception:
msg = __("There is a programmable error in your configuration file:\n\n%s")
raise ConfigError(msg % traceback.format_exc())
diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py
index dec5efa85..5e5e673d2 100644
--- a/sphinx/deprecation.py
+++ b/sphinx/deprecation.py
@@ -15,15 +15,15 @@ from typing import Any, Dict
from typing import Type # for python3.5.1
-class RemovedInSphinx30Warning(DeprecationWarning):
+class RemovedInSphinx40Warning(DeprecationWarning):
pass
-class RemovedInSphinx40Warning(PendingDeprecationWarning):
+class RemovedInSphinx50Warning(PendingDeprecationWarning):
pass
-RemovedInNextVersionWarning = RemovedInSphinx30Warning
+RemovedInNextVersionWarning = RemovedInSphinx40Warning
def deprecated_alias(modname: str, objects: Dict, warning: Type[Warning]) -> None:
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 9a2fb4412..0d9490f31 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -18,7 +18,9 @@ from docutils.parsers.rst import directives, roles
from sphinx import addnodes
from sphinx.addnodes import desc_signature
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
+from sphinx.deprecation import (
+ RemovedInSphinx40Warning, RemovedInSphinx50Warning, deprecated_alias
+)
from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer, Field, TypedField
from sphinx.util.docutils import SphinxDirective
@@ -34,7 +36,7 @@ nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(.)')
-def optional_int(argument):
+def optional_int(argument: str) -> int:
"""
Check for an integer argument or None value; raise ``ValueError`` if not.
"""
@@ -160,6 +162,8 @@ class ObjectDescription(SphinxDirective):
# 'desctype' is a backwards compatible attribute
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = noindex = ('noindex' in self.options)
+ if self.domain:
+ node['classes'].append(self.domain)
self.names = [] # type: List[Any]
signatures = self.get_signatures()
@@ -167,7 +171,7 @@ class ObjectDescription(SphinxDirective):
# add a signature node for each signature in the current unit
# and add a reference target for it
signode = addnodes.desc_signature(sig, '')
- signode['first'] = False
+ self.set_source_info(signode)
node.append(signode)
try:
# name can also be a tuple, e.g. (classname, objname);
@@ -285,9 +289,11 @@ deprecated_alias('sphinx.directives',
},
RemovedInSphinx40Warning)
-
-# backwards compatible old name (will be marked deprecated in 3.0)
-DescDirective = ObjectDescription
+deprecated_alias('sphinx.directives',
+ {
+ 'DescDirective': ObjectDescription,
+ },
+ RemovedInSphinx50Warning)
def setup(app: "Sphinx") -> Dict[str, Any]:
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index 87d769b41..e4fcc0f5c 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -85,14 +85,14 @@ class TocTree(SphinxDirective):
ret.append(wrappernode)
return ret
- def parse_content(self, toctree):
+ def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
suffixes = self.config.source_suffix
# glob target documents
all_docnames = self.env.found_docs.copy()
all_docnames.remove(self.env.docname) # remove current document
- ret = []
+ ret = [] # type: List[Node]
excluded = Matcher(self.config.exclude_patterns)
for entry in self.content:
if not entry:
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index b521cd025..11b3a4604 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -11,6 +11,7 @@
import copy
from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Union
+from typing import cast
from docutils import nodes
from docutils.nodes import Element, Node, system_message
@@ -70,6 +71,9 @@ class Index:
a domain, subclass Index, overriding the three name attributes:
* `name` is an identifier used for generating file names.
+ It is also used for a hyperlink target for the index. Therefore, users can
+ refer the index page using ``ref`` role and a string which is combined
+ domain name and ``name`` attribute (ex. ``:ref:`py-modindex```).
* `localname` is the section title for the index.
* `shortname` is a short name for the index, for use in the relation bar in
HTML output. Can be empty to disable entries in the relation bar.
@@ -77,6 +81,11 @@ class Index:
and providing a :meth:`generate()` method. Then, add the index class to
your domain's `indices` list. Extensions can add indices to existing
domains using :meth:`~sphinx.application.Sphinx.add_index_to_domain()`.
+
+ .. versionchanged:: 3.0
+
+ Index pages can be referred by domain name and index name via
+ :rst:role:`ref` role.
"""
name = None # type: str
@@ -219,6 +228,17 @@ class Domain:
self.objtypes_for_role = self._role2type.get # type: Callable[[str], List[str]]
self.role_for_objtype = self._type2role.get # type: Callable[[str], str]
+ def setup(self) -> None:
+ """Set up domain object."""
+ from sphinx.domains.std import StandardDomain
+
+ # Add special hyperlink target for index pages (ex. py-modindex)
+ std = cast(StandardDomain, self.env.get_domain('std'))
+ for index in self.indices:
+ if index.name and index.localname:
+ docname = "%s-%s" % (self.name, index.name)
+ std.note_hyperlink_target(docname, docname, '', index.localname)
+
def add_object_type(self, name: str, objtype: ObjType) -> None:
"""Add an object type."""
self.object_types[name] = objtype
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index cc24df47e..539a52e59 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -203,7 +203,6 @@ class CObject(ObjectDescription):
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
signode['ids'].append(targetname)
- signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
domain = cast(CDomain, self.env.get_domain('c'))
diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py
index f3f1d8aef..a07944db8 100644
--- a/sphinx/domains/changeset.py
+++ b/sphinx/domains/changeset.py
@@ -16,8 +16,6 @@ from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
-from sphinx import locale
-from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning
from sphinx.domains import Domain
from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
@@ -41,13 +39,6 @@ versionlabel_classes = {
'deprecated': 'deprecated',
}
-locale.versionlabels = DeprecatedDict(
- versionlabels,
- 'sphinx.locale.versionlabels is deprecated. '
- 'Please use sphinx.domains.changeset.versionlabels instead.',
- RemovedInSphinx30Warning
-)
-
# TODO: move to typing.NamedTuple after dropping py35 support (see #5958)
ChangeSet = namedtuple('ChangeSet',
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 53c958601..3a23ff15a 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -367,6 +367,8 @@ _keywords = [
_max_id = 4
_id_prefix = [None, '', '_CPPv2', '_CPPv3', '_CPPv4']
+# Ids are used in lookup keys which are used across pickled files,
+# so when _max_id changes, make sure to update the ENV_VERSION.
# ------------------------------------------------------------------------------
# Id v1 constants
@@ -1790,7 +1792,8 @@ class ASTTemplateIntroduction(ASTBase):
class ASTTemplateDeclarationPrefix(ASTBase):
- def __init__(self, templates: List[Any]) -> None:
+ def __init__(self, templates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]])\
+ -> None:
# templates is None means it's an explicit instantiation of a variable
self.templates = templates
@@ -3547,10 +3550,25 @@ class SymbolLookupResult:
self.templateArgs = templateArgs
+class LookupKey:
+ def __init__(self, data: List[Tuple[ASTNestedNameElement,
+ Union[ASTTemplateParams,
+ ASTTemplateIntroduction],
+ str]]) -> None:
+ self.data = data
+
+
class Symbol:
+ debug_indent = 0
+ debug_indent_string = " "
debug_lookup = False
debug_show_tree = False
+ @staticmethod
+ def debug_print(*args):
+ print(Symbol.debug_indent_string * Symbol.debug_indent, end="")
+ print(*args)
+
def _assert_invariants(self) -> None:
if not self.parent:
# parent == None means global scope, so declaration means a parent
@@ -3570,9 +3588,12 @@ class Symbol:
return super().__setattr__(key, value)
def __init__(self, parent: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator],
- templateParams: Any, templateArgs: Any, declaration: ASTDeclaration,
- docname: str) -> None:
+ templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction],
+ templateArgs: Any, declaration: ASTDeclaration, docname: str) -> None:
self.parent = parent
+ # declarations in a single directive are linked together
+ self.siblingAbove = None # type: Symbol
+ self.siblingBelow = None # type: Symbol
self.identOrOp = identOrOp
self.templateParams = templateParams # template<templateParams>
self.templateArgs = templateArgs # identifier<templateArgs>
@@ -3607,6 +3628,9 @@ class Symbol:
self._add_template_and_function_params()
def _add_template_and_function_params(self):
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("_add_template_and_function_params:")
# Note: we may be called from _fill_empty, so the symbols we want
# to add may actually already be present (as empty symbols).
@@ -3636,6 +3660,8 @@ class Symbol:
assert not nn.rooted
assert len(nn.names) == 1
self._add_symbols(nn, [], decl, self.docname)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 1
def remove(self):
if self.parent is None:
@@ -3645,12 +3671,18 @@ class Symbol:
self.parent = None
def clear_doc(self, docname: str) -> None:
- newChildren = []
+ newChildren = [] # type: List[Symbol]
for sChild in self._children:
sChild.clear_doc(docname)
if sChild.declaration and sChild.docname == docname:
sChild.declaration = None
sChild.docname = None
+ if sChild.siblingAbove is not None:
+ sChild.siblingAbove.siblingBelow = sChild.siblingBelow
+ if sChild.siblingBelow is not None:
+ sChild.siblingBelow.siblingAbove = sChild.siblingAbove
+ sChild.siblingAbove = None
+ sChild.siblingBelow = None
newChildren.append(sChild)
self._children = newChildren
@@ -3669,7 +3701,11 @@ class Symbol:
yield from c.children_recurse_anon
- def get_lookup_key(self) -> List[Tuple[ASTNestedNameElement, Any]]:
+ def get_lookup_key(self) -> "LookupKey":
+ # The pickle files for the environment and for each document are distinct.
+ # The environment has all the symbols, but the documents has xrefs that
+ # must know their scope. A lookup key is essentially a specification of
+ # how to find a specific symbol.
symbols = []
s = self
while s.parent:
@@ -3679,14 +3715,23 @@ class Symbol:
key = []
for s in symbols:
nne = ASTNestedNameElement(s.identOrOp, s.templateArgs)
- key.append((nne, s.templateParams))
- return key
+ if s.declaration is not None:
+ key.append((nne, s.templateParams, s.declaration.get_newest_id()))
+ else:
+ key.append((nne, s.templateParams, None))
+ return LookupKey(key)
def get_full_nested_name(self) -> ASTNestedName:
+ symbols = []
+ s = self
+ while s.parent:
+ symbols.append(s)
+ s = s.parent
+ symbols.reverse()
names = []
templates = []
- for nne, templateParams in self.get_lookup_key():
- names.append(nne)
+ for s in symbols:
+ names.append(ASTNestedNameElement(s.identOrOp, s.templateArgs))
templates.append(False)
return ASTNestedName(names, templates, rooted=False)
@@ -3695,9 +3740,12 @@ class Symbol:
templateShorthand: bool, matchSelf: bool,
recurseInAnon: bool, correctPrimaryTemplateArgs: bool
) -> "Symbol":
+ if Symbol.debug_lookup:
+ Symbol.debug_print("_find_first_named_symbol ->")
res = self._find_named_symbols(identOrOp, templateParams, templateArgs,
templateShorthand, matchSelf, recurseInAnon,
- correctPrimaryTemplateArgs)
+ correctPrimaryTemplateArgs,
+ searchInSiblings=False)
try:
return next(res)
except StopIteration:
@@ -3706,8 +3754,22 @@ class Symbol:
def _find_named_symbols(self, identOrOp: Union[ASTIdentifier, ASTOperator],
templateParams: Any, templateArgs: ASTTemplateArgs,
templateShorthand: bool, matchSelf: bool,
- recurseInAnon: bool, correctPrimaryTemplateArgs: bool
- ) -> Iterator["Symbol"]:
+ recurseInAnon: bool, correctPrimaryTemplateArgs: bool,
+ searchInSiblings: bool) -> Iterator["Symbol"]:
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("_find_named_symbols:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("self:")
+ print(self.to_string(Symbol.debug_indent + 1), end="")
+ Symbol.debug_print("identOrOp: ", identOrOp)
+ Symbol.debug_print("templateParams: ", templateParams)
+ Symbol.debug_print("templateArgs: ", templateArgs)
+ Symbol.debug_print("templateShorthand: ", templateShorthand)
+ Symbol.debug_print("matchSelf: ", matchSelf)
+ Symbol.debug_print("recurseInAnon: ", recurseInAnon)
+ Symbol.debug_print("correctPrimaryTemplateAargs:", correctPrimaryTemplateArgs)
+ Symbol.debug_print("searchInSiblings: ", searchInSiblings)
def isSpecialization():
# the names of the template parameters must be given exactly as args
@@ -3759,20 +3821,64 @@ class Symbol:
if str(s.templateArgs) != str(templateArgs):
return False
return True
- if matchSelf and matches(self):
- yield self
- children = self.children_recurse_anon if recurseInAnon else self._children
- for s in children:
+
+ def candidates():
+ s = self
+ if Symbol.debug_lookup:
+ Symbol.debug_print("searching in self:")
+ print(s.to_string(Symbol.debug_indent + 1), end="")
+ while True:
+ if matchSelf:
+ yield s
+ if recurseInAnon:
+ yield from s.children_recurse_anon
+ else:
+ yield from s._children
+
+ if s.siblingAbove is None:
+ break
+ s = s.siblingAbove
+ if Symbol.debug_lookup:
+ Symbol.debug_print("searching in sibling:")
+ print(s.to_string(Symbol.debug_indent + 1), end="")
+
+ for s in candidates():
+ if Symbol.debug_lookup:
+ Symbol.debug_print("candidate:")
+ print(s.to_string(Symbol.debug_indent + 1), end="")
if matches(s):
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("matches")
+ Symbol.debug_indent -= 3
yield s
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 2
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
def _symbol_lookup(self, nestedName: ASTNestedName, templateDecls: List[Any],
onMissingQualifiedSymbol: Callable[["Symbol", Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs], "Symbol"], # NOQA
strictTemplateParamArgLists: bool, ancestorLookupType: str,
templateShorthand: bool, matchSelf: bool,
- recurseInAnon: bool, correctPrimaryTemplateArgs: bool
- ) -> SymbolLookupResult:
+ recurseInAnon: bool, correctPrimaryTemplateArgs: bool,
+ searchInSiblings: bool) -> SymbolLookupResult:
# ancestorLookupType: if not None, specifies the target type of the lookup
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("_symbol_lookup:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("self:")
+ print(self.to_string(Symbol.debug_indent + 1), end="")
+ Symbol.debug_print("nestedName: ", nestedName)
+ Symbol.debug_print("templateDecls: ", templateDecls)
+ Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists)
+ Symbol.debug_print("ancestorLookupType:", ancestorLookupType)
+ Symbol.debug_print("templateShorthand: ", templateShorthand)
+ Symbol.debug_print("matchSelf: ", matchSelf)
+ Symbol.debug_print("recurseInAnon: ", recurseInAnon)
+ Symbol.debug_print("correctPrimaryTemplateArgs: ", correctPrimaryTemplateArgs)
+ Symbol.debug_print("searchInSiblings: ", searchInSiblings)
if strictTemplateParamArgLists:
# Each template argument list must have a template parameter list.
@@ -3796,7 +3902,8 @@ class Symbol:
while parentSymbol.parent:
if parentSymbol.find_identifier(firstName.identOrOp,
matchSelf=matchSelf,
- recurseInAnon=recurseInAnon):
+ recurseInAnon=recurseInAnon,
+ searchInSiblings=searchInSiblings):
# if we are in the scope of a constructor but wants to
# reference the class we need to walk one extra up
if (len(names) == 1 and ancestorLookupType == 'class' and matchSelf and
@@ -3807,6 +3914,10 @@ class Symbol:
break
parentSymbol = parentSymbol.parent
+ if Symbol.debug_lookup:
+ Symbol.debug_print("starting point:")
+ print(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
+
# and now the actual lookup
iTemplateDecl = 0
for name in names[:-1]:
@@ -3840,6 +3951,8 @@ class Symbol:
symbol = onMissingQualifiedSymbol(parentSymbol, identOrOp,
templateParams, templateArgs)
if symbol is None:
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
return None
# We have now matched part of a nested name, and need to match more
# so even if we should matchSelf before, we definitely shouldn't
@@ -3847,6 +3960,10 @@ class Symbol:
matchSelf = False
parentSymbol = symbol
+ if Symbol.debug_lookup:
+ Symbol.debug_print("handle last name from:")
+ print(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
+
# handle the last name
name = names[-1]
identOrOp = name.identOrOp
@@ -3861,7 +3978,11 @@ class Symbol:
symbols = parentSymbol._find_named_symbols(
identOrOp, templateParams, templateArgs,
templateShorthand=templateShorthand, matchSelf=matchSelf,
- recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False)
+ recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False,
+ searchInSiblings=searchInSiblings)
+ if Symbol.debug_lookup:
+ symbols = list(symbols) # type: ignore
+ Symbol.debug_indent -= 2
return SymbolLookupResult(symbols, parentSymbol,
identOrOp, templateParams, templateArgs)
@@ -3871,21 +3992,26 @@ class Symbol:
# be an actual declaration.
if Symbol.debug_lookup:
- print("_add_symbols:")
- print(" tdecls:", templateDecls)
- print(" nn: ", nestedName)
- print(" decl: ", declaration)
- print(" doc: ", docname)
+ Symbol.debug_indent += 1
+ Symbol.debug_print("_add_symbols:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("tdecls:", templateDecls)
+ Symbol.debug_print("nn: ", nestedName)
+ Symbol.debug_print("decl: ", declaration)
+ Symbol.debug_print("doc: ", docname)
def onMissingQualifiedSymbol(parentSymbol: "Symbol",
identOrOp: Union[ASTIdentifier, ASTOperator],
templateParams: Any, templateArgs: ASTTemplateArgs
) -> "Symbol":
if Symbol.debug_lookup:
- print(" _add_symbols, onMissingQualifiedSymbol:")
- print(" templateParams:", templateParams)
- print(" identOrOp: ", identOrOp)
- print(" templateARgs: ", templateArgs)
+ Symbol.debug_indent += 1
+ Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("templateParams:", templateParams)
+ Symbol.debug_print("identOrOp: ", identOrOp)
+ Symbol.debug_print("templateARgs: ", templateArgs)
+ Symbol.debug_indent -= 2
return Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams,
templateArgs=templateArgs, declaration=None,
@@ -3898,32 +4024,40 @@ class Symbol:
templateShorthand=False,
matchSelf=False,
recurseInAnon=True,
- correctPrimaryTemplateArgs=True)
+ correctPrimaryTemplateArgs=True,
+ searchInSiblings=False)
assert lookupResult is not None # we create symbols all the way, so that can't happen
symbols = list(lookupResult.symbols)
if len(symbols) == 0:
if Symbol.debug_lookup:
- print(" _add_symbols, result, no symbol:")
- print(" templateParams:", lookupResult.templateParams)
- print(" identOrOp: ", lookupResult.identOrOp)
- print(" templateArgs: ", lookupResult.templateArgs)
- print(" declaration: ", declaration)
- print(" docname: ", docname)
+ Symbol.debug_print("_add_symbols, result, no symbol:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("templateParams:", lookupResult.templateParams)
+ Symbol.debug_print("identOrOp: ", lookupResult.identOrOp)
+ Symbol.debug_print("templateArgs: ", lookupResult.templateArgs)
+ Symbol.debug_print("declaration: ", declaration)
+ Symbol.debug_print("docname: ", docname)
+ Symbol.debug_indent -= 1
symbol = Symbol(parent=lookupResult.parentSymbol,
identOrOp=lookupResult.identOrOp,
templateParams=lookupResult.templateParams,
templateArgs=lookupResult.templateArgs,
declaration=declaration,
docname=docname)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
return symbol
if Symbol.debug_lookup:
- print(" _add_symbols, result, symbols:")
- print(" number symbols:", len(symbols))
+ Symbol.debug_print("_add_symbols, result, symbols:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("number symbols:", len(symbols))
+ Symbol.debug_indent -= 1
if not declaration:
if Symbol.debug_lookup:
- print(" no delcaration")
+ Symbol.debug_print("no delcaration")
+ Symbol.debug_indent -= 2
# good, just a scope creation
# TODO: what if we have more than one symbol?
return symbols[0]
@@ -3939,9 +4073,9 @@ class Symbol:
else:
withDecl.append(s)
if Symbol.debug_lookup:
- print(" #noDecl: ", len(noDecl))
- print(" #withDecl:", len(withDecl))
- print(" #dupDecl: ", len(dupDecl))
+ Symbol.debug_print("#noDecl: ", len(noDecl))
+ Symbol.debug_print("#withDecl:", len(withDecl))
+ Symbol.debug_print("#dupDecl: ", len(dupDecl))
# With partial builds we may start with a large symbol tree stripped of declarations.
# Essentially any combination of noDecl, withDecl, and dupDecls seems possible.
# TODO: make partial builds fully work. What should happen when the primary symbol gets
@@ -3952,7 +4086,7 @@ class Symbol:
# otherwise there should be only one symbol with a declaration.
def makeCandSymbol():
if Symbol.debug_lookup:
- print(" begin: creating candidate symbol")
+ Symbol.debug_print("begin: creating candidate symbol")
symbol = Symbol(parent=lookupResult.parentSymbol,
identOrOp=lookupResult.identOrOp,
templateParams=lookupResult.templateParams,
@@ -3960,7 +4094,7 @@ class Symbol:
declaration=declaration,
docname=docname)
if Symbol.debug_lookup:
- print(" end: creating candidate symbol")
+ Symbol.debug_print("end: creating candidate symbol")
return symbol
if len(withDecl) == 0:
candSymbol = None
@@ -3969,7 +4103,10 @@ class Symbol:
def handleDuplicateDeclaration(symbol, candSymbol):
if Symbol.debug_lookup:
- print(" redeclaration")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("redeclaration")
+ Symbol.debug_indent -= 1
+ Symbol.debug_indent -= 2
# Redeclaration of the same symbol.
# Let the new one be there, but raise an error to the client
# so it can use the real symbol as subscope.
@@ -3985,11 +4122,11 @@ class Symbol:
# a function, so compare IDs
candId = declaration.get_newest_id()
if Symbol.debug_lookup:
- print(" candId:", candId)
+ Symbol.debug_print("candId:", candId)
for symbol in withDecl:
oldId = symbol.declaration.get_newest_id()
if Symbol.debug_lookup:
- print(" oldId: ", oldId)
+ Symbol.debug_print("oldId: ", oldId)
if candId == oldId:
handleDuplicateDeclaration(symbol, candSymbol)
# (not reachable)
@@ -3997,14 +4134,16 @@ class Symbol:
# if there is an empty symbol, fill that one
if len(noDecl) == 0:
if Symbol.debug_lookup:
- print(" no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA
+ Symbol.debug_print("no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA
+ Symbol.debug_indent -= 2
if candSymbol is not None:
return candSymbol
else:
return makeCandSymbol()
else:
if Symbol.debug_lookup:
- print(" no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None) # NOQA
+ Symbol.debug_print("no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None) # NOQA
+ Symbol.debug_indent -= 2
if candSymbol is not None:
candSymbol.remove()
# assert len(noDecl) == 1
@@ -4021,6 +4160,9 @@ class Symbol:
def merge_with(self, other: "Symbol", docnames: List[str],
env: "BuildEnvironment") -> None:
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("merge_with:")
assert other is not None
for otherChild in other._children:
ourChild = self._find_first_named_symbol(
@@ -4050,17 +4192,28 @@ class Symbol:
# just ignore it, right?
pass
ourChild.merge_with(otherChild, docnames, env)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 1
def add_name(self, nestedName: ASTNestedName,
templatePrefix: ASTTemplateDeclarationPrefix = None) -> "Symbol":
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("add_name:")
if templatePrefix:
templateDecls = templatePrefix.templates
else:
templateDecls = []
- return self._add_symbols(nestedName, templateDecls,
- declaration=None, docname=None)
+ res = self._add_symbols(nestedName, templateDecls,
+ declaration=None, docname=None)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 1
+ return res
def add_declaration(self, declaration: ASTDeclaration, docname: str) -> "Symbol":
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("add_declaration:")
assert declaration
assert docname
nestedName = declaration.name
@@ -4068,37 +4221,105 @@ class Symbol:
templateDecls = declaration.templatePrefix.templates
else:
templateDecls = []
- return self._add_symbols(nestedName, templateDecls, declaration, docname)
+ res = self._add_symbols(nestedName, templateDecls, declaration, docname)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 1
+ return res
def find_identifier(self, identOrOp: Union[ASTIdentifier, ASTOperator],
- matchSelf: bool, recurseInAnon: bool) -> "Symbol":
- if matchSelf and self.identOrOp == identOrOp:
- return self
- children = self.children_recurse_anon if recurseInAnon else self._children
- for s in children:
- if s.identOrOp == identOrOp:
- return s
+ matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool
+ ) -> "Symbol":
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("find_identifier:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("identOrOp: ", identOrOp)
+ Symbol.debug_print("matchSelf: ", matchSelf)
+ Symbol.debug_print("recurseInAnon: ", recurseInAnon)
+ Symbol.debug_print("searchInSiblings:", searchInSiblings)
+ print(self.to_string(Symbol.debug_indent + 1), end="")
+ Symbol.debug_indent -= 2
+ current = self
+ while current is not None:
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 2
+ Symbol.debug_print("trying:")
+ print(current.to_string(Symbol.debug_indent + 1), end="")
+ Symbol.debug_indent -= 2
+ if matchSelf and current.identOrOp == identOrOp:
+ return current
+ children = current.children_recurse_anon if recurseInAnon else current._children
+ for s in children:
+ if s.identOrOp == identOrOp:
+ return s
+ if not searchInSiblings:
+ break
+ current = current.siblingAbove
return None
- def direct_lookup(self, key: List[Tuple[ASTNestedNameElement, Any]]) -> "Symbol":
+ def direct_lookup(self, key: "LookupKey") -> "Symbol":
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("direct_lookup:")
+ Symbol.debug_indent += 1
s = self
- for name, templateParams in key:
- identOrOp = name.identOrOp
- templateArgs = name.templateArgs
- s = s._find_first_named_symbol(identOrOp,
- templateParams, templateArgs,
- templateShorthand=False,
- matchSelf=False,
- recurseInAnon=False,
- correctPrimaryTemplateArgs=False)
- if not s:
+ for name, templateParams, id_ in key.data:
+ if id_ is not None:
+ res = None
+ for cand in s._children:
+ if cand.declaration is None:
+ continue
+ if cand.declaration.get_newest_id() == id_:
+ res = cand
+ break
+ s = res
+ else:
+ identOrOp = name.identOrOp
+ templateArgs = name.templateArgs
+ s = s._find_first_named_symbol(identOrOp,
+ templateParams, templateArgs,
+ templateShorthand=False,
+ matchSelf=False,
+ recurseInAnon=False,
+ correctPrimaryTemplateArgs=False)
+ if Symbol.debug_lookup:
+ Symbol.debug_print("name: ", name)
+ Symbol.debug_print("templateParams:", templateParams)
+ Symbol.debug_print("id: ", id_)
+ if s is not None:
+ print(s.to_string(Symbol.debug_indent + 1), end="")
+ else:
+ Symbol.debug_print("not found")
+ if s is None:
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
return None
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
return s
def find_name(self, nestedName: ASTNestedName, templateDecls: List[Any],
typ: str, templateShorthand: bool, matchSelf: bool,
- recurseInAnon: bool) -> List["Symbol"]:
+ recurseInAnon: bool, searchInSiblings: bool) -> Tuple[List["Symbol"], str]:
# templateShorthand: missing template parameter lists for templates is ok
+ # If the first component is None,
+ # then the second component _may_ be a string explaining why.
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("find_name:")
+ Symbol.debug_indent += 1
+ Symbol.debug_print("self:")
+ print(self.to_string(Symbol.debug_indent + 1), end="")
+ Symbol.debug_print("nestedName: ", nestedName)
+ Symbol.debug_print("templateDecls: ", templateDecls)
+ Symbol.debug_print("typ: ", typ)
+ Symbol.debug_print("templateShorthand:", templateShorthand)
+ Symbol.debug_print("matchSelf: ", matchSelf)
+ Symbol.debug_print("recurseInAnon: ", recurseInAnon)
+ Symbol.debug_print("searchInSiblings: ", searchInSiblings)
+
+ class QualifiedSymbolIsTemplateParam(Exception):
+ pass
def onMissingQualifiedSymbol(parentSymbol: "Symbol",
identOrOp: Union[ASTIdentifier, ASTOperator],
@@ -4108,37 +4329,58 @@ class Symbol:
# Though, the correctPrimaryTemplateArgs does
# that for primary templates.
# Is there another case where it would be good?
+ if parentSymbol.declaration is not None:
+ if parentSymbol.declaration.objectType == 'templateParam':
+ raise QualifiedSymbolIsTemplateParam()
return None
- lookupResult = self._symbol_lookup(nestedName, templateDecls,
- onMissingQualifiedSymbol,
- strictTemplateParamArgLists=False,
- ancestorLookupType=typ,
- templateShorthand=templateShorthand,
- matchSelf=matchSelf,
- recurseInAnon=recurseInAnon,
- correctPrimaryTemplateArgs=False)
+ try:
+ lookupResult = self._symbol_lookup(nestedName, templateDecls,
+ onMissingQualifiedSymbol,
+ strictTemplateParamArgLists=False,
+ ancestorLookupType=typ,
+ templateShorthand=templateShorthand,
+ matchSelf=matchSelf,
+ recurseInAnon=recurseInAnon,
+ correctPrimaryTemplateArgs=False,
+ searchInSiblings=searchInSiblings)
+ except QualifiedSymbolIsTemplateParam:
+ return None, "templateParamInQualified"
+
if lookupResult is None:
# if it was a part of the qualification that could not be found
- return None
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
+ return None, None
res = list(lookupResult.symbols)
if len(res) != 0:
- return res
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
+ return res, None
+
+ if lookupResult.parentSymbol.declaration is not None:
+ if lookupResult.parentSymbol.declaration.objectType == 'templateParam':
+ return None, "templateParamInQualified"
# try without template params and args
symbol = lookupResult.parentSymbol._find_first_named_symbol(
lookupResult.identOrOp, None, None,
templateShorthand=templateShorthand, matchSelf=matchSelf,
recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 2
if symbol is not None:
- return [symbol]
+ return [symbol], None
else:
- return None
+ return None, None
def find_declaration(self, declaration: ASTDeclaration, typ: str, templateShorthand: bool,
matchSelf: bool, recurseInAnon: bool) -> "Symbol":
# templateShorthand: missing template parameter lists for templates is ok
+ if Symbol.debug_lookup:
+ Symbol.debug_indent += 1
+ Symbol.debug_print("find_declaration:")
nestedName = declaration.name
if declaration.templatePrefix:
templateDecls = declaration.templatePrefix.templates
@@ -4158,8 +4400,10 @@ class Symbol:
templateShorthand=templateShorthand,
matchSelf=matchSelf,
recurseInAnon=recurseInAnon,
- correctPrimaryTemplateArgs=False)
-
+ correctPrimaryTemplateArgs=False,
+ searchInSiblings=False)
+ if Symbol.debug_lookup:
+ Symbol.debug_indent -= 1
if lookupResult is None:
return None
@@ -4185,14 +4429,14 @@ class Symbol:
return None
def to_string(self, indent: int) -> str:
- res = ['\t' * indent]
+ res = [Symbol.debug_indent_string * indent]
if not self.parent:
res.append('::')
else:
if self.templateParams:
res.append(str(self.templateParams))
res.append('\n')
- res.append('\t' * indent)
+ res.append(Symbol.debug_indent_string * indent)
if self.identOrOp:
res.append(str(self.identOrOp))
else:
@@ -5931,14 +6175,15 @@ class DefinitionParser:
def _parse_template_declaration_prefix(self, objectType: str
) -> ASTTemplateDeclarationPrefix:
- templates = [] # type: List[str]
+ templates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
while 1:
self.skip_ws()
# the saved position is only used to provide a better error message
+ params = None # type: Union[ASTTemplateParams, ASTTemplateIntroduction]
pos = self.pos
if self.skip_word("template"):
try:
- params = self._parse_template_parameter_list() # type: Any
+ params = self._parse_template_parameter_list()
except DefinitionError as e:
if objectType == 'member' and len(templates) == 0:
return ASTTemplateDeclarationPrefix(None)
@@ -5990,7 +6235,7 @@ class DefinitionParser:
msg += str(nestedName)
self.warn(msg)
- newTemplates = []
+ newTemplates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
for i in range(numExtra):
newTemplates.append(ASTTemplateParams([]))
if templatePrefix and not isMemberInstantiation:
@@ -6176,7 +6421,8 @@ class CPPObject(ObjectDescription):
return
targetSymbol = parentSymbol.parent
- s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True)
+ s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True,
+ searchInSiblings=False)
if s is not None:
# something is already declared with that name
return
@@ -6244,7 +6490,6 @@ class CPPObject(ObjectDescription):
continue
if id not in self.state.document.ids:
signode['ids'].append(id)
- signode['first'] = (not self.names) # hmm, what is this about?
self.state.document.note_explicit_target(signode)
@property
@@ -6291,6 +6536,10 @@ class CPPObject(ObjectDescription):
symbol = parentSymbol.add_name(name)
env.temp_data['cpp:last_symbol'] = symbol
return []
+ # When multiple declarations are made in the same directive
+ # they need to know about each other to provide symbol lookup for function parameters.
+ # We use last_symbol to store the latest added declaration in a directive.
+ env.temp_data['cpp:last_symbol'] = None
return super().run()
def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration:
@@ -6311,6 +6560,13 @@ class CPPObject(ObjectDescription):
try:
symbol = parentSymbol.add_declaration(ast, docname=self.env.docname)
+ # append the new declaration to the sibling list
+ assert symbol.siblingAbove is None
+ assert symbol.siblingBelow is None
+ symbol.siblingAbove = self.env.temp_data['cpp:last_symbol']
+ if symbol.siblingAbove is not None:
+ assert symbol.siblingAbove.siblingBelow is None
+ symbol.siblingAbove.siblingBelow = symbol
self.env.temp_data['cpp:last_symbol'] = symbol
except _DuplicateSymbolError as e:
# Assume we are actually in the old symbol,
@@ -6329,10 +6585,10 @@ class CPPObject(ObjectDescription):
return ast
def before_content(self) -> None:
- lastSymbol = self.env.temp_data['cpp:last_symbol']
+ lastSymbol = self.env.temp_data['cpp:last_symbol'] # type: Symbol
assert lastSymbol
self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol']
- self.oldParentKey = self.env.ref_context['cpp:parent_key']
+ self.oldParentKey = self.env.ref_context['cpp:parent_key'] # type: LookupKey
self.env.temp_data['cpp:parent_symbol'] = lastSymbol
self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key()
@@ -6515,14 +6771,13 @@ class AliasTransform(SphinxTransform):
if ast is None:
# could not be parsed, so stop here
signode = addnodes.desc_signature(sig, '')
- signode['first'] = False
signode.clear()
signode += addnodes.desc_name(sig, sig)
node.replace_self(signode)
continue
- rootSymbol = self.env.domains['cpp'].data['root_symbol']
- parentSymbol = rootSymbol.direct_lookup(parentKey)
+ rootSymbol = self.env.domains['cpp'].data['root_symbol'] # type: Symbol
+ parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
if not parentSymbol:
print("Target: ", sig)
print("ParentKey: ", parentKey)
@@ -6537,9 +6792,13 @@ class AliasTransform(SphinxTransform):
templateDecls = ns.templatePrefix.templates
else:
templateDecls = []
- symbols = parentSymbol.find_name(name, templateDecls, 'any',
- templateShorthand=True,
- matchSelf=True, recurseInAnon=True)
+ symbols, failReason = parentSymbol.find_name(
+ nestedName=name,
+ templateDecls=templateDecls,
+ typ='any',
+ templateShorthand=True,
+ matchSelf=True, recurseInAnon=True,
+ searchInSiblings=False)
if symbols is None:
symbols = []
else:
@@ -6555,7 +6814,6 @@ class AliasTransform(SphinxTransform):
if len(symbols) == 0:
signode = addnodes.desc_signature(sig, '')
- signode['first'] = False
node.append(signode)
signode.clear()
signode += addnodes.desc_name(sig, sig)
@@ -6569,7 +6827,6 @@ class AliasTransform(SphinxTransform):
options['tparam-line-spec'] = False
for s in symbols:
signode = addnodes.desc_signature(sig, '')
- signode['first'] = False
nodes.append(signode)
s.declaration.describe_signature(signode, 'markName', self.env, options)
node.replace_self(nodes)
@@ -6824,13 +7081,13 @@ class CPPDomain(Domain):
t, ex = findWarning(e)
warner.warn('Unparseable C++ cross-reference: %r\n%s' % (t, ex))
return None, None
- parentKey = node.get("cpp:parent_key", None)
+ parentKey = node.get("cpp:parent_key", None) # type: LookupKey
rootSymbol = self.data['root_symbol']
if parentKey:
- parentSymbol = rootSymbol.direct_lookup(parentKey)
+ parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
if not parentSymbol:
print("Target: ", target)
- print("ParentKey: ", parentKey)
+ print("ParentKey: ", parentKey.data)
print(rootSymbol.dump(1))
assert parentSymbol # should be there
else:
@@ -6843,11 +7100,23 @@ class CPPDomain(Domain):
templateDecls = ns.templatePrefix.templates
else:
templateDecls = []
- symbols = parentSymbol.find_name(name, templateDecls, typ,
- templateShorthand=True,
- matchSelf=True, recurseInAnon=True)
- # just refer to the arbitrarily first symbol
- s = None if symbols is None else symbols[0]
+ # let's be conservative with the sibling lookup for now
+ searchInSiblings = (not name.rooted) and len(name.names) == 1
+ symbols, failReason = parentSymbol.find_name(
+ name, templateDecls, typ,
+ templateShorthand=True,
+ matchSelf=True, recurseInAnon=True,
+ searchInSiblings=searchInSiblings)
+ if symbols is None:
+ if typ == 'identifier':
+ if failReason == 'templateParamInQualified':
+ # this is an xref we created as part of a signature,
+ # so don't warn for names nested in template parameters
+ raise NoUri(str(name), typ)
+ s = None
+ else:
+ # just refer to the arbitrarily first symbol
+ s = symbols[0]
else:
decl = ast # type: ASTDeclaration
name = decl.name
@@ -6977,8 +7246,8 @@ class CPPDomain(Domain):
target = node.get('reftarget', None)
if target is None:
return None
- parentKey = node.get("cpp:parent_key", None)
- if parentKey is None or len(parentKey) <= 0:
+ parentKey = node.get("cpp:parent_key", None) # type: LookupKey
+ if parentKey is None or len(parentKey.data) <= 0:
return None
rootSymbol = self.data['root_symbol']
@@ -6996,7 +7265,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
- 'env_version': 1,
+ 'env_version': 2,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/domains/index.py b/sphinx/domains/index.py
index d0bdbcfe0..18a256bac 100644
--- a/sphinx/domains/index.py
+++ b/sphinx/domains/index.py
@@ -12,6 +12,7 @@ from typing import Any, Dict, Iterable, List, Tuple
from docutils import nodes
from docutils.nodes import Node, system_message
+from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.domains import Domain
@@ -68,19 +69,27 @@ class IndexDirective(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
- option_spec = {} # type: Dict
+ option_spec = {
+ 'name': directives.unchanged,
+ }
def run(self) -> List[Node]:
arguments = self.arguments[0].split('\n')
- targetid = 'index-%s' % self.env.new_serialno('index')
- targetnode = nodes.target('', '', ids=[targetid])
+
+ if 'name' in self.options:
+ targetname = self.options['name']
+ targetnode = nodes.target('', '', names=[targetname])
+ else:
+ targetid = 'index-%s' % self.env.new_serialno('index')
+ targetnode = nodes.target('', '', ids=[targetid])
+
self.state.document.note_explicit_target(targetnode)
indexnode = addnodes.index()
indexnode['entries'] = []
indexnode['inline'] = False
self.set_source_info(indexnode)
for entry in arguments:
- indexnode['entries'].extend(process_index_entry(entry, targetid))
+ indexnode['entries'].extend(process_index_entry(entry, targetnode['ids'][0]))
return [indexnode, targetnode]
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index feb39bc9d..d7e44ee60 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -28,12 +28,30 @@ from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
-from sphinx.util.nodes import make_refnode
+from sphinx.util.nodes import make_id, make_refnode
logger = logging.getLogger(__name__)
+def make_old_jsmod_id(modname: str) -> str:
+ """Generate old styled node_id for JS modules.
+
+ .. note:: Old Styled node_id was used until Sphinx-3.0.
+ This will be removed in Sphinx-5.0.
+ """
+ return 'module-' + modname
+
+
+def make_old_jsobj_id(fullname: str) -> str:
+ """Generate old styled node_id for JS objects.
+
+ .. note:: Old Styled node_id was used until Sphinx-3.0.
+ This will be removed in Sphinx-5.0.
+ """
+ return fullname.replace('$', '_S_')
+
+
class JSObject(ObjectDescription):
"""
Description of a JavaScript object.
@@ -106,21 +124,23 @@ class JSObject(ObjectDescription):
signode: desc_signature) -> None:
mod_name = self.env.ref_context.get('js:module')
fullname = (mod_name + '.' if mod_name else '') + name_obj[0]
- if fullname not in self.state.document.ids:
- signode['names'].append(fullname)
- signode['ids'].append(fullname.replace('$', '_S_'))
- signode['first'] = not self.names
- self.state.document.note_explicit_target(signode)
+ node_id = make_id(self.env, self.state.document, '', fullname)
+ signode['ids'].append(node_id)
- domain = cast(JavaScriptDomain, self.env.get_domain('js'))
- domain.note_object(fullname, self.objtype,
- location=(self.env.docname, self.lineno))
+ # Assign old styled node_id not to break old hyperlinks (if possible)
+ # Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
+ old_node_id = make_old_jsobj_id(fullname)
+ if old_node_id not in self.state.document.ids and old_node_id not in signode['ids']:
+ signode['ids'].append(old_node_id)
+
+ self.state.document.note_explicit_target(signode)
+
+ domain = cast(JavaScriptDomain, self.env.get_domain('js'))
+ domain.note_object(fullname, self.objtype, node_id, location=signode)
indextext = self.get_index_text(mod_name, name_obj)
if indextext:
- self.indexnode['entries'].append(('single', indextext,
- fullname.replace('$', '_S_'),
- '', None))
+ self.indexnode['entries'].append(('single', indextext, node_id, '', None))
def get_index_text(self, objectname: str, name_obj: Tuple[str, str]) -> str:
name, obj = name_obj
@@ -251,18 +271,25 @@ class JSModule(SphinxDirective):
if not noindex:
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
- domain.note_module(mod_name)
+ node_id = make_id(self.env, self.state.document, 'module', mod_name)
+ domain.note_module(mod_name, node_id)
# Make a duplicate entry in 'objects' to facilitate searching for
# the module in JavaScriptDomain.find_obj()
- domain.note_object(mod_name, 'module', location=(self.env.docname, self.lineno))
+ domain.note_object(mod_name, 'module', node_id,
+ location=(self.env.docname, self.lineno))
+
+ target = nodes.target('', '', ids=[node_id], ismod=True)
+
+ # Assign old styled node_id not to break old hyperlinks (if possible)
+ # Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
+ old_node_id = make_old_jsmod_id(mod_name)
+ if old_node_id not in self.state.document.ids and old_node_id not in target['ids']:
+ target['ids'].append(old_node_id)
- targetnode = nodes.target('', '', ids=['module-' + mod_name],
- ismod=True)
- self.state.document.note_explicit_target(targetnode)
- ret.append(targetnode)
+ self.state.document.note_explicit_target(target)
+ ret.append(target)
indextext = _('%s (module)') % mod_name
- inode = addnodes.index(entries=[('single', indextext,
- 'module-' + mod_name, '', None)])
+ inode = addnodes.index(entries=[('single', indextext, node_id, '', None)])
ret.append(inode)
return ret
@@ -317,47 +344,48 @@ class JavaScriptDomain(Domain):
'mod': JSXRefRole(),
}
initial_data = {
- 'objects': {}, # fullname -> docname, objtype
- 'modules': {}, # modname -> docname
+ 'objects': {}, # fullname -> docname, node_id, objtype
+ 'modules': {}, # modname -> docname, node_id
} # type: Dict[str, Dict[str, Tuple[str, str]]]
@property
- def objects(self) -> Dict[str, Tuple[str, str]]:
- return self.data.setdefault('objects', {}) # fullname -> docname, objtype
+ def objects(self) -> Dict[str, Tuple[str, str, str]]:
+ return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype
- def note_object(self, fullname: str, objtype: str, location: Any = None) -> None:
+ def note_object(self, fullname: str, objtype: str, node_id: str,
+ location: Any = None) -> None:
if fullname in self.objects:
docname = self.objects[fullname][0]
- logger.warning(__('duplicate object description of %s, other instance in %s'),
- fullname, docname, location=location)
- self.objects[fullname] = (self.env.docname, objtype)
+ logger.warning(__('duplicate %s description of %s, other %s in %s'),
+ objtype, fullname, objtype, docname, location=location)
+ self.objects[fullname] = (self.env.docname, node_id, objtype)
@property
- def modules(self) -> Dict[str, str]:
- return self.data.setdefault('modules', {}) # modname -> docname
+ def modules(self) -> Dict[str, Tuple[str, str]]:
+ return self.data.setdefault('modules', {}) # modname -> docname, node_id
- def note_module(self, modname: str) -> None:
- self.modules[modname] = self.env.docname
+ def note_module(self, modname: str, node_id: str) -> None:
+ self.modules[modname] = (self.env.docname, node_id)
def clear_doc(self, docname: str) -> None:
- for fullname, (pkg_docname, _l) in list(self.objects.items()):
+ for fullname, (pkg_docname, node_id, _l) in list(self.objects.items()):
if pkg_docname == docname:
del self.objects[fullname]
- for modname, pkg_docname in list(self.modules.items()):
+ for modname, (pkg_docname, node_id) in list(self.modules.items()):
if pkg_docname == docname:
del self.modules[modname]
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates
- for fullname, (fn, objtype) in otherdata['objects'].items():
+ for fullname, (fn, node_id, objtype) in otherdata['objects'].items():
if fn in docnames:
- self.objects[fullname] = (fn, objtype)
- for mod_name, pkg_docname in otherdata['modules'].items():
+ self.objects[fullname] = (fn, node_id, objtype)
+ for mod_name, (pkg_docname, node_id) in otherdata['modules'].items():
if pkg_docname in docnames:
- self.modules[mod_name] = pkg_docname
+ self.modules[mod_name] = (pkg_docname, node_id)
def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str,
- typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str]]:
+ typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str, str]]:
if name[-2:] == '()':
name = name[:-2]
@@ -389,8 +417,7 @@ class JavaScriptDomain(Domain):
name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder)
if not obj:
return None
- return make_refnode(builder, fromdocname, obj[0],
- name.replace('$', '_S_'), contnode, name)
+ return make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name)
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element
@@ -400,13 +427,12 @@ class JavaScriptDomain(Domain):
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
if not obj:
return []
- return [('js:' + self.role_for_objtype(obj[1]),
- make_refnode(builder, fromdocname, obj[0],
- name.replace('$', '_S_'), contnode, name))]
+ return [('js:' + self.role_for_objtype(obj[2]),
+ make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name))]
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
- for refname, (docname, type) in list(self.objects.items()):
- yield refname, refname, type, docname, refname.replace('$', '_S_'), 1
+ for refname, (docname, node_id, typ) in list(self.objects.items()):
+ yield refname, refname, typ, docname, node_id, 1
def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('js:module')
@@ -423,7 +449,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
- 'env_version': 1,
+ 'env_version': 2,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py
index b07b2603b..88b6e4eb8 100644
--- a/sphinx/domains/math.py
+++ b/sphinx/domains/math.py
@@ -15,7 +15,6 @@ from docutils import nodes
from docutils.nodes import Element, Node, system_message
from docutils.nodes import make_id
-from sphinx.addnodes import math_block as displaymath
from sphinx.addnodes import pending_xref
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
@@ -54,7 +53,6 @@ class MathDomain(Domain):
'eq': 'equation not found: %(target)s',
}
enumerable_nodes = { # node_class -> (figtype, title_getter)
- displaymath: ('displaymath', None),
nodes.math_block: ('displaymath', None),
}
roles = {
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 3a49d6745..c9c8d5704 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -10,6 +10,7 @@
import re
import warnings
+from inspect import Parameter
from typing import Any, Dict, Iterable, Iterator, List, Tuple
from typing import cast
@@ -17,13 +18,11 @@ from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers.rst import directives
-from sphinx import addnodes, locale
+from sphinx import addnodes
from sphinx.addnodes import pending_xref, desc_signature
from sphinx.application import Sphinx
from sphinx.builders import Builder
-from sphinx.deprecation import (
- DeprecatedDict, RemovedInSphinx30Warning, RemovedInSphinx40Warning
-)
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType, Index, IndexEntry
from sphinx.environment import BuildEnvironment
@@ -32,6 +31,7 @@ from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
+from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import make_refnode
from sphinx.util.typing import TextlikeNode
@@ -63,12 +63,46 @@ pairindextypes = {
'builtin': _('built-in function'),
}
-locale.pairindextypes = DeprecatedDict(
- pairindextypes,
- 'sphinx.locale.pairindextypes is deprecated. '
- 'Please use sphinx.domains.python.pairindextypes instead.',
- RemovedInSphinx30Warning
-)
+
+def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
+ """Parse a list of arguments using AST parser"""
+ params = addnodes.desc_parameterlist(arglist)
+ sig = signature_from_str('(%s)' % arglist)
+ last_kind = None
+ for param in sig.parameters.values():
+ if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
+ # PEP-570: Separator for Positional Only Parameter: /
+ params += addnodes.desc_parameter('', nodes.Text('/'))
+ if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
+ param.POSITIONAL_ONLY,
+ None):
+ # PEP-3102: Separator for Keyword Only Parameter: *
+ params += addnodes.desc_parameter('', nodes.Text('*'))
+
+ node = addnodes.desc_parameter()
+ if param.kind == param.VAR_POSITIONAL:
+ node += nodes.Text('*' + param.name)
+ elif param.kind == param.VAR_KEYWORD:
+ node += nodes.Text('**' + param.name)
+ else:
+ node += nodes.Text(param.name)
+
+ if param.annotation is not param.empty:
+ node += nodes.Text(': ' + param.annotation)
+ if param.default is not param.empty:
+ if param.annotation is not param.empty:
+ node += nodes.Text(' = ' + str(param.default))
+ else:
+ node += nodes.Text('=' + str(param.default))
+
+ params += node
+ last_kind = param.kind
+
+ if last_kind == Parameter.POSITIONAL_ONLY:
+ # PEP-570: Separator for Positional Only Parameter: /
+ params += addnodes.desc_parameter('', nodes.Text('/'))
+
+ return params
def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
@@ -293,7 +327,15 @@ class PyObject(ObjectDescription):
signode += addnodes.desc_name(name, name)
if arglist:
- _pseudo_parse_arglist(signode, arglist)
+ try:
+ signode += _parse_arglist(arglist)
+ except SyntaxError:
+ # fallback to parse arglist original parser.
+ # it supports to represent optional arguments (ex. "func(foo [, bar])")
+ _pseudo_parse_arglist(signode, arglist)
+ except NotImplementedError as exc:
+ logger.warning(exc)
+ _pseudo_parse_arglist(signode, arglist)
else:
if self.needs_arglist():
# for callables, add an empty parameter list
@@ -320,12 +362,10 @@ class PyObject(ObjectDescription):
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
- signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
domain = cast(PythonDomain, self.env.get_domain('py'))
- domain.note_object(fullname, self.objtype,
- location=(self.env.docname, self.lineno))
+ domain.note_object(fullname, self.objtype, location=signode)
indextext = self.get_index_text(modname, name_cls)
if indextext:
@@ -811,6 +851,21 @@ class PyXRefRole(XRefRole):
return title, target
+def filter_meta_fields(app: Sphinx, domain: str, objtype: str, content: Element) -> None:
+ """Filter ``:meta:`` field from its docstring."""
+ if domain != 'py':
+ return
+
+ for node in content:
+ if isinstance(node, nodes.field_list):
+ fields = cast(List[nodes.field], node)
+ for field in fields:
+ field_name = cast(nodes.field_body, field[0]).astext().strip()
+ if field_name == 'meta' or field_name.startswith('meta '):
+ node.remove(field)
+ break
+
+
class PythonModuleIndex(Index):
"""
Index subclass to provide the Python module index.
@@ -1119,7 +1174,10 @@ class PythonDomain(Domain):
def setup(app: Sphinx) -> Dict[str, Any]:
+ app.setup_extension('sphinx.directives')
+
app.add_domain(PythonDomain)
+ app.connect('object-description-transform', filter_meta_fields)
return {
'version': 'builtin',
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 81287c815..c0117d89f 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -43,11 +43,10 @@ class ReSTMarkup(ObjectDescription):
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
signode['ids'].append(targetname)
- signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
domain = cast(ReSTDomain, self.env.get_domain('rst'))
- domain.note_object(self.objtype, name, location=(self.env.docname, self.lineno))
+ domain.note_object(self.objtype, name, location=signode)
indextext = self.get_index_text(self.objtype, name)
if indextext:
@@ -133,12 +132,11 @@ class ReSTDirectiveOption(ReSTMarkup):
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
signode['ids'].append(targetname)
- signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
objname = ':'.join(filter(None, [directive_name, name]))
domain = cast(ReSTDomain, self.env.get_domain('rst'))
- domain.note_object(self.objtype, objname, location=(self.env.docname, self.lineno))
+ domain.note_object(self.objtype, objname, location=signode)
if directive_name:
key = name[0].upper()
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 4e46c1551..abf3be716 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -22,10 +22,9 @@ from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
-from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import ws_re, logging, docname_join
@@ -82,7 +81,7 @@ class GenericObject(ObjectDescription):
targetname, '', None))
std = cast(StandardDomain, self.env.get_domain('std'))
- std.add_object(self.objtype, name, self.env.docname, targetname)
+ std.note_object(self.objtype, name, targetname, location=signode)
class EnvVar(GenericObject):
@@ -127,6 +126,7 @@ class Target(SphinxDirective):
fullname = ws_re.sub(' ', self.arguments[0].strip())
targetname = '%s-%s' % (self.name, fullname)
node = nodes.target('', '', ids=[targetname])
+ self.set_source_info(node)
self.state.document.note_explicit_target(node)
ret = [node] # type: List[Node]
if self.indextemplate:
@@ -144,7 +144,7 @@ class Target(SphinxDirective):
_, name = self.name.split(':', 1)
std = cast(StandardDomain, self.env.get_domain('std'))
- std.add_object(name, fullname, self.env.docname, targetname)
+ std.note_object(name, fullname, targetname, location=node)
return ret
@@ -165,7 +165,7 @@ class Cmdoption(ObjectDescription):
logger.warning(__('Malformed option description %r, should '
'look like "opt", "-opt args", "--opt args", '
'"/opt args" or "+opt args"'), potential_option,
- location=(self.env.docname, self.lineno))
+ location=signode)
continue
optname, args = m.groups()
if count:
@@ -274,7 +274,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index
term['ids'].append(node_id)
std = cast(StandardDomain, env.get_domain('std'))
- std.add_object('term', termtext.lower(), env.docname, node_id)
+ std.note_object('term', termtext.lower(), node_id, location=term)
# add an index entry too
indexnode = addnodes.index()
@@ -406,7 +406,9 @@ class Glossary(SphinxDirective):
return messages + [node]
-def token_xrefs(text: str) -> List[Node]:
+def token_xrefs(text: str, productionGroup: str = '') -> List[Node]:
+ if len(productionGroup) != 0:
+ productionGroup += ':'
retnodes = [] # type: List[Node]
pos = 0
for m in token_re.finditer(text):
@@ -414,7 +416,7 @@ def token_xrefs(text: str) -> List[Node]:
txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt))
refnode = pending_xref(m.group(1), reftype='token', refdomain='std',
- reftarget=m.group(1))
+ reftarget=productionGroup + m.group(1))
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
retnodes.append(refnode)
pos = m.end()
@@ -437,11 +439,16 @@ class ProductionList(SphinxDirective):
def run(self) -> List[Node]:
domain = cast(StandardDomain, self.env.get_domain('std'))
node = addnodes.productionlist() # type: Element
- i = 0
+ self.set_source_info(node)
+ # The backslash handling is from ObjectDescription.get_signatures
+ nl_escape_re = re.compile(r'\\\n')
+ lines = nl_escape_re.sub('', self.arguments[0]).split('\n')
- for rule in self.arguments[0].split('\n'):
+ productionGroup = ""
+ i = 0
+ for rule in lines:
if i == 0 and ':' not in rule:
- # production group
+ productionGroup = rule.strip()
continue
i += 1
try:
@@ -451,16 +458,41 @@ class ProductionList(SphinxDirective):
subnode = addnodes.production(rule)
subnode['tokenname'] = name.strip()
if subnode['tokenname']:
- idname = nodes.make_id('grammar-token-%s' % subnode['tokenname'])
+ # nodes.make_id converts '_' to '-',
+ # so we can use '_' to delimit group from name,
+ # and make sure we don't clash with other IDs.
+ idname = 'grammar-token-%s_%s' \
+ % (nodes.make_id(productionGroup), nodes.make_id(name))
if idname not in self.state.document.ids:
subnode['ids'].append(idname)
+
+ idnameOld = nodes.make_id('grammar-token-' + name)
+ if idnameOld not in self.state.document.ids:
+ subnode['ids'].append(idnameOld)
self.state.document.note_implicit_target(subnode, subnode)
- domain.add_object('token', subnode['tokenname'], self.env.docname, idname)
- subnode.extend(token_xrefs(tokens))
+ if len(productionGroup) != 0:
+ objName = "%s:%s" % (productionGroup, name)
+ else:
+ objName = name
+ domain.note_object(objtype='token', name=objName, labelid=idname,
+ location=node)
+ subnode.extend(token_xrefs(tokens, productionGroup))
node.append(subnode)
return [node]
+class TokenXRefRole(XRefRole):
+ def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
+ title: str, target: str) -> Tuple[str, str]:
+ target = target.lstrip('~') # a title-specific thing
+ if not self.has_explicit_title and title[0] == '~':
+ if ':' in title:
+ _, title = title.split(':')
+ else:
+ title = title[1:]
+ return title, target
+
+
class StandardDomain(Domain):
"""
Domain for all objects that don't fit into another domain or are added
@@ -492,7 +524,7 @@ class StandardDomain(Domain):
'option': OptionXRefRole(warn_dangling=True),
'envvar': EnvVarXRefRole(),
# links to tokens in grammar productions
- 'token': XRefRole(),
+ 'token': TokenXRefRole(),
# links to terms in glossary
'term': XRefRole(lowercase=True, innernodeclass=nodes.inline,
warn_dangling=True),
@@ -547,10 +579,51 @@ class StandardDomain(Domain):
for node, settings in env.app.registry.enumerable_nodes.items():
self.enumerable_nodes[node] = settings
+ def note_hyperlink_target(self, name: str, docname: str, node_id: str,
+ title: str = '') -> None:
+ """Add a hyperlink target for cross reference.
+
+ .. warning::
+
+ This is only for internal use. Please don't use this from your extension.
+ ``document.note_explicit_target()`` or ``note_implicit_target()`` are recommended to
+ add a hyperlink target to the document.
+
+ This only adds a hyperlink target to the StandardDomain. And this does not add a
+ node_id to node. Therefore, it is very fragile to calling this without
+ understanding hyperlink target framework in both docutils and Sphinx.
+
+ .. versionadded:: 3.0
+ """
+ if name in self.anonlabels and self.anonlabels[name] != (docname, node_id):
+ logger.warning(__('duplicate label %s, other instance in %s'),
+ name, self.env.doc2path(self.anonlabels[name][0]))
+
+ self.anonlabels[name] = (docname, node_id)
+ if title:
+ self.labels[name] = (docname, node_id, title)
+
@property
def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid
+ def note_object(self, objtype: str, name: str, labelid: str, location: Any = None
+ ) -> None:
+ """Note a generic object for cross reference.
+
+ .. versionadded:: 3.0
+ """
+ if (objtype, name) in self.objects:
+ docname = self.objects[objtype, name][0]
+ logger.warning(__('duplicate %s description of %s, other instance in %s'),
+ objtype, name, docname, location=location)
+ self.objects[objtype, name] = (self.env.docname, labelid)
+
+ def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
+ warnings.warn('StandardDomain.add_object() is deprecated.',
+ RemovedInSphinx50Warning)
+ self.objects[objtype, name] = (docname, labelid)
+
@property
def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
@@ -632,9 +705,6 @@ class StandardDomain(Domain):
continue
self.labels[name] = docname, labelid, sectname
- def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
- self.objects[objtype, name] = (docname, labelid)
-
def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
self.progoptions[program, name] = (docname, labelid)
@@ -814,30 +884,6 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def _resolve_citation_xref(self, env: "BuildEnvironment", fromdocname: str,
- builder: "Builder", typ: str, target: str,
- node: pending_xref, contnode: Element) -> Element:
- warnings.warn('StandardDomain._resolve_citation_xref() is deprecated.',
- RemovedInSphinx30Warning)
- docname, labelid, lineno = self.data['citations'].get(target, ('', '', 0))
- if not docname:
- if 'ids' in node:
- # remove ids attribute that annotated at
- # transforms.CitationReference.apply.
- del node['ids'][:]
- return None
-
- try:
- return make_refnode(builder, fromdocname, docname,
- labelid, contnode)
- except NoUri:
- # remove the ids we added in the CitationReferences
- # transform since they can't be transfered to
- # the contnode (if it's a Text node)
- if not isinstance(contnode, Element):
- del node['ids'][:]
- raise
-
def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
@@ -934,16 +980,6 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype
- def get_figtype(self, node: Node) -> str:
- """Get figure type of nodes.
-
- .. deprecated:: 1.8
- """
- warnings.warn('StandardDomain.get_figtype() is deprecated. '
- 'Please use get_enumerable_node_type() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.get_enumerable_node_type(node)
-
def get_fignumber(self, env: "BuildEnvironment", builder: "Builder",
figtype: str, docname: str, target_node: Element) -> Tuple[int, ...]:
if figtype == 'section':
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index f1ae6024d..d40a6cbb3 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -13,9 +13,8 @@ import pickle
import warnings
from collections import defaultdict
from copy import copy
-from io import BytesIO
from os import path
-from typing import Any, Callable, Dict, Generator, IO, Iterator, List, Set, Tuple, Union
+from typing import Any, Callable, Dict, Generator, Iterator, List, Set, Tuple, Union
from typing import cast
from docutils import nodes
@@ -23,9 +22,7 @@ from docutils.nodes import Node
from sphinx import addnodes
from sphinx.config import Config
-from sphinx.deprecation import (
- RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
-)
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
@@ -221,6 +218,10 @@ class BuildEnvironment:
for domain in app.registry.create_domains(self):
self.domains[domain.name] = domain
+ # setup domains (must do after all initialization)
+ for domain in self.domains.values():
+ domain.setup()
+
# initialize config
self._update_config(app.config)
@@ -640,113 +641,6 @@ class BuildEnvironment:
domain.check_consistency()
self.events.emit('env-check-consistency', self)
- # --------- METHODS FOR COMPATIBILITY --------------------------------------
-
- def update(self, config: Config, srcdir: str, doctreedir: str) -> List[str]:
- warnings.warn('env.update() is deprecated. Please use builder.read() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.app.builder.read()
-
- def _read_serial(self, docnames: List[str], app: "Sphinx") -> None:
- warnings.warn('env._read_serial() is deprecated. Please use builder.read() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.app.builder._read_serial(docnames)
-
- def _read_parallel(self, docnames: List[str], app: "Sphinx", nproc: int) -> None:
- warnings.warn('env._read_parallel() is deprecated. Please use builder.read() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.app.builder._read_parallel(docnames, nproc)
-
- def read_doc(self, docname: str, app: "Sphinx" = None) -> None:
- warnings.warn('env.read_doc() is deprecated. Please use builder.read_doc() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.app.builder.read_doc(docname)
-
- def write_doctree(self, docname: str, doctree: nodes.document) -> None:
- warnings.warn('env.write_doctree() is deprecated. '
- 'Please use builder.write_doctree() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.app.builder.write_doctree(docname, doctree)
-
- @property
- def _nitpick_ignore(self) -> List[str]:
- warnings.warn('env._nitpick_ignore is deprecated. '
- 'Please use config.nitpick_ignore instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.config.nitpick_ignore
-
- @staticmethod
- def load(f: IO, app: "Sphinx" = None) -> "BuildEnvironment":
- warnings.warn('BuildEnvironment.load() is deprecated. '
- 'Please use pickle.load() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- try:
- env = pickle.load(f)
- except Exception as exc:
- # This can happen for example when the pickle is from a
- # different version of Sphinx.
- raise OSError(exc)
- if app:
- env.app = app
- env.config.values = app.config.values
- return env
-
- @classmethod
- def loads(cls, string: bytes, app: "Sphinx" = None) -> "BuildEnvironment":
- warnings.warn('BuildEnvironment.loads() is deprecated. '
- 'Please use pickle.loads() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- io = BytesIO(string)
- return cls.load(io, app)
-
- @classmethod
- def frompickle(cls, filename: str, app: "Sphinx") -> "BuildEnvironment":
- warnings.warn('BuildEnvironment.frompickle() is deprecated. '
- 'Please use pickle.load() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- with open(filename, 'rb') as f:
- return cls.load(f, app)
-
- @staticmethod
- def dump(env: "BuildEnvironment", f: IO) -> None:
- warnings.warn('BuildEnvironment.dump() is deprecated. '
- 'Please use pickle.dump() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- pickle.dump(env, f, pickle.HIGHEST_PROTOCOL)
-
- @classmethod
- def dumps(cls, env: "BuildEnvironment") -> bytes:
- warnings.warn('BuildEnvironment.dumps() is deprecated. '
- 'Please use pickle.dumps() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- io = BytesIO()
- cls.dump(env, io)
- return io.getvalue()
-
- def topickle(self, filename: str) -> None:
- warnings.warn('env.topickle() is deprecated. '
- 'Please use pickle.dump() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- with open(filename, 'wb') as f:
- self.dump(self, f)
-
- @property
- def versionchanges(self) -> Dict[str, List[Tuple[str, str, int, str, str, str]]]:
- warnings.warn('env.versionchanges() is deprecated. '
- 'Please use ChangeSetDomain instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.domaindata['changeset']['changes']
-
- def note_versionchange(self, type: str, version: str,
- node: addnodes.versionmodified, lineno: int) -> None:
- warnings.warn('env.note_versionchange() is deprecated. '
- 'Please use ChangeSetDomain.note_changeset() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- node['type'] = type
- node['version'] = version
- node.line = lineno
- self.get_domain('changeset').note_changeset(node) # type: ignore
-
@property
def indexentries(self) -> Dict[str, List[Tuple[str, str, str, str, str]]]:
warnings.warn('env.indexentries() is deprecated. Please use IndexDomain instead.',
@@ -762,13 +656,3 @@ class BuildEnvironment:
from sphinx.domains.index import IndexDomain
domain = cast(IndexDomain, self.get_domain('index'))
domain.data['entries'] = entries
-
-
-from sphinx.errors import NoUri # NOQA
-
-
-deprecated_alias('sphinx.environment',
- {
- 'NoUri': NoUri,
- },
- RemovedInSphinx30Warning)
diff --git a/sphinx/events.py b/sphinx/events.py
index e6ea379eb..ff49f290c 100644
--- a/sphinx/events.py
+++ b/sphinx/events.py
@@ -11,8 +11,9 @@
"""
import warnings
-from collections import OrderedDict, defaultdict
-from typing import Any, Callable, Dict, List
+from collections import defaultdict
+from operator import attrgetter
+from typing import Any, Callable, Dict, List, NamedTuple
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ExtensionError
@@ -26,6 +27,10 @@ if False:
logger = logging.getLogger(__name__)
+EventListener = NamedTuple('EventListener', [('id', int),
+ ('handler', Callable),
+ ('priority', int)])
+
# List of all known core events. Maps name to arguments description.
core_events = {
@@ -57,7 +62,7 @@ class EventManager:
RemovedInSphinx40Warning)
self.app = app
self.events = core_events.copy()
- self.listeners = defaultdict(OrderedDict) # type: Dict[str, Dict[int, Callable]]
+ self.listeners = defaultdict(list) # type: Dict[str, List[EventListener]]
self.next_listener_id = 0
def add(self, name: str) -> None:
@@ -66,20 +71,22 @@ class EventManager:
raise ExtensionError(__('Event %r already present') % name)
self.events[name] = ''
- def connect(self, name: str, callback: Callable) -> int:
+ def connect(self, name: str, callback: Callable, priority: int) -> int:
"""Connect a handler to specific event."""
if name not in self.events:
raise ExtensionError(__('Unknown event name: %s') % name)
listener_id = self.next_listener_id
self.next_listener_id += 1
- self.listeners[name][listener_id] = callback
+ self.listeners[name].append(EventListener(listener_id, callback, priority))
return listener_id
def disconnect(self, listener_id: int) -> None:
"""Disconnect a handler."""
- for event in self.listeners.values():
- event.pop(listener_id, None)
+ for listeners in self.listeners.values():
+ for listener in listeners[:]:
+ if listener.id == listener_id:
+ listeners.remove(listener)
def emit(self, name: str, *args: Any) -> List:
"""Emit a Sphinx event."""
@@ -91,12 +98,13 @@ class EventManager:
pass
results = []
- for callback in self.listeners[name].values():
+ listeners = sorted(self.listeners[name], key=attrgetter("priority"))
+ for listener in listeners:
if self.app is None:
# for compatibility; RemovedInSphinx40Warning
- results.append(callback(*args))
+ results.append(listener.handler(*args))
else:
- results.append(callback(self.app, *args))
+ results.append(listener.handler(self.app, *args))
return results
def emit_firstresult(self, name: str, *args: Any) -> Any:
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 08e4e5301..7abd6c879 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -20,10 +20,8 @@ from docutils.statemachine import StringList
import sphinx
from sphinx.application import Sphinx
-from sphinx.config import Config, ENUM
-from sphinx.deprecation import (
- RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
-)
+from sphinx.config import ENUM
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
from sphinx.ext.autodoc.mock import mock
@@ -32,7 +30,7 @@ from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import inspect
from sphinx.util import logging
from sphinx.util import rpartition
-from sphinx.util.docstrings import prepare_docstring
+from sphinx.util.docstrings import extract_metadata, prepare_docstring
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
from sphinx.util.typing import stringify as stringify_typehint
@@ -84,6 +82,14 @@ def members_set_option(arg: Any) -> Union[object, Set[str]]:
return {x.strip() for x in arg.split(',') if x.strip()}
+def inherited_members_option(arg: Any) -> Union[object, Set[str]]:
+ """Used to convert the :members: option to auto directives."""
+ if arg is None:
+ return 'object'
+ else:
+ return arg
+
+
SUPPRESS = object()
@@ -516,6 +522,17 @@ class Documenter:
The user can override the skipping decision by connecting to the
``autodoc-skip-member`` event.
"""
+ def is_filtered_inherited_member(name: str) -> bool:
+ if inspect.isclass(self.object):
+ for cls in self.object.__mro__:
+ if cls.__name__ == self.options.inherited_members and cls != self.object:
+ # given member is a member of specified *super class*
+ return True
+ elif name in cls.__dict__:
+ return False
+
+ return False
+
ret = []
# search for members in source code too
@@ -545,32 +562,45 @@ class Documenter:
doc = None
has_doc = bool(doc)
+ metadata = extract_metadata(doc)
+ if 'private' in metadata:
+ # consider a member private if docstring has "private" metadata
+ isprivate = True
+ else:
+ isprivate = membername.startswith('_')
+
keep = False
if want_all and membername.startswith('__') and \
membername.endswith('__') and len(membername) > 4:
# special __methods__
- if self.options.special_members is ALL and \
- membername != '__doc__':
- keep = has_doc or self.options.undoc_members
- elif self.options.special_members and \
- self.options.special_members is not ALL and \
- membername in self.options.special_members:
- keep = has_doc or self.options.undoc_members
+ if self.options.special_members is ALL:
+ if membername == '__doc__':
+ keep = False
+ elif is_filtered_inherited_member(membername):
+ keep = False
+ else:
+ keep = has_doc or self.options.undoc_members
+ elif self.options.special_members:
+ if membername in self.options.special_members:
+ keep = has_doc or self.options.undoc_members
elif (namespace, membername) in attr_docs:
- if want_all and membername.startswith('_'):
+ if want_all and isprivate:
# ignore members whose name starts with _ by default
keep = self.options.private_members
else:
# keep documented attributes
keep = True
isattr = True
- elif want_all and membername.startswith('_'):
+ elif want_all and isprivate:
# ignore members whose name starts with _ by default
keep = self.options.private_members and \
(has_doc or self.options.undoc_members)
else:
- # ignore undocumented members if :undoc-members: is not given
- keep = has_doc or self.options.undoc_members
+ if self.options.members is ALL and is_filtered_inherited_member(membername):
+ keep = False
+ else:
+ # ignore undocumented members if :undoc-members: is not given
+ keep = has_doc or self.options.undoc_members
# give the user a chance to decide whether this member
# should be skipped
@@ -744,7 +774,7 @@ class ModuleDocumenter(Documenter):
option_spec = {
'members': members_option, 'undoc-members': bool_option,
- 'noindex': bool_option, 'inherited-members': bool_option,
+ 'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'synopsis': identity,
'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option,
@@ -1035,7 +1065,7 @@ class DecoratorDocumenter(FunctionDocumenter):
# must be lower than FunctionDocumenter
priority = -1
- def format_args(self, **kwargs):
+ def format_args(self, **kwargs: Any) -> Any:
args = super().format_args(**kwargs)
if ',' in args:
return args
@@ -1051,7 +1081,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
member_order = 20
option_spec = {
'members': members_option, 'undoc-members': bool_option,
- 'noindex': bool_option, 'inherited-members': bool_option,
+ 'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'member-order': identity,
'exclude-members': members_set_option,
'private-members': bool_option, 'special-members': members_option,
@@ -1116,7 +1146,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if hasattr(self.object, '__bases__') and len(self.object.__bases__):
bases = [':class:`%s`' % b.__name__
if b.__module__ in ('__builtin__', 'builtins')
- else ':class:`%s.%s`' % (b.__module__, b.__name__)
+ else ':class:`%s.%s`' % (b.__module__, b.__qualname__)
for b in self.object.__bases__]
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
sourcename)
@@ -1575,38 +1605,6 @@ def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any:
return safe_getattr(obj, name, *defargs)
-def merge_autodoc_default_flags(app: Sphinx, config: Config) -> None:
- """This merges the autodoc_default_flags to autodoc_default_options."""
- if not config.autodoc_default_flags:
- return
-
- # Note: this option will be removed in Sphinx-4.0. But I marked this as
- # RemovedInSphinx *30* Warning because we have to emit warnings for users
- # who will be still in use with Sphinx-3.x. So we should replace this by
- # logger.warning() on 3.0.0 release.
- warnings.warn('autodoc_default_flags is now deprecated. '
- 'Please use autodoc_default_options instead.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- for option in config.autodoc_default_flags:
- if isinstance(option, str):
- config.autodoc_default_options[option] = None
- else:
- logger.warning(
- __("Ignoring invalid option in autodoc_default_flags: %r"),
- option, type='autodoc'
- )
-
-
-from sphinx.ext.autodoc.mock import _MockImporter # NOQA
-
-deprecated_alias('sphinx.ext.autodoc',
- {
- '_MockImporter': _MockImporter,
- },
- RemovedInSphinx40Warning)
-
-
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ModuleDocumenter)
app.add_autodocumenter(ClassDocumenter)
@@ -1635,7 +1633,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')
- app.connect('config-inited', merge_autodoc_default_flags)
app.setup_extension('sphinx.ext.autodoc.type_comment')
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index c0381d7b4..e98b97915 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -180,12 +180,11 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
from sphinx.ext.autodoc.mock import ( # NOQA
- _MockImporter, _MockModule, _MockObject, MockFinder, MockLoader, mock
+ _MockModule, _MockObject, MockFinder, MockLoader, mock
)
deprecated_alias('sphinx.ext.autodoc.importer',
{
- '_MockImporter': _MockImporter,
'_MockModule': _MockModule,
'_MockObject': _MockObject,
'MockFinder': MockFinder,
diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py
index 0ee0fddc1..25f50d27e 100644
--- a/sphinx/ext/autodoc/mock.py
+++ b/sphinx/ext/autodoc/mock.py
@@ -11,13 +11,11 @@
import contextlib
import os
import sys
-import warnings
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import FunctionType, MethodType, ModuleType
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.util import logging
logger = logging.getLogger(__name__)
@@ -81,15 +79,11 @@ class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = os.devnull
- def __init__(self, name: str, loader: "_MockImporter" = None) -> None:
+ def __init__(self, name: str) -> None:
super().__init__(name)
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
- if loader is not None:
- warnings.warn('The loader argument for _MockModule is deprecated.',
- RemovedInSphinx30Warning)
-
def __getattr__(self, name: str) -> _MockObject:
return _make_subclass(name, self.__name__)()
@@ -97,44 +91,6 @@ class _MockModule(ModuleType):
return self.__name__
-class _MockImporter(MetaPathFinder):
- def __init__(self, names: List[str]) -> None:
- self.names = names
- self.mocked_modules = [] # type: List[str]
- # enable hook by adding itself to meta_path
- sys.meta_path.insert(0, self)
-
- warnings.warn('_MockImporter is now deprecated.',
- RemovedInSphinx30Warning)
-
- def disable(self) -> None:
- # remove `self` from `sys.meta_path` to disable import hook
- sys.meta_path = [i for i in sys.meta_path if i is not self]
- # remove mocked modules from sys.modules to avoid side effects after
- # running auto-documenter
- for m in self.mocked_modules:
- if m in sys.modules:
- del sys.modules[m]
-
- def find_module(self, name: str, path: Sequence[Union[bytes, str]] = None) -> Any:
- # check if name is (or is a descendant of) one of our base_packages
- for n in self.names:
- if n == name or name.startswith(n + '.'):
- return self
- return None
-
- def load_module(self, name: str) -> ModuleType:
- if name in sys.modules:
- # module has already been imported, return it
- return sys.modules[name]
- else:
- logger.debug('[autodoc] adding a mock module %s!', name)
- module = _MockModule(name, self)
- sys.modules[name] = module
- self.mocked_modules.append(name)
- return module
-
-
class MockLoader(Loader):
"""A loader for mocking."""
def __init__(self, finder: "MockFinder") -> None:
diff --git a/sphinx/ext/autodoc/type_comment.py b/sphinx/ext/autodoc/type_comment.py
index fb0eebfd8..e6a77f24d 100644
--- a/sphinx/ext/autodoc/type_comment.py
+++ b/sphinx/ext/autodoc/type_comment.py
@@ -14,6 +14,7 @@ from typing import cast
import sphinx
from sphinx.application import Sphinx
+from sphinx.locale import __
from sphinx.pycode.ast import ast
from sphinx.pycode.ast import parse as ast_parse
from sphinx.pycode.ast import unparse as ast_unparse
@@ -128,7 +129,7 @@ def update_annotations_using_type_comments(app: Sphinx, obj: Any, bound_method:
if 'return' not in obj.__annotations__:
obj.__annotations__['return'] = type_sig.return_annotation
except NotImplementedError as exc: # failed to ast.unparse()
- logger.warning("Failed to parse type_comment for %r: %s", obj, exc)
+ logger.warning(__("Failed to parse type_comment for %r: %s"), obj, exc)
def setup(app: Sphinx) -> Dict[str, Any]:
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 7a68552df..3d296cbde 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -745,7 +745,8 @@ def process_generate_options(app: Sphinx) -> None:
with mock(app.config.autosummary_mock_imports):
generate_autosummary_docs(genfiles, builder=app.builder,
suffix=suffix, base_path=app.srcdir,
- app=app, imported_members=imported_members)
+ app=app, imported_members=imported_members,
+ overwrite=app.config.autosummary_generate_overwrite)
def setup(app: Sphinx) -> Dict[str, Any]:
@@ -768,6 +769,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('doctree-read', process_autosummary_toc)
app.connect('builder-inited', process_generate_options)
app.add_config_value('autosummary_generate', [], True, [bool])
+ app.add_config_value('autosummary_generate_overwrite', True, False)
app.add_config_value('autosummary_mock_imports',
lambda config: config.autodoc_mock_imports, 'env')
app.add_config_value('autosummary_imported_members', [], False, [bool])
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index a59a97ea7..ef623e2bd 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -223,7 +223,8 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
suffix: str = '.rst', warn: Callable = None,
info: Callable = None, base_path: str = None,
builder: Builder = None, template_dir: str = None,
- imported_members: bool = False, app: Any = None) -> None:
+ imported_members: bool = False, app: Any = None,
+ overwrite: bool = True) -> None:
if info:
warnings.warn('info argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx40Warning)
@@ -271,22 +272,27 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
try:
name, obj, parent, mod_name = import_by_name(name)
except ImportError as e:
- _warn('[autosummary] failed to import %r: %s' % (name, e))
+ _warn(__('[autosummary] failed to import %r: %s') % (name, e))
continue
- fn = os.path.join(path, name + suffix)
+ content = generate_autosummary_content(name, obj, parent, template, template_name,
+ imported_members, app)
- # skip it if it exists
- if os.path.isfile(fn):
- continue
-
- new_files.append(fn)
+ filename = os.path.join(path, name + suffix)
+ if os.path.isfile(filename):
+ with open(filename) as f:
+ old_content = f.read()
- with open(fn, 'w') as f:
- rendered = generate_autosummary_content(name, obj, parent,
- template, template_name,
- imported_members, app)
- f.write(rendered)
+ if content == old_content:
+ continue
+ elif overwrite: # content has changed
+ with open(filename, 'w') as f:
+ f.write(content)
+ new_files.append(filename)
+ else:
+ with open(filename, 'w') as f:
+ f.write(content)
+ new_files.append(filename)
# descend recursively to new files
if new_files:
@@ -294,7 +300,8 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
suffix=suffix, warn=warn, info=info,
base_path=base_path, builder=builder,
template_dir=template_dir,
- imported_members=imported_members, app=app)
+ imported_members=imported_members, app=app,
+ overwrite=overwrite)
# -- Finding documented entries in files ---------------------------------------
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 987cf2919..e8157848f 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -123,7 +123,7 @@ class CoverageBuilder(Builder):
op.write(' * %-50s [%9s]\n' % (name, typ))
op.write('\n')
- def ignore_pyobj(self, full_name):
+ def ignore_pyobj(self, full_name: str) -> bool:
for exp in self.py_ignorexps:
if exp.search(full_name):
return True
diff --git a/sphinx/ext/duration.py b/sphinx/ext/duration.py
index 02e60cf7e..669baf2f1 100644
--- a/sphinx/ext/duration.py
+++ b/sphinx/ext/duration.py
@@ -11,8 +11,8 @@
from datetime import datetime, timedelta
from itertools import islice
from operator import itemgetter
+from typing import Any, Dict, List
from typing import cast
-from typing import Dict, List
from docutils import nodes
@@ -82,7 +82,7 @@ def on_build_finished(app: Sphinx, error: Exception) -> None:
logger.info('%d.%03d %s', d.seconds, d.microseconds / 1000, docname)
-def setup(app):
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(DurationDomain)
app.connect('builder-inited', on_builder_inited)
app.connect('source-read', on_source_read)
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index 01803708d..db2a15b14 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -229,7 +229,7 @@ class InheritanceGraph:
if module in ('__builtin__', 'builtins'):
fullname = cls.__name__
else:
- fullname = '%s.%s' % (module, cls.__name__)
+ fullname = '%s.%s' % (module, cls.__qualname__)
if parts == 0:
result = fullname
else:
@@ -247,6 +247,7 @@ class InheritanceGraph:
default_graph_attrs = {
'rankdir': 'LR',
'size': '"8.0, 12.0"',
+ 'bgcolor': 'transparent',
}
default_node_attrs = {
'shape': 'box',
@@ -254,7 +255,8 @@ class InheritanceGraph:
'height': 0.25,
'fontname': '"Vera Sans, DejaVu Sans, Liberation Sans, '
'Arial, Helvetica, sans"',
- 'style': '"setlinewidth(0.5)"',
+ 'style': '"setlinewidth(0.5),filled"',
+ 'fillcolor': 'white',
}
default_edge_attrs = {
'arrowsize': 0.5,
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
deleted file mode 100644
index 99f117bf7..000000000
--- a/sphinx/ext/mathbase.py
+++ /dev/null
@@ -1,153 +0,0 @@
-"""
- sphinx.ext.mathbase
- ~~~~~~~~~~~~~~~~~~~
-
- Set up math support in source files and LaTeX/text output.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Callable, List, Tuple
-
-from docutils import nodes
-from docutils.nodes import Element, Node
-from docutils.parsers.rst.roles import math_role as math_role_base
-
-from sphinx.addnodes import math, math_block as displaymath # NOQA # to keep compatibility
-from sphinx.application import Sphinx
-from sphinx.builders.latex.nodes import math_reference as eqref # NOQA # to keep compatibility
-from sphinx.deprecation import RemovedInSphinx30Warning
-from sphinx.directives.patches import MathDirective as MathDirectiveBase
-from sphinx.domains.math import MathDomain # NOQA # to keep compatibility
-from sphinx.domains.math import MathReferenceRole as EqXRefRole # NOQA # to keep compatibility
-from sphinx.writers.html import HTMLTranslator
-from sphinx.writers.latex import LaTeXTranslator
-from sphinx.writers.manpage import ManualPageTranslator
-from sphinx.writers.texinfo import TexinfoTranslator
-from sphinx.writers.text import TextTranslator
-
-
-class MathDirective(MathDirectiveBase):
- def run(self) -> List[Node]:
- warnings.warn('sphinx.ext.mathbase.MathDirective is moved to '
- 'sphinx.directives.patches package.',
- RemovedInSphinx30Warning, stacklevel=2)
- return super().run()
-
-
-def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
- warnings.warn('sphinx.ext.mathbase.math_role() is deprecated. '
- 'Please use docutils.parsers.rst.roles.math_role() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return math_role_base(role, rawtext, text, lineno, inliner, options, content)
-
-
-def get_node_equation_number(writer: HTMLTranslator, node: nodes.math_block) -> str:
- warnings.warn('sphinx.ext.mathbase.get_node_equation_number() is moved to '
- 'sphinx.util.math package.',
- RemovedInSphinx30Warning, stacklevel=2)
- from sphinx.util.math import get_node_equation_number
- return get_node_equation_number(writer, node)
-
-
-def wrap_displaymath(text: str, label: str, numbering: bool) -> str:
- warnings.warn('sphinx.ext.mathbase.wrap_displaymath() is moved to '
- 'sphinx.util.math package.',
- RemovedInSphinx30Warning, stacklevel=2)
- from sphinx.util.math import wrap_displaymath
- return wrap_displaymath(text, label, numbering)
-
-
-def is_in_section_title(node: Element) -> bool:
- """Determine whether the node is in a section title"""
- from sphinx.util.nodes import traverse_parent
-
- warnings.warn('is_in_section_title() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- for ancestor in traverse_parent(node):
- if isinstance(ancestor, nodes.title) and \
- isinstance(ancestor.parent, nodes.section):
- return True
- return False
-
-
-def latex_visit_math(self: LaTeXTranslator, node: Element) -> None:
- warnings.warn('latex_visit_math() is deprecated. '
- 'Please use LaTeXTranslator.visit_math() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math(node)
-
-
-def latex_visit_displaymath(self: LaTeXTranslator, node: Element) -> None:
- warnings.warn('latex_visit_displaymath() is deprecated. '
- 'Please use LaTeXTranslator.visit_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math_block(node)
-
-
-def man_visit_math(self: ManualPageTranslator, node: Element) -> None:
- warnings.warn('man_visit_math() is deprecated. '
- 'Please use ManualPageTranslator.visit_math() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math(node)
-
-
-def man_visit_displaymath(self: ManualPageTranslator, node: Element) -> None:
- warnings.warn('man_visit_displaymath() is deprecated. '
- 'Please use ManualPageTranslator.visit_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math_block(node)
-
-
-def man_depart_displaymath(self: ManualPageTranslator, node: Element) -> None:
- warnings.warn('man_depart_displaymath() is deprecated. '
- 'Please use ManualPageTranslator.depart_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.depart_math_block(node)
-
-
-def texinfo_visit_math(self: TexinfoTranslator, node: Element) -> None:
- warnings.warn('texinfo_visit_math() is deprecated. '
- 'Please use TexinfoTranslator.visit_math() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math(node)
-
-
-def texinfo_visit_displaymath(self: TexinfoTranslator, node: Element) -> None:
- warnings.warn('texinfo_visit_displaymath() is deprecated. '
- 'Please use TexinfoTranslator.visit_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math_block(node)
-
-
-def texinfo_depart_displaymath(self: TexinfoTranslator, node: Element) -> None:
- warnings.warn('texinfo_depart_displaymath() is deprecated. '
- 'Please use TexinfoTranslator.depart_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
-
-
-def text_visit_math(self: TextTranslator, node: Element) -> None:
- warnings.warn('text_visit_math() is deprecated. '
- 'Please use TextTranslator.visit_math() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math(node)
-
-
-def text_visit_displaymath(self: TextTranslator, node: Element) -> None:
- warnings.warn('text_visit_displaymath() is deprecated. '
- 'Please use TextTranslator.visit_math_block() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.visit_math_block(node)
-
-
-def setup_math(app: Sphinx,
- htmlinlinevisitors: Tuple[Callable, Callable],
- htmldisplayvisitors: Tuple[Callable, Callable]) -> None:
- warnings.warn('setup_math() is deprecated. '
- 'Please use app.add_html_math_renderer() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- app.add_html_math_renderer('unknown', htmlinlinevisitors, htmldisplayvisitors)
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index 81f2496cb..7f6ebe478 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -561,7 +561,7 @@ class GoogleDocstring:
lines = self._consume_to_next_section()
self._parsed_lines.extend(lines)
- def _parse_admonition(self, admonition, section):
+ def _parse_admonition(self, admonition: str, section: str) -> List[str]:
# type (str, str) -> List[str]
lines = self._consume_to_next_section()
return self._format_admonition(admonition, lines)
@@ -603,7 +603,7 @@ class GoogleDocstring:
label = labels.get(section.lower(), section)
return self._parse_generic_section(label, use_admonition)
- def _parse_custom_generic_section(self, section):
+ def _parse_custom_generic_section(self, section: str) -> List[str]:
# for now, no admonition for simple custom sections
return self._parse_generic_section(section, False)
diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
index 72ab74120..e91a3ec14 100644
--- a/sphinx/ext/napoleon/iterators.py
+++ b/sphinx/ext/napoleon/iterators.py
@@ -60,9 +60,7 @@ class peek_iter:
return self
def __next__(self, n: int = None) -> Any:
- # note: prevent 2to3 to transform self.next() in next(self) which
- # causes an infinite loop !
- return getattr(self, 'next')(n)
+ return self.next(n)
def _fillcache(self, n: int) -> None:
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 82c2111ef..dc24a1993 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -9,7 +9,6 @@
"""
import traceback
-import warnings
from typing import Any, Dict, Iterable, Iterator, Set, Tuple
from docutils import nodes
@@ -18,8 +17,6 @@ from docutils.nodes import Element, Node
import sphinx
from sphinx import addnodes
from sphinx.application import Sphinx
-from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer
@@ -57,10 +54,10 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
if app.builder.name.startswith("epub") and not env.config.viewcode_enable_epub:
return
- def has_tag(modname, fullname, docname, refname):
+ def has_tag(modname: str, fullname: str, docname: str, refname: str) -> bool:
entry = env._viewcode_modules.get(modname, None) # type: ignore
if entry is False:
- return
+ return False
code_tags = app.emit_firstresult('viewcode-find-source', modname)
if code_tags is None:
@@ -69,7 +66,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
analyzer.find_tags()
except Exception:
env._viewcode_modules[modname] = False # type: ignore
- return
+ return False
code = analyzer.code
tags = analyzer.tags
@@ -84,6 +81,8 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
used[fullname] = docname
return True
+ return False
+
for objnode in doctree.traverse(addnodes.desc):
if objnode.get('domain') != 'py':
continue
@@ -234,18 +233,10 @@ def collect_pages(app: Sphinx) -> Iterator[Tuple[str, Dict[str, Any], str]]:
yield ('_modules/index', context, 'page.html')
-def migrate_viewcode_import(app: Sphinx, config: Config) -> None:
- if config.viewcode_import is not None:
- warnings.warn('viewcode_import was renamed to viewcode_follow_imported_members. '
- 'Please update your configuration.',
- RemovedInSphinx30Warning, stacklevel=2)
-
-
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('viewcode_import', None, False)
app.add_config_value('viewcode_enable_epub', False, False)
app.add_config_value('viewcode_follow_imported_members', True, False)
- app.connect('config-inited', migrate_viewcode_import)
app.connect('doctree-read', doctree_read)
app.connect('env-merge-info', env_merge_info)
app.connect('html-collect-pages', collect_pages)
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 49a4105bf..4dc9ce543 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -8,8 +8,6 @@
:license: BSD, see LICENSE for details.
"""
-import html
-import warnings
from functools import partial
from importlib import import_module
from typing import Any, Dict
@@ -26,8 +24,6 @@ from pygments.style import Style
from pygments.styles import get_style_by_name
from pygments.util import ClassNotFound
-from sphinx.deprecation import RemovedInSphinx30Warning
-from sphinx.ext import doctest
from sphinx.locale import __
from sphinx.pygments_styles import SphinxStyle, NoneStyle
from sphinx.util import logging, texescape
@@ -65,7 +61,7 @@ class PygmentsBridge:
latex_formatter = LatexFormatter
def __init__(self, dest: str = 'html', stylename: str = 'sphinx',
- trim_doctest_flags: bool = None, latex_engine: str = None) -> None:
+ latex_engine: str = None) -> None:
self.dest = dest
self.latex_engine = latex_engine
@@ -77,11 +73,6 @@ class PygmentsBridge:
self.formatter = self.latex_formatter
self.formatter_args['commandprefix'] = 'PYG'
- self.trim_doctest_flags = trim_doctest_flags
- if trim_doctest_flags is not None:
- warnings.warn('trim_doctest_flags option for PygmentsBridge is now deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
def get_style(self, stylename: str) -> Style:
if stylename is None or stylename == 'sphinx':
return SphinxStyle
@@ -97,19 +88,6 @@ class PygmentsBridge:
kwargs.update(self.formatter_args)
return self.formatter(**kwargs)
- def unhighlighted(self, source: str) -> str:
- warnings.warn('PygmentsBridge.unhighlighted() is now deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- if self.dest == 'html':
- return '<pre>' + html.escape(source) + '</pre>\n'
- else:
- # first, escape highlighting characters like Pygments does
- source = source.translate(escape_hl_chars)
- # then, escape all characters nonrepresentable in LaTeX
- source = texescape.escape(source, self.latex_engine)
- return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \
- source + '\\end{Verbatim}\n'
-
def get_lexer(self, source: str, lang: str, opts: Dict = None,
force: bool = False, location: Any = None) -> Lexer:
if not opts:
@@ -127,11 +105,6 @@ class PygmentsBridge:
lang = 'pycon3'
else:
lang = 'python3'
- elif lang == 'guess':
- try:
- lexer = guess_lexer(source)
- except Exception:
- lexer = lexers['none']
if lang in lexers:
# just return custom lexers here (without installing raiseonerror filter)
@@ -141,7 +114,7 @@ class PygmentsBridge:
else:
try:
if lang == 'guess':
- lexer = guess_lexer(lang, **opts)
+ lexer = guess_lexer(source, **opts)
else:
lexer = get_lexer_by_name(lang, **opts)
except ClassNotFound:
@@ -161,11 +134,6 @@ class PygmentsBridge:
lexer = self.get_lexer(source, lang, opts, force, location)
- # trim doctest options if wanted
- if isinstance(lexer, PythonConsoleLexer) and self.trim_doctest_flags:
- source = doctest.blankline_re.sub('', source)
- source = doctest.doctestopt_re.sub('', source)
-
# highlight via Pygments
formatter = self.get_formatter(**kwargs)
try:
diff --git a/sphinx/io.py b/sphinx/io.py
index b1290ae89..18b4f053e 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -9,7 +9,7 @@
"""
import codecs
import warnings
-from typing import Any, List, Tuple
+from typing import Any, List
from typing import Type # for python3.5.1
from docutils import nodes
@@ -19,14 +19,11 @@ from docutils.io import FileInput, Input, NullOutput
from docutils.parsers import Parser
from docutils.parsers.rst import Parser as RSTParser
from docutils.readers import standalone
-from docutils.statemachine import StringList, string2lines
from docutils.transforms import Transform
from docutils.transforms.references import DanglingReferences
from docutils.writers import UnfilteredWriter
-from sphinx.deprecation import (
- RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
-)
+from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment import BuildEnvironment
from sphinx.errors import FiletypeNotFoundError
from sphinx.transforms import (
@@ -39,7 +36,6 @@ from sphinx.transforms.references import SphinxDomains
from sphinx.util import logging, get_filetype
from sphinx.util import UnicodeDecodeErrorHandler
from sphinx.util.docutils import LoggingReporter
-from sphinx.util.rst import append_epilog, docinfo_re, prepend_prolog
from sphinx.versioning import UIDTransform
if False:
@@ -160,17 +156,6 @@ class SphinxI18nReader(SphinxBaseReader):
if transform in self.transforms:
self.transforms.remove(transform)
- def set_lineno_for_reporter(self, lineno: int) -> None:
- """Stores the source line number of original text."""
- warnings.warn('SphinxI18nReader.set_lineno_for_reporter() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- @property
- def line(self) -> int:
- warnings.warn('SphinxI18nReader.line is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return 0
-
class SphinxDummyWriter(UnfilteredWriter):
"""Dummy writer module used for generating doctree."""
@@ -186,93 +171,13 @@ def SphinxDummySourceClass(source: Any, *args: Any, **kwargs: Any) -> Any:
return source
-class SphinxBaseFileInput(FileInput):
- """A base class of SphinxFileInput.
-
- It supports to replace unknown Unicode characters to '?'.
- """
-
- def __init__(self, app: "Sphinx", env: BuildEnvironment,
- *args: Any, **kwargs: Any) -> None:
- self.app = app
- self.env = env
-
- warnings.warn('%s is deprecated.' % self.__class__.__name__,
- RemovedInSphinx30Warning, stacklevel=2)
-
- kwargs['error_handler'] = 'sphinx' # py3: handle error on open.
- super().__init__(*args, **kwargs)
-
- def warn_and_replace(self, error: Any) -> Tuple:
- return UnicodeDecodeErrorHandler(self.env.docname)(error)
-
-
class SphinxFileInput(FileInput):
"""A basic FileInput for Sphinx."""
- supported = ('*',) # RemovedInSphinx30Warning
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
kwargs['error_handler'] = 'sphinx'
super().__init__(*args, **kwargs)
-class SphinxRSTFileInput(SphinxBaseFileInput):
- """A reST FileInput for Sphinx.
-
- This FileInput automatically prepends and appends text by :confval:`rst_prolog` and
- :confval:`rst_epilog`.
-
- .. important::
-
- This FileInput uses an instance of ``StringList`` as a return value of ``read()``
- method to indicate original source filename and line numbers after prepending and
- appending.
- For that reason, ``sphinx.parsers.RSTParser`` should be used with this to parse
- a content correctly.
- """
- supported = ('restructuredtext',)
-
- def prepend_prolog(self, text: StringList, prolog: str) -> None:
- docinfo = self.count_docinfo_lines(text)
- if docinfo:
- # insert a blank line after docinfo
- text.insert(docinfo, '', '<generated>', 0)
- docinfo += 1
-
- # insert prolog (after docinfo if exists)
- for lineno, line in enumerate(prolog.splitlines()):
- text.insert(docinfo + lineno, line, '<rst_prolog>', lineno)
-
- text.insert(docinfo + lineno + 1, '', '<generated>', 0)
-
- def append_epilog(self, text: StringList, epilog: str) -> None:
- # append a blank line and rst_epilog
- text.append('', '<generated>', 0)
- for lineno, line in enumerate(epilog.splitlines()):
- text.append(line, '<rst_epilog>', lineno)
-
- def read(self) -> StringList: # type: ignore
- inputstring = super().read()
- lines = string2lines(inputstring, convert_whitespace=True)
- content = StringList()
- for lineno, line in enumerate(lines):
- content.append(line, self.source_path, lineno)
-
- prepend_prolog(content, self.env.config.rst_prolog)
- append_epilog(content, self.env.config.rst_epilog)
-
- return content
-
- def count_docinfo_lines(self, content: StringList) -> int:
- if len(content) == 0:
- return 0
- else:
- for lineno, line in enumerate(content.data):
- if not docinfo_re.match(line):
- break
- return lineno
-
-
def read_doc(app: "Sphinx", env: BuildEnvironment, filename: str) -> nodes.document:
"""Parse a document and convert to doctree."""
# set up error_handler for the target document
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 262425a36..9812355ca 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -10,13 +10,10 @@
import gettext
import locale
-import warnings
from collections import UserString, defaultdict
from gettext import NullTranslations
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union
-from sphinx.deprecation import RemovedInSphinx30Warning
-
class _TranslationProxy(UserString):
"""
@@ -106,24 +103,6 @@ class _TranslationProxy(UserString):
return '<%s broken>' % self.__class__.__name__
-def mygettext(string: str) -> str:
- """Used instead of _ when creating TranslationProxies, because _ is
- not bound yet at that time.
- """
- warnings.warn('sphinx.locale.mygettext() is deprecated. Please use `_()` instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return _(string)
-
-
-def lazy_gettext(string: str) -> str:
- """A lazy version of `gettext`."""
- # if isinstance(string, _TranslationProxy):
- # return string
- warnings.warn('sphinx.locale.laxy_gettext() is deprecated. Please use `_()` instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return _TranslationProxy(mygettext, string) # type: ignore
-
-
translators = defaultdict(NullTranslations) # type: Dict[Tuple[str, str], NullTranslations]
@@ -218,7 +197,7 @@ def _lazy_translate(catalog: str, namespace: str, message: str) -> str:
return translator.gettext(message)
-def get_translation(catalog, namespace='general'):
+def get_translation(catalog: str, namespace: str = 'general') -> Callable:
"""Get a translation function based on the *catalog* and *namespace*.
The extension can use this API to translate the messages on the
@@ -266,12 +245,6 @@ _ = get_translation('sphinx')
__ = get_translation('sphinx', 'console')
-def l_(*args):
- warnings.warn('sphinx.locale.l_() is deprecated. Please use `_()` instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return _(*args)
-
-
# labels
admonitionlabels = {
'attention': _('Attention'),
diff --git a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
index 0d402ede1..c651d7510 100644
--- a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po
@@ -1,7 +1,7 @@
# Translations template for Sphinx.
# Copyright (C) 2019 ORGANIZATION
# This file is distributed under the same license as the Sphinx project.
-#
+#
# Translators:
# Yinian Chin <yinian1992@live.com>, 2015,2017-2018
# Hsiaoming Yang <me@lepture.com>, 2018
@@ -28,6 +28,7 @@ msgstr ""
"Generated-By: Babel 2.6.0\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 2.2.4\n"
#: sphinx/application.py:153
#, python-format
@@ -50,9 +51,7 @@ msgstr "正在è¿è¡Œ Sphinx v%s"
#: sphinx/application.py:214
#, python-format
-msgid ""
-"This project needs at least Sphinx v%s and therefore cannot be built with "
-"this version."
+msgid "This project needs at least Sphinx v%s and therefore cannot be built with this version."
msgstr "è¯¥é¡¹ç›®éœ€è¦ Sphinx v%s åŠä»¥ä¸Šç‰ˆæœ¬ï¼Œä½¿ç”¨çŽ°æœ‰ç‰ˆæœ¬ä¸èƒ½æž„建文档。"
#: sphinx/application.py:234
@@ -66,9 +65,8 @@ msgstr "åŒæ—¶è®¾ç½®æ‰©å±•å %s:"
#: sphinx/application.py:245
msgid ""
-"'setup' as currently defined in conf.py isn't a Python callable. Please "
-"modify its definition to make it a callable function. This is needed for "
-"conf.py to behave as a Sphinx extension."
+"'setup' as currently defined in conf.py isn't a Python callable. Please modify its definition to make it a callable function. This is needed for conf.py to "
+"behave as a Sphinx extension."
msgstr "å½“å‰ conf.py 中定义的 'setup' 䏿˜¯ä¸€ä¸ªå¯è°ƒç”¨çš„ Python 对象。请把其定义改为一个å¯è°ƒç”¨çš„函数。Sphinx 扩展的 conf.py 必须这样é…置。"
#: sphinx/application.py:269
@@ -76,9 +74,8 @@ msgstr "å½“å‰ conf.py 中定义的 'setup' 䏿˜¯ä¸€ä¸ªå¯è°ƒç”¨çš„ Python 对è
msgid "loading translations [%s]... "
msgstr "正在加载翻译 [%s]... "
-#: sphinx/application.py:285 sphinx/builders/html.py:852
-#: sphinx/builders/html.py:870 sphinx/builders/html.py:1133
-#: sphinx/builders/html.py:1151 sphinx/util/__init__.py:702
+#: sphinx/application.py:285 sphinx/builders/html.py:852 sphinx/builders/html.py:870 sphinx/builders/html.py:1133 sphinx/builders/html.py:1151
+#: sphinx/util/__init__.py:702
msgid "done"
msgstr "完æˆ"
@@ -134,18 +131,12 @@ msgstr "角色 %r 已注册,将被覆盖"
#: sphinx/application.py:1179
#, python-format
-msgid ""
-"the %s extension does not declare if it is safe for parallel reading, "
-"assuming it isn't - please ask the extension author to check and make it "
-"explicit"
+msgid "the %s extension does not declare if it is safe for parallel reading, assuming it isn't - please ask the extension author to check and make it explicit"
msgstr "扩展 %s 没有声明是å¦å¹¶è¡Œè¯»å–安全,默认å‡å®šä¸ºå¦ - 请è”ç³»æ‰©å±•ä½œè€…æ£€æŸ¥æ˜¯å¦æ”¯æŒè¯¥ç‰¹æ€§å¹¶æ˜¾å¼å£°æ˜Ž"
#: sphinx/application.py:1185
#, python-format
-msgid ""
-"the %s extension does not declare if it is safe for parallel writing, "
-"assuming it isn't - please ask the extension author to check and make it "
-"explicit"
+msgid "the %s extension does not declare if it is safe for parallel writing, assuming it isn't - please ask the extension author to check and make it explicit"
msgstr "%s 扩展没有声明是å¦å¹¶è¡Œå†™å…¥å®‰å…¨ï¼Œé»˜è®¤å‡å®šä¸ºå¦ - 请è”ç³»æ‰©å±•ä½œè€…æ£€æŸ¥æ˜¯å¦æ”¯æŒè¯¥ç‰¹æ€§å¹¶æ˜¾å¼å£°æ˜Ž"
#: sphinx/application.py:1196
@@ -155,9 +146,7 @@ msgstr "æ‰§è¡Œé¡ºåº %s"
#: sphinx/config.py:220
#, python-format
-msgid ""
-"cannot override dictionary config setting %r, ignoring (use %r to set "
-"individual elements)"
+msgid "cannot override dictionary config setting %r, ignoring (use %r to set individual elements)"
msgstr "ä¸èƒ½è¦†ç›–å­—å…¸é…置项 %r,已忽略 (请用 %r 设置å•个字典元素)"
#: sphinx/config.py:229
@@ -191,8 +180,7 @@ msgid "There is a syntax error in your configuration file: %s\n"
msgstr "é…置文件中存在语法错误: %s\n"
#: sphinx/config.py:366
-msgid ""
-"The configuration file (or one of the modules it imports) called sys.exit()"
+msgid "The configuration file (or one of the modules it imports) called sys.exit()"
msgstr "é…置文件(或é…置文件导入的模å—)调用了 sys.exit()"
#: sphinx/config.py:370
@@ -201,7 +189,10 @@ msgid ""
"There is a programmable error in your configuration file:\n"
"\n"
"%s"
-msgstr "é…置文件中有程åºä¸Šçš„错误:\n\n%s"
+msgstr ""
+"é…置文件中有程åºä¸Šçš„错误:\n"
+"\n"
+"%s"
#: sphinx/config.py:397
#, python-format
@@ -231,9 +222,7 @@ msgid "Listing %s"
msgstr "列表 %s"
#: sphinx/config.py:447
-msgid ""
-"The config value `{name}` has to be a one of {candidates}, but `{current}` "
-"is given."
+msgid "The config value `{name}` has to be a one of {candidates}, but `{current}` is given."
msgstr "é…置项 `{name}` 必须设置为 {candidates} 之一,但现在是 `{current}` 。"
#: sphinx/config.py:465
@@ -243,16 +232,12 @@ msgid ""
msgstr "é…置值\"[name]\"的类型为\"[当å‰._name];但\"当å‰\"为\"当å‰\"。\"当å‰\"为\"当å‰\"。\"当å‰\"为\"当å‰\"。=================================预期 [å…许]。"
#: sphinx/config.py:478
-msgid ""
-"The config value `{name}' has type `{current.__name__}', defaults to "
-"`{default.__name__}'."
+msgid "The config value `{name}' has type `{current.__name__}', defaults to `{default.__name__}'."
msgstr "é…置项 `{name}' 的类型是 `{current.__name__}',默认为 `{default.__name__}'。"
#: sphinx/config.py:497
#, python-format
-msgid ""
-"the config value %r is set to a string with non-ASCII characters; this can "
-"lead to Unicode errors occurring. Please use Unicode strings, e.g. %r."
+msgid "the config value %r is set to a string with non-ASCII characters; this can lead to Unicode errors occurring. Please use Unicode strings, e.g. %r."
msgstr "é…置项 %r 的值包å«äº†éž ASCII 字符,这会导致 Unicode 错误。请使用 Unicode 字符串,例如 %r。"
#: sphinx/config.py:506
@@ -278,16 +263,12 @@ msgstr "未知事件å称:%s"
#: sphinx/extension.py:52
#, python-format
-msgid ""
-"The %s extension is required by needs_extensions settings, but it is not "
-"loaded."
+msgid "The %s extension is required by needs_extensions settings, but it is not loaded."
msgstr "未能加载 needs_extensions é…置项所需的 %s。"
#: sphinx/extension.py:57
#, python-format
-msgid ""
-"This project needs the extension %s at least in version %s and therefore "
-"cannot be built with the loaded version (%s)."
+msgid "This project needs the extension %s at least in version %s and therefore cannot be built with the loaded version (%s)."
msgstr "该项目所需扩展 %s æœ€ä½Žè¦æ±‚版本 %s ,当å‰åŠ è½½ç‰ˆæœ¬ (%s) 无法构建文档。"
#: sphinx/highlighting.py:142
@@ -417,23 +398,17 @@ msgstr "无法导入扩展 %s"
#: sphinx/registry.py:479
#, python-format
-msgid ""
-"extension %r has no setup() function; is it really a Sphinx extension "
-"module?"
+msgid "extension %r has no setup() function; is it really a Sphinx extension module?"
msgstr "扩展 %r 未包å«setup() 函数;它确实是一个 Sphinx 扩展模å—å—?"
#: sphinx/registry.py:488
#, python-format
-msgid ""
-"The %s extension used by this project needs at least Sphinx v%s; it "
-"therefore cannot be built with this version."
+msgid "The %s extension used by this project needs at least Sphinx v%s; it therefore cannot be built with this version."
msgstr "该项目所用扩展 %s éœ€è¦ Sphinx 版本 %s 以上;当å‰ç‰ˆæœ¬æ— æ³•构建文档。"
#: sphinx/registry.py:496
#, python-format
-msgid ""
-"extension %r returned an unsupported object from its setup() function; it "
-"should return None or a metadata dictionary"
+msgid "extension %r returned an unsupported object from its setup() function; it should return None or a metadata dictionary"
msgstr "扩展 %r 在其 setup() å‡½æ•°ä¸­è¿”å›žäº†ä¸€ä¸ªä¸æ”¯æŒçš„对象;该函数应返回 None 或一个元数æ®å­—å…¸"
#: sphinx/roles.py:221 sphinx/roles.py:272
@@ -472,9 +447,7 @@ msgid "file %r on theme path is not a valid zipfile or contains no theme"
msgstr "主题路径指定的文件 %r 是一个无效的或ä¸åŒ…å«ä¸»é¢˜çš„ zip 文件"
#: sphinx/theming.py:258
-msgid ""
-"sphinx_rtd_theme is no longer a hard dependency since version 1.4.0. Please "
-"install it manually.(pip install sphinx_rtd_theme)"
+msgid "sphinx_rtd_theme is no longer a hard dependency since version 1.4.0. Please install it manually.(pip install sphinx_rtd_theme)"
msgstr "sphinx_rtd_theme 从 1.4.0 版本开始ä¸å†ä½œä¸ºå¼ºä¾èµ–。请手动安装。(pip install sphinx_rtd_theme)"
#: sphinx/theming.py:262
@@ -496,8 +469,7 @@ msgstr "æ²¡æœ‰æ‰¾åˆ°é€‚åˆ %s 构建器的图åƒï¼š%s"
msgid "building [mo]: "
msgstr "构建 [mo]: "
-#: sphinx/builders/__init__.py:232 sphinx/builders/__init__.py:574
-#: sphinx/builders/__init__.py:602
+#: sphinx/builders/__init__.py:232 sphinx/builders/__init__.py:574 sphinx/builders/__init__.py:602
msgid "writing output... "
msgstr "写入输出... "
@@ -522,8 +494,7 @@ msgstr "æ‰€æœ‰æºæ–‡ä»¶"
#: sphinx/builders/__init__.py:298
#, python-format
-msgid ""
-"file %r given on command line is not under the source directory, ignoring"
+msgid "file %r given on command line is not under the source directory, ignoring"
msgstr "æºæ–‡ä»¶ç›®å½•下没有命令行给出的 %r 文件,将被忽略"
#: sphinx/builders/__init__.py:303
@@ -602,8 +573,7 @@ msgstr "准备文件"
msgid "duplicated ToC entry found: %s"
msgstr "找到é‡å¤çš„ToCæ¡ç›®: %s"
-#: sphinx/builders/_epub_base.py:414 sphinx/builders/html.py:761
-#: sphinx/builders/latex/__init__.py:415 sphinx/builders/texinfo.py:190
+#: sphinx/builders/_epub_base.py:414 sphinx/builders/html.py:761 sphinx/builders/latex/__init__.py:415 sphinx/builders/texinfo.py:190
msgid "copying images... "
msgstr "å¤åˆ¶å›¾åƒ... "
@@ -612,8 +582,7 @@ msgstr "å¤åˆ¶å›¾åƒ... "
msgid "cannot read image file %r: copying it instead"
msgstr "无法读å–å›¾åƒæ–‡ä»¶ %r:直接å¤åˆ¶"
-#: sphinx/builders/_epub_base.py:427 sphinx/builders/html.py:769
-#: sphinx/builders/latex/__init__.py:423 sphinx/builders/texinfo.py:199
+#: sphinx/builders/_epub_base.py:427 sphinx/builders/html.py:769 sphinx/builders/latex/__init__.py:423 sphinx/builders/texinfo.py:199
#, python-format
msgid "cannot copy image file %r: %s"
msgstr "无法å¤åˆ¶å›¾åƒæ–‡ä»¶ %r:%s"
@@ -627,8 +596,7 @@ msgstr "æ— æ³•å†™å…¥å›¾åƒæ–‡ä»¶ %r:%s"
msgid "Pillow not found - copying image files"
msgstr "未找到Pillow - å¤åˆ¶å›¾åƒæ–‡ä»¶"
-#: sphinx/builders/_epub_base.py:490 sphinx/builders/_epub_base.py:503
-#: sphinx/builders/_epub_base.py:539 sphinx/builders/_epub_base.py:724
+#: sphinx/builders/_epub_base.py:490 sphinx/builders/_epub_base.py:503 sphinx/builders/_epub_base.py:539 sphinx/builders/_epub_base.py:724
#: sphinx/builders/_epub_base.py:757 sphinx/builders/epub3.py:183
#, python-format
msgid "writing %s file..."
@@ -762,9 +730,7 @@ msgstr "HTML 页é¢ä¿å­˜åœ¨ %(outdir)s 目录。"
msgid "Failed to read build info file: %r"
msgstr "è¯»å–æž„å»ºä¿¡æ¯æ–‡ä»¶å¤±è´¥ï¼š%r"
-#: sphinx/builders/html.py:488 sphinx/builders/latex/__init__.py:206
-#: sphinx/transforms/__init__.py:121 sphinx/writers/manpage.py:118
-#: sphinx/writers/texinfo.py:243
+#: sphinx/builders/html.py:488 sphinx/builders/latex/__init__.py:206 sphinx/transforms/__init__.py:121 sphinx/writers/manpage.py:118 sphinx/writers/texinfo.py:243
#, python-format
msgid "%b %d, %Y"
msgstr "%Y 年 %m 月 %d 日"
@@ -850,9 +816,7 @@ msgid "Failed to write build info file: %r"
msgstr "å†™å…¥æž„å»ºä¿¡æ¯æ–‡ä»¶å¤±è´¥ï¼š%r"
#: sphinx/builders/html.py:927
-msgid ""
-"search index couldn't be loaded, but not all documents will be built: the "
-"index will be incomplete."
+msgid "search index couldn't be loaded, but not all documents will be built: the index will be incomplete."
msgstr "无法加载æœç´¢ç´¢å¼•,ä¸ä¼šæž„建所有文档:索引将ä¸å®Œæ•´ã€‚"
#: sphinx/builders/html.py:996
@@ -862,9 +826,7 @@ msgstr "é¡µé¢ %s 匹é…了 html_sidebars 中的两æ¡è§„则:%r å’Œ %r"
#: sphinx/builders/html.py:1094
#, python-format
-msgid ""
-"a Unicode error occurred when rendering the page %s. Please make sure all "
-"config values that contain non-ASCII content are Unicode strings."
+msgid "a Unicode error occurred when rendering the page %s. Please make sure all config values that contain non-ASCII content are Unicode strings."
msgstr "æ¸²æŸ“é¡µé¢ %s æ—¶å‘生了 Unicode é”™è¯¯ã€‚è¯·ç¡®ä¿æ‰€æœ‰åŒ…å«éž ASCII 字符的é…置项是 Unicode 字符串。"
#: sphinx/builders/html.py:1099
@@ -872,10 +834,11 @@ msgstr "æ¸²æŸ“é¡µé¢ %s æ—¶å‘生了 Unicode é”™è¯¯ã€‚è¯·ç¡®ä¿æ‰€æœ‰åŒ…å«éž A
msgid ""
"An error happened in rendering the page %s.\n"
"Reason: %r"
-msgstr "æ¸²æŸ“é¡µé¢ %s æ—¶å‘生了错误。\n原因:%r"
+msgstr ""
+"æ¸²æŸ“é¡µé¢ %s æ—¶å‘生了错误。\n"
+"原因:%r"
-#: sphinx/builders/html.py:1111 sphinx/builders/text.py:85
-#: sphinx/builders/xml.py:99
+#: sphinx/builders/html.py:1111 sphinx/builders/text.py:85 sphinx/builders/xml.py:99
#, python-format
msgid "error writing file %s: %s"
msgstr "写入文件 %s æ—¶å‘生错误:%s"
@@ -932,8 +895,7 @@ msgstr "手册页ä¿å­˜åœ¨ %(outdir)s 目录。"
msgid "no \"man_pages\" config value found; no manual pages will be written"
msgstr "未找到“man_pagesâ€é…置项,ä¸ä¼šå†™å…¥æ‰‹å†Œé¡µ"
-#: sphinx/builders/latex/__init__.py:272 sphinx/builders/manpage.py:64
-#: sphinx/builders/singlehtml.py:174 sphinx/builders/texinfo.py:120
+#: sphinx/builders/latex/__init__.py:272 sphinx/builders/manpage.py:64 sphinx/builders/singlehtml.py:174 sphinx/builders/texinfo.py:120
msgid "writing"
msgstr "写作"
@@ -965,7 +927,10 @@ msgid ""
"\n"
"Run 'make' in that directory to run these through makeinfo\n"
"(use 'make info' here to do that automatically)."
-msgstr "\n在该目录下è¿è¡Œâ€œmakeâ€å‘½ä»¤ä»¥é€šè¿‡ makeinfo è¿è¡Œè¿™äº› Texinfo文件\n(在此处用“make infoâ€å³å¯è‡ªåŠ¨æ‰§è¡Œï¼‰ã€‚"
+msgstr ""
+"\n"
+"在该目录下è¿è¡Œâ€œmakeâ€å‘½ä»¤ä»¥é€šè¿‡ makeinfo è¿è¡Œè¿™äº› Texinfo文件\n"
+"(在此处用“make infoâ€å³å¯è‡ªåŠ¨æ‰§è¡Œï¼‰ã€‚"
#: sphinx/builders/texinfo.py:85
msgid "no \"texinfo_documents\" config value found; no documents will be written"
@@ -1023,7 +988,10 @@ msgid ""
"\n"
"Run 'make' in that directory to run these through (pdf)latex\n"
"(use `make latexpdf' here to do that automatically)."
-msgstr "\n在该目录下è¿è¡Œâ€œmakeâ€ä»¥é€šè¿‡ (pdf)latex è¿è¡Œè¿™äº› LaTex 文件\n(在此处用“make latexpdfâ€å³å¯è‡ªåŠ¨æ‰§è¡Œï¼‰ã€‚"
+msgstr ""
+"\n"
+"在该目录下è¿è¡Œâ€œmakeâ€ä»¥é€šè¿‡ (pdf)latex è¿è¡Œè¿™äº› LaTex 文件\n"
+"(在此处用“make latexpdfâ€å³å¯è‡ªåŠ¨æ‰§è¡Œï¼‰ã€‚"
#: sphinx/builders/latex/__init__.py:165
msgid "no \"latex_documents\" config value found; no documents will be written"
@@ -1034,14 +1002,9 @@ msgstr "未找到“latex_documentsâ€é…置项,ä¸ä¼šå†™å…¥æ–‡æ¡£"
msgid "\"latex_documents\" config value references unknown document %s"
msgstr "é…置项“latex_documentsâ€å¼•用了未知文档 %s"
-#: sphinx/builders/latex/__init__.py:213 sphinx/domains/std.py:501
-#: sphinx/templates/latex/latex.tex_t:79
-#: sphinx/themes/basic/genindex-single.html:30
-#: sphinx/themes/basic/genindex-single.html:55
-#: sphinx/themes/basic/genindex-split.html:11
-#: sphinx/themes/basic/genindex-split.html:14
-#: sphinx/themes/basic/genindex.html:30 sphinx/themes/basic/genindex.html:33
-#: sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:150
+#: sphinx/builders/latex/__init__.py:213 sphinx/domains/std.py:501 sphinx/templates/latex/latex.tex_t:79 sphinx/themes/basic/genindex-single.html:30
+#: sphinx/themes/basic/genindex-single.html:55 sphinx/themes/basic/genindex-split.html:11 sphinx/themes/basic/genindex-split.html:14
+#: sphinx/themes/basic/genindex.html:30 sphinx/themes/basic/genindex.html:33 sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:150
#: sphinx/writers/texinfo.py:522
msgid "Index"
msgstr "索引"
@@ -1090,9 +1053,7 @@ msgstr "ç¼–ç é”™è¯¯ï¼š"
#: sphinx/cmd/build.py:59 sphinx/cmd/build.py:74
#, python-format
-msgid ""
-"The full traceback has been saved in %s, if you want to report the issue to "
-"the developers."
+msgid "The full traceback has been saved in %s, if you want to report the issue to the developers."
msgstr "如果你想å‘å¼€å‘者报告问题,å¯ä»¥æŸ¥é˜…å·²ç»ä¿å­˜åœ¨ %s 的完整 Traceback ä¿¡æ¯ ã€‚"
#: sphinx/cmd/build.py:63
@@ -1100,10 +1061,7 @@ msgid "Recursion error:"
msgstr "递归错误:"
#: sphinx/cmd/build.py:66
-msgid ""
-"This can happen with very large or deeply nested source files. You can "
-"carefully increase the default Python recursion limit of 1000 in conf.py "
-"with e.g.:"
+msgid "This can happen with very large or deeply nested source files. You can carefully increase the default Python recursion limit of 1000 in conf.py with e.g.:"
msgstr "åœ¨æºæ–‡ä»¶è¿‡å¤§æˆ–嵌套层数过深时会出现此错误。你å¯ä»¥åœ¨ conf.py 中增大默认的Python 递归 1000 层é™åˆ¶ï¼Œåƒè¿™æ ·ï¼š"
#: sphinx/cmd/build.py:69
@@ -1115,23 +1073,18 @@ msgid "Exception occurred:"
msgstr "抛出异常:"
#: sphinx/cmd/build.py:77
-msgid ""
-"Please also report this if it was a user error, so that a better error "
-"message can be provided next time."
+msgid "Please also report this if it was a user error, so that a better error message can be provided next time."
msgstr "å¦‚æžœæ­¤å¤„æŠ›å‡ºäº†ç”¨æˆ·çš„é”™è¯¯ï¼Œä¹Ÿè¯·å‘æˆ‘们报告,这样以åŽå¯ä»¥æ˜¾ç¤ºæ›´å‹å¥½ã€æ›´è¯¦ç»†çš„错误信æ¯ã€‚"
#: sphinx/cmd/build.py:80
-msgid ""
-"A bug report can be filed in the tracker at <https://github.com/sphinx-"
-"doc/sphinx/issues>. Thanks!"
+msgid "A bug report can be filed in the tracker at <https://github.com/sphinx-doc/sphinx/issues>. Thanks!"
msgstr "è¯·å‘ Bug 追踪系统 <https://github.com/sphinx-doc/sphinx/issues> 投递 Bug 报告。谢谢ï¼"
#: sphinx/cmd/build.py:97
msgid "job number should be a positive number"
msgstr "工作编å·åº”为正值"
-#: sphinx/cmd/build.py:106 sphinx/cmd/quickstart.py:497
-#: sphinx/ext/apidoc.py:298 sphinx/ext/autosummary/generate.py:363
+#: sphinx/cmd/build.py:106 sphinx/cmd/quickstart.py:497 sphinx/ext/apidoc.py:298 sphinx/ext/autosummary/generate.py:363
msgid "For more information, visit <http://sphinx-doc.org/>."
msgstr "更多信æ¯è¯·è®¿é—® <http://sphinx-doc.org/>。"
@@ -1152,7 +1105,20 @@ msgid ""
"\n"
"By default, everything that is outdated is built. Output only for selected\n"
"files can be built by specifying individual filenames.\n"
-msgstr "\nä»Žæºæ–‡ä»¶ç”Ÿæˆæ–‡æ¡£ã€‚\n\nsphinx-build 从 SOURCEDIRÂ ä¸­çš„æ–‡ä»¶ç”Ÿæˆæ–‡æ¡£ï¼Œå¹¶ä¿å­˜åœ¨ OUTPUTDIR。\n它从 SOURCEDIR 的“conf.py†中读å–é…置。“sphinx-quickstartâ€å·¥å…·å¯ä»¥ç”Ÿ\næˆåŒ…括“conf.pyâ€åœ¨å†…çš„æ¨¡æ¿æ–‡ä»¶ã€‚\n\nsphinx-build å¯ä»¥ç”Ÿæˆå¤šç§æ ¼å¼çš„æ–‡æ¡£ã€‚在命令行中指定构建器åç§°å³å¯\n选择文档格å¼ï¼Œé»˜è®¤æ˜¯ HTML。构建器也å¯ä»¥æ‰§è¡Œæ–‡æ¡£å¤„ç†ç›¸å…³çš„å…¶ä»–\n任务。\n\n默认åªä¼šé‡æ–°æž„建过期内容。如果指定了文件å,那么åªä¼šäº§ç”Ÿè¿™äº›æ–‡ä»¶\n的输出。\n"
+msgstr ""
+"\n"
+"ä»Žæºæ–‡ä»¶ç”Ÿæˆæ–‡æ¡£ã€‚\n"
+"\n"
+"sphinx-build 从 SOURCEDIRÂ ä¸­çš„æ–‡ä»¶ç”Ÿæˆæ–‡æ¡£ï¼Œå¹¶ä¿å­˜åœ¨ OUTPUTDIR。\n"
+"它从 SOURCEDIR 的“conf.py†中读å–é…置。“sphinx-quickstartâ€å·¥å…·å¯ä»¥ç”Ÿ\n"
+"æˆåŒ…括“conf.pyâ€åœ¨å†…çš„æ¨¡æ¿æ–‡ä»¶ã€‚\n"
+"\n"
+"sphinx-build å¯ä»¥ç”Ÿæˆå¤šç§æ ¼å¼çš„æ–‡æ¡£ã€‚在命令行中指定构建器åç§°å³å¯\n"
+"选择文档格å¼ï¼Œé»˜è®¤æ˜¯ HTML。构建器也å¯ä»¥æ‰§è¡Œæ–‡æ¡£å¤„ç†ç›¸å…³çš„å…¶ä»–\n"
+"任务。\n"
+"\n"
+"默认åªä¼šé‡æ–°æž„建过期内容。如果指定了文件å,那么åªä¼šäº§ç”Ÿè¿™äº›æ–‡ä»¶\n"
+"的输出。\n"
#: sphinx/cmd/build.py:128
msgid "path to documentation source files"
@@ -1183,21 +1149,15 @@ msgid "don't use a saved environment, always read all files"
msgstr "ä¸ä½¿ç”¨å·²ä¿å­˜çš„环境,始终读å–全部文件"
#: sphinx/cmd/build.py:146
-msgid ""
-"path for the cached environment and doctree files (default: "
-"OUTPUTDIR/.doctrees)"
+msgid "path for the cached environment and doctree files (default: OUTPUTDIR/.doctrees)"
msgstr "缓存环境和 doctree 文件路径(默认:OUTPUTDIR/.doctrees)"
#: sphinx/cmd/build.py:149
-msgid ""
-"build in parallel with N processes where possible (special value \"auto\" "
-"will set N to cpu-count)"
+msgid "build in parallel with N processes where possible (special value \"auto\" will set N to cpu-count)"
msgstr "如果å¯èƒ½ï¼Œç”¨ N 个进程并行构建文档(如果指定为“autoâ€ï¼Œåˆ™ N 为 CPU æ•°é‡ï¼‰"
#: sphinx/cmd/build.py:153
-msgid ""
-"path where configuration file (conf.py) is located (default: same as "
-"SOURCEDIR)"
+msgid "path where configuration file (conf.py) is located (default: same as SOURCEDIR)"
msgstr "é…置文件(conf.py)所在目录路径(默认:与 SOURCEDIR 相åŒï¼‰"
#: sphinx/cmd/build.py:156
@@ -1348,9 +1308,7 @@ msgid "Please enter a file suffix, e.g. '.rst' or '.txt'."
msgstr "请输入文件åŽç¼€ï¼Œä¾‹å¦‚:“.rstâ€æˆ–者“.txtâ€ã€‚"
#: sphinx/cmd/quickstart.py:169
-msgid ""
-"* Note: non-ASCII characters entered and terminal encoding unknown -- "
-"assuming UTF-8 or Latin-1."
+msgid "* Note: non-ASCII characters entered and terminal encoding unknown -- assuming UTF-8 or Latin-1."
msgstr "* æç¤ºï¼šè¾“å…¥äº†éž ASCII å­—ç¬¦å¹¶ä¸”ç»ˆç«¯ç¼–ç æœªçŸ¥â€”—å‡å®šä¸º UTF-8 或 Latin-1。"
#: sphinx/cmd/quickstart.py:248
@@ -1363,20 +1321,27 @@ msgid ""
"\n"
"Please enter values for the following settings (just press Enter to\n"
"accept a default value, if one is given in brackets)."
-msgstr "\n请输入接下æ¥å„项设置的值(如果方括å·ä¸­æŒ‡å®šäº†é»˜è®¤å€¼ï¼Œç›´æŽ¥\n按回车å³å¯ä½¿ç”¨é»˜è®¤å€¼ï¼‰ã€‚"
+msgstr ""
+"\n"
+"请输入接下æ¥å„项设置的值(如果方括å·ä¸­æŒ‡å®šäº†é»˜è®¤å€¼ï¼Œç›´æŽ¥\n"
+"按回车å³å¯ä½¿ç”¨é»˜è®¤å€¼ï¼‰ã€‚"
#: sphinx/cmd/quickstart.py:254
#, python-format
msgid ""
"\n"
"Selected root path: %s"
-msgstr "\n已选择根路径:%s"
+msgstr ""
+"\n"
+"已选择根路径:%s"
#: sphinx/cmd/quickstart.py:257
msgid ""
"\n"
"Enter the root path for documentation."
-msgstr "\n输入文档的根路径。"
+msgstr ""
+"\n"
+"输入文档的根路径。"
#: sphinx/cmd/quickstart.py:259
msgid "Root path for the documentation"
@@ -1400,7 +1365,11 @@ msgid ""
"You have two options for placing the build directory for Sphinx output.\n"
"Either, you use a directory \"_build\" within the root path, or you separate\n"
"\"source\" and \"build\" directories within the root path."
-msgstr "\n布置用于ä¿å­˜ Sphinx 输出的构建目录,有两ç§é€‰æ‹©ã€‚\n一是在根路径下创建“_buildâ€ç›®å½•,二是在根路径下创建“sourceâ€\n和“buildâ€ä¸¤ä¸ªç‹¬ç«‹çš„目录。"
+msgstr ""
+"\n"
+"布置用于ä¿å­˜ Sphinx 输出的构建目录,有两ç§é€‰æ‹©ã€‚\n"
+"一是在根路径下创建“_buildâ€ç›®å½•,二是在根路径下创建“sourceâ€\n"
+"和“buildâ€ä¸¤ä¸ªç‹¬ç«‹çš„目录。"
#: sphinx/cmd/quickstart.py:278
msgid "Separate source and build directories (y/n)"
@@ -1412,7 +1381,11 @@ msgid ""
"Inside the root directory, two more directories will be created; \"_templates\"\n"
"for custom HTML templates and \"_static\" for custom stylesheets and other static\n"
"files. You can enter another prefix (such as \".\") to replace the underscore."
-msgstr "\n在根目录下,还会创建两个目录:“_templatesâ€ç”¨äºŽè‡ªå®šä¹‰ HTML 模æ¿ã€\n“_staticâ€ç”¨äºŽè‡ªå®šä¹‰ CSS å’Œå…¶ä»–é™æ€æ–‡ä»¶ã€‚ä½ å¯ä»¥è¾“入其他å‰ç¼€ï¼ˆæ¯”如“.â€ï¼‰\n代替默认的下划线。"
+msgstr ""
+"\n"
+"在根目录下,还会创建两个目录:“_templatesâ€ç”¨äºŽè‡ªå®šä¹‰ HTML 模æ¿ã€\n"
+"“_staticâ€ç”¨äºŽè‡ªå®šä¹‰ CSS å’Œå…¶ä»–é™æ€æ–‡ä»¶ã€‚ä½ å¯ä»¥è¾“入其他å‰ç¼€ï¼ˆæ¯”如“.â€ï¼‰\n"
+"代替默认的下划线。"
#: sphinx/cmd/quickstart.py:286
msgid "Name prefix for templates and static dir"
@@ -1422,7 +1395,9 @@ msgstr "模æ¿ç›®å½•åå’Œé™æ€ç›®å½•åçš„å‰ç¼€"
msgid ""
"\n"
"The project name will occur in several places in the built documentation."
-msgstr "\n项目å称会出现在文档的许多地方。"
+msgstr ""
+"\n"
+"项目å称会出现在文档的许多地方。"
#: sphinx/cmd/quickstart.py:291
msgid "Project name"
@@ -1440,7 +1415,12 @@ msgid ""
"Python the version is something like 2.5 or 3.0, while the release is\n"
"something like 2.5.1 or 3.0a1. If you don't need this dual structure,\n"
"just set both to the same value."
-msgstr "\n在 Sphinx 中,会区分“版本â€å’Œâ€œå‘行版本â€ä¸¤ä¸ªæ¦‚念。åŒä¸€ç‰ˆæœ¬å¯ä»¥\n有多个å‘行版本。例如,Python 版本å¯ä»¥æ˜¯ 2.5 或 3.0,而å‘行版\n本则是 2.5.1 或 3.0a1。如果你ä¸éœ€è¦è¿™æ ·çš„åŒé‡ç‰ˆæœ¬ç»“构,请把这\n两个选项设置为相åŒå€¼ã€‚"
+msgstr ""
+"\n"
+"在 Sphinx 中,会区分“版本â€å’Œâ€œå‘行版本â€ä¸¤ä¸ªæ¦‚念。åŒä¸€ç‰ˆæœ¬å¯ä»¥\n"
+"有多个å‘行版本。例如,Python 版本å¯ä»¥æ˜¯ 2.5 或 3.0,而å‘行版\n"
+"本则是 2.5.1 或 3.0a1。如果你ä¸éœ€è¦è¿™æ ·çš„åŒé‡ç‰ˆæœ¬ç»“构,请把这\n"
+"两个选项设置为相åŒå€¼ã€‚"
#: sphinx/cmd/quickstart.py:302
msgid "Project version"
@@ -1470,7 +1450,10 @@ msgid ""
"\n"
"The file name suffix for source files. Commonly, this is either \".txt\"\n"
"or \".rst\". Only files with this suffix are considered documents."
-msgstr "\næºæ–‡ä»¶çš„æ–‡ä»¶ååŽç¼€ã€‚一般是“.txtâ€æˆ–“.rstâ€ã€‚åªæœ‰æ­¤åŽç¼€çš„æ–‡ä»¶æ‰ä¼š\nè¢«è§†ä¸ºæ–‡æ¡£çš„æºæ–‡ä»¶ã€‚"
+msgstr ""
+"\n"
+"æºæ–‡ä»¶çš„æ–‡ä»¶ååŽç¼€ã€‚一般是“.txtâ€æˆ–“.rstâ€ã€‚åªæœ‰æ­¤åŽç¼€çš„æ–‡ä»¶æ‰ä¼š\n"
+"è¢«è§†ä¸ºæ–‡æ¡£çš„æºæ–‡ä»¶ã€‚"
#: sphinx/cmd/quickstart.py:322
msgid "Source file suffix"
@@ -1483,7 +1466,11 @@ msgid ""
"\"contents tree\", that is, it is the root of the hierarchical structure\n"
"of the documents. Normally, this is \"index\", but if your \"index\"\n"
"document is a custom template, you can also set this to another filename."
-msgstr "\n有一个特殊的文档将被视为“目录树â€çš„æ ‘é¡¶èŠ‚ç‚¹ï¼Œä¹Ÿå³æ˜¯æ–‡æ¡£å±‚级\n结构的根。一般用“indexâ€ä½œä¸ºè¿™ä¸ªç‰¹æ®Šæ–‡æ¡£ï¼Œå¦‚果你的“indexâ€æ–‡\n档使用了自定义模æ¿ï¼Œä½ ä¹Ÿå¯ä»¥æŒ‡å®šå…¶ä»–的文件å。"
+msgstr ""
+"\n"
+"有一个特殊的文档将被视为“目录树â€çš„æ ‘é¡¶èŠ‚ç‚¹ï¼Œä¹Ÿå³æ˜¯æ–‡æ¡£å±‚级\n"
+"结构的根。一般用“indexâ€ä½œä¸ºè¿™ä¸ªç‰¹æ®Šæ–‡æ¡£ï¼Œå¦‚果你的“indexâ€æ–‡\n"
+"档使用了自定义模æ¿ï¼Œä½ ä¹Ÿå¯ä»¥æŒ‡å®šå…¶ä»–的文件å。"
#: sphinx/cmd/quickstart.py:330
msgid "Name of your master document (without suffix)"
@@ -1491,8 +1478,7 @@ msgstr "主文档文件å(ä¸å«åŽç¼€ï¼‰"
#: sphinx/cmd/quickstart.py:336
#, python-format
-msgid ""
-"Error: the master file %s has already been found in the selected root path."
+msgid "Error: the master file %s has already been found in the selected root path."
msgstr "错误:选择的根目录下已存在主文档文件 %s。"
#: sphinx/cmd/quickstart.py:338
@@ -1500,8 +1486,7 @@ msgid "sphinx-quickstart will not overwrite the existing file."
msgstr "sphinx-quickstart ä¸ä¼šè¦†ç›–已有的文件。"
#: sphinx/cmd/quickstart.py:340
-msgid ""
-"Please enter a new file name, or rename the existing file and press Enter"
+msgid "Please enter a new file name, or rename the existing file and press Enter"
msgstr "请输入新文件å,若è¦é‡å‘½å现有文件请按回车"
#: sphinx/cmd/quickstart.py:344
@@ -1509,9 +1494,7 @@ msgid "Indicate which of the following Sphinx extensions should be enabled:"
msgstr "å¯ç”¨ Sphinx 扩展:"
#: sphinx/cmd/quickstart.py:353
-msgid ""
-"Note: imgmath and mathjax cannot be enabled at the same time. imgmath has "
-"been deselected."
+msgid "Note: imgmath and mathjax cannot be enabled at the same time. imgmath has been deselected."
msgstr "注æ„:imgmath å’Œ mathjax ä¸èƒ½åŒæ—¶å¯ç”¨ã€‚已喿¶ˆé€‰æ‹© imgmath。"
#: sphinx/cmd/quickstart.py:358
@@ -1520,7 +1503,10 @@ msgid ""
"A Makefile and a Windows command file can be generated for you so that you\n"
"only have to run e.g. `make html' instead of invoking sphinx-build\n"
"directly."
-msgstr "\nç”Ÿæˆ Makefile å’Œ Windows æ‰¹å¤„ç†æ–‡ä»¶ï¼Œå¯ä»¥ç›´æŽ¥åƒâ€œmake htmlâ€è¿™æ ·\nè¿è¡Œï¼Œè€Œä¸éœ€è¦ç›´æŽ¥è°ƒç”¨ sphinx-build。"
+msgstr ""
+"\n"
+"ç”Ÿæˆ Makefile å’Œ Windows æ‰¹å¤„ç†æ–‡ä»¶ï¼Œå¯ä»¥ç›´æŽ¥åƒâ€œmake htmlâ€è¿™æ ·\n"
+"è¿è¡Œï¼Œè€Œä¸éœ€è¦ç›´æŽ¥è°ƒç”¨ sphinx-build。"
#: sphinx/cmd/quickstart.py:362
msgid "Create Makefile? (y/n)"
@@ -1556,19 +1542,21 @@ msgstr "\n你现在å¯ä»¥å¡«å†™ä¸»æ–‡æ¡£æ–‡ä»¶ %s å¹¶åˆ›å»ºå…¶ä»–æ–‡æ¡£æºæ–‡ä»¶
msgid ""
"Use the Makefile to build the docs, like so:\n"
" make builder\n"
-msgstr "用 Makefile 构建文档,åƒè¿™æ ·ï¼š\n make builder\n"
+msgstr ""
+"用 Makefile 构建文档,åƒè¿™æ ·ï¼š\n"
+" make builder\n"
#: sphinx/cmd/quickstart.py:455
#, python-format
msgid ""
"Use the sphinx-build command to build the docs, like so:\n"
" sphinx-build -b builder %s %s\n"
-msgstr "用 sphinx-build 命令构建文档,åƒè¿™æ ·ï¼š\n sphinx-build -b builder %s %s\n"
+msgstr ""
+"用 sphinx-build 命令构建文档,åƒè¿™æ ·ï¼š\n"
+" sphinx-build -b builder %s %s\n"
#: sphinx/cmd/quickstart.py:458
-msgid ""
-"where \"builder\" is one of the supported builders, e.g. html, latex or "
-"linkcheck.\n"
+msgid "where \"builder\" is one of the supported builders, e.g. html, latex or linkcheck.\n"
msgstr "此处的“builderâ€æ˜¯æ”¯æŒçš„æž„建器å,比如 htmlã€latex 或 linkcheck。\n"
#: sphinx/cmd/quickstart.py:498
@@ -1579,7 +1567,12 @@ msgid ""
"sphinx-quickstart is an interactive tool that asks some questions about your\n"
"project and then generates a complete documentation directory and sample\n"
"Makefile to be used with sphinx-build.\n"
-msgstr "\nç”Ÿæˆ Sphinx 项目的必需文件。\n\nsphinx-quickstart 是一个交互å¼å·¥å…·ï¼Œè¯¢é—®ä¸€äº›å…³äºŽé¡¹ç›®çš„问题,生æˆ\n完整的文档目录和用于 sphinx-build 的示例 Makefile。\n"
+msgstr ""
+"\n"
+"ç”Ÿæˆ Sphinx 项目的必需文件。\n"
+"\n"
+"sphinx-quickstart 是一个交互å¼å·¥å…·ï¼Œè¯¢é—®ä¸€äº›å…³äºŽé¡¹ç›®çš„问题,生æˆ\n"
+"完整的文档目录和用于 sphinx-build 的示例 Makefile。\n"
#: sphinx/cmd/quickstart.py:508
msgid "quiet mode"
@@ -1695,14 +1688,11 @@ msgid "\"quiet\" is specified, but any of \"project\" or \"author\" is not speci
msgstr "指定了“quietâ€ï¼Œä½†æ˜¯æ²¡æœ‰æŒ‡å®šâ€œprojectâ€å’Œâ€œauthorâ€ã€‚"
#: sphinx/cmd/quickstart.py:618
-msgid ""
-"Error: specified path is not a directory, or sphinx files already exist."
+msgid "Error: specified path is not a directory, or sphinx files already exist."
msgstr "é”™è¯¯ï¼šæŒ‡å®šçš„è·¯å¾„ä¸æ˜¯ä¸€ä¸ªç›®å½•,或是 Sphinx 文件已存在。"
#: sphinx/cmd/quickstart.py:620
-msgid ""
-"sphinx-quickstart only generate into a empty directory. Please specify a new"
-" root path."
+msgid "sphinx-quickstart only generate into a empty directory. Please specify a new root path."
msgstr "sphinx-quickstart åªä¼šåœ¨ç©ºç›®å½•ä¸­ç”Ÿæˆæ–‡ä»¶ã€‚请指定一个新的根路径。"
#: sphinx/cmd/quickstart.py:635
@@ -1719,8 +1709,7 @@ msgstr "检测到过度的去缩进"
msgid "Invalid caption: %s"
msgstr "无效的标题:%s"
-#: sphinx/directives/code.py:140 sphinx/directives/code.py:292
-#: sphinx/directives/code.py:462
+#: sphinx/directives/code.py:140 sphinx/directives/code.py:292 sphinx/directives/code.py:462
#, python-format
msgid "line number spec is out of range(1-%d): %r"
msgstr "指定的行å·è¶…出范围(1-%d):%r"
@@ -1777,18 +1766,15 @@ msgstr "作者: "
msgid "%s %s"
msgstr "%s %s"
-#: sphinx/domains/c.py:64 sphinx/domains/cpp.py:6445
-#: sphinx/domains/python.py:210 sphinx/ext/napoleon/docstring.py:696
+#: sphinx/domains/c.py:64 sphinx/domains/cpp.py:6445 sphinx/domains/python.py:210 sphinx/ext/napoleon/docstring.py:696
msgid "Parameters"
msgstr "傿•°"
-#: sphinx/domains/c.py:67 sphinx/domains/cpp.py:6454
-#: sphinx/domains/javascript.py:209 sphinx/domains/python.py:222
+#: sphinx/domains/c.py:67 sphinx/domains/cpp.py:6454 sphinx/domains/javascript.py:209 sphinx/domains/python.py:222
msgid "Returns"
msgstr "返回"
-#: sphinx/domains/c.py:69 sphinx/domains/javascript.py:211
-#: sphinx/domains/python.py:224
+#: sphinx/domains/c.py:69 sphinx/domains/javascript.py:211 sphinx/domains/python.py:224
msgid "Return type"
msgstr "返回类型"
@@ -1817,8 +1803,7 @@ msgstr "%s (C 类型)"
msgid "%s (C variable)"
msgstr "%s (C å˜é‡)"
-#: sphinx/domains/c.py:258 sphinx/domains/cpp.py:7029
-#: sphinx/domains/javascript.py:297 sphinx/domains/python.py:742
+#: sphinx/domains/c.py:258 sphinx/domains/cpp.py:7029 sphinx/domains/javascript.py:297 sphinx/domains/python.py:742
msgid "function"
msgstr "函数"
@@ -1858,7 +1843,9 @@ msgstr "%s 版åŽå·²ç§»é™¤"
msgid ""
"Duplicate declaration, also defined in '%s'.\n"
"Declaration is '%s'."
-msgstr "é‡å¤çš„声明,已ç»åœ¨â€œ%sâ€å¤„定义。\n定义为“%sâ€ã€‚"
+msgstr ""
+"é‡å¤çš„声明,已ç»åœ¨â€œ%sâ€å¤„定义。\n"
+"定义为“%sâ€ã€‚"
#: sphinx/domains/cpp.py:6448
msgid "Template Parameters"
@@ -1873,8 +1860,7 @@ msgstr "抛出"
msgid "%s (C++ %s)"
msgstr "%s (C++ %s)"
-#: sphinx/domains/cpp.py:7027 sphinx/domains/javascript.py:299
-#: sphinx/domains/python.py:744
+#: sphinx/domains/cpp.py:7027 sphinx/domains/javascript.py:299 sphinx/domains/python.py:744
msgid "class"
msgstr "ç±»"
@@ -1899,7 +1885,9 @@ msgstr "枚举å­"
msgid ""
"Duplicate declaration, also defined in '%s'.\n"
"Name of declaration is '%s'."
-msgstr "é‡å¤çš„声明,已ç»åœ¨â€œ%sâ€å¤„定义。\n声明å称为“%sâ€ã€‚"
+msgstr ""
+"é‡å¤çš„声明,已ç»åœ¨â€œ%sâ€å¤„定义。\n"
+"声明å称为“%sâ€ã€‚"
#: sphinx/domains/javascript.py:130 sphinx/domains/python.py:430
#, python-format
@@ -1947,8 +1935,7 @@ msgstr "æ•°æ®"
msgid "attribute"
msgstr "属性"
-#: sphinx/domains/javascript.py:302 sphinx/domains/python.py:49
-#: sphinx/domains/python.py:750
+#: sphinx/domains/javascript.py:302 sphinx/domains/python.py:49 sphinx/domains/python.py:750
msgid "module"
msgstr "模å—"
@@ -1994,8 +1981,7 @@ msgstr "å˜é‡"
msgid "Raises"
msgstr "引å‘"
-#: sphinx/domains/python.py:431 sphinx/domains/python.py:489
-#: sphinx/domains/python.py:501 sphinx/domains/python.py:514
+#: sphinx/domains/python.py:431 sphinx/domains/python.py:489 sphinx/domains/python.py:501 sphinx/domains/python.py:514
#, python-format
msgid "%s() (in module %s)"
msgstr "%s() (在 %s 模å—中)"
@@ -2104,9 +2090,7 @@ msgstr "环境å˜é‡; %s"
#: sphinx/domains/std.py:166
#, python-format
-msgid ""
-"Malformed option description %r, should look like \"opt\", \"-opt args\", \""
-"--opt args\", \"/opt args\" or \"+opt args\""
+msgid "Malformed option description %r, should look like \"opt\", \"-opt args\", \"--opt args\", \"/opt args\" or \"+opt args\""
msgstr "畸形的选项æè¿° %r,应是“optâ€ã€â€œ-opt argsâ€ã€â€œ--opt argsâ€ã€â€œ/opt argsâ€æˆ–“+opt argsâ€å½¢å¼"
#: sphinx/domains/std.py:207
@@ -2206,9 +2190,7 @@ msgid "source directory has changed"
msgstr "æºæ–‡ä»¶ç›®å½•å·²å˜åŒ–"
#: sphinx/environment/__init__.py:283
-msgid ""
-"This environment is incompatible with the selected builder, please choose "
-"another doctree directory."
+msgid "This environment is incompatible with the selected builder, please choose another doctree directory."
msgstr "本环境与选择的构建器ä¸å…¼å®¹ï¼Œè¯·é€‰æ‹©å…¶ä»–的文档树目录。"
#: sphinx/environment/__init__.py:404
@@ -2244,8 +2226,7 @@ msgstr "å‚è§ %s"
msgid "unknown index entry type %r"
msgstr "未知的索引æ¡ç›®ç±»åž‹ %r"
-#: sphinx/environment/adapters/indexentries.py:156
-#: sphinx/templates/latex/sphinxmessages.sty_t:11
+#: sphinx/environment/adapters/indexentries.py:156 sphinx/templates/latex/sphinxmessages.sty_t:11
msgid "Symbols"
msgstr "符å·"
@@ -2256,9 +2237,7 @@ msgstr "在文档树中检测到循环引用,已忽略:%s <- %s"
#: sphinx/environment/adapters/toctree.py:172
#, python-format
-msgid ""
-"toctree contains reference to document %r that doesn't have a title: no link"
-" will be generated"
+msgid "toctree contains reference to document %r that doesn't have a title: no link will be generated"
msgstr "目录树引用的文档 %r 缺少标题:ä¸ä¼šç”Ÿæˆé“¾æŽ¥"
#: sphinx/environment/adapters/toctree.py:178
@@ -2306,15 +2285,21 @@ msgid ""
"excluded from generation.\n"
"\n"
"Note: By default this script will not overwrite already created files."
-msgstr "\n在 <MODULE_PATH> 中递归查找 Python 模å—和包,然åŽåœ¨ <OUTPUT_PATH> 中为æ¯ä¸ªä½¿ç”¨äº†\nautomodule 指令的包创建一个 reST 文件。\n\n<EXCLUDE_PATTERN> å¯ä»¥æŽ’除生æˆç¬¦åˆè§„则的文件/目录的文档。\n\næç¤ºï¼šæœ¬è„šæœ¬é»˜è®¤ä¸ä¼šè¦†ç›–已有文件。"
+msgstr ""
+"\n"
+"在 <MODULE_PATH> 中递归查找 Python 模å—和包,然åŽåœ¨ <OUTPUT_PATH> 中为æ¯ä¸ªä½¿ç”¨äº†\n"
+"automodule 指令的包创建一个 reST 文件。\n"
+"\n"
+"<EXCLUDE_PATTERN> å¯ä»¥æŽ’除生æˆç¬¦åˆè§„则的文件/目录的文档。\n"
+"\n"
+"æç¤ºï¼šæœ¬è„šæœ¬é»˜è®¤ä¸ä¼šè¦†ç›–已有文件。"
#: sphinx/ext/apidoc.py:312
msgid "path to module to document"
msgstr "è¦ç”Ÿæˆæ–‡æ¡£çš„æ¨¡å—路径"
#: sphinx/ext/apidoc.py:314
-msgid ""
-"fnmatch-style file and/or directory patterns to exclude from generation"
+msgid "fnmatch-style file and/or directory patterns to exclude from generation"
msgstr "排除的文件/目录,fnmatch 风格的规则"
#: sphinx/ext/apidoc.py:319
@@ -2330,9 +2315,7 @@ msgid "overwrite existing files"
msgstr "覆盖已有文件"
#: sphinx/ext/apidoc.py:328
-msgid ""
-"follow symbolic links. Powerful when combined with "
-"collective.recipe.omelette."
+msgid "follow symbolic links. Powerful when combined with collective.recipe.omelette."
msgstr "éµå¾ªç¬¦å·é“¾æŽ¥ã€‚é…åˆ collective.recipe.omelette ä½¿ç”¨å°¤å…¶å¥æ•ˆã€‚"
#: sphinx/ext/apidoc.py:331
@@ -2356,9 +2339,7 @@ msgid "don't create a table of contents file"
msgstr "ä¸åˆ›å»ºç›®å½•文件"
#: sphinx/ext/apidoc.py:344
-msgid ""
-"don't create headings for the module/package packages (e.g. when the "
-"docstrings already contain them)"
+msgid "don't create headings for the module/package packages (e.g. when the docstrings already contain them)"
msgstr "ä¸åˆ›å»ºæ¨¡å—/包的标题(比如当 Docstring ä¸­å·²ç»æœ‰æ ‡é¢˜æ—¶ï¼Œå¯ä»¥ä½¿ç”¨è¿™ä¸ªé€‰é¡¹ï¼‰"
#: sphinx/ext/apidoc.py:349
@@ -2366,9 +2347,7 @@ msgid "put module documentation before submodule documentation"
msgstr "æ¨¡å—æ–‡æ¡£å…ˆäºŽå­æ¨¡å—文档"
#: sphinx/ext/apidoc.py:353
-msgid ""
-"interpret module paths according to PEP-0420 implicit namespaces "
-"specification"
+msgid "interpret module paths according to PEP-0420 implicit namespaces specification"
msgstr "æ ¹æ® PEP-0420 éšå¼å‘½å空间规范解释模å—路径"
#: sphinx/ext/apidoc.py:357
@@ -2415,9 +2394,7 @@ msgstr "æ— æ•ˆçš„æ­£åˆ™è¡¨è¾¾å¼ %r 在 %s"
#: sphinx/ext/coverage.py:55
#, python-format
-msgid ""
-"Testing of coverage in the sources finished, look at the results in "
-"%(outdir)spython.txt."
+msgid "Testing of coverage in the sources finished, look at the results in %(outdir)spython.txt."
msgstr "å·²å®Œæˆæºæ–‡ä»¶çš„覆盖率测试,请在 %(outdir)s/python.txt 中查看结果。"
#: sphinx/ext/coverage.py:70
@@ -2451,9 +2428,7 @@ msgstr "无效的 TestCode 类型"
#: sphinx/ext/doctest.py:283
#, python-format
-msgid ""
-"Testing of doctests in the sources finished, look at the results in "
-"%(outdir)s/output.txt."
+msgid "Testing of doctests in the sources finished, look at the results in %(outdir)s/output.txt."
msgstr "å·²å®Œæˆæºæ–‡ä»¶çš„æ–‡æ¡£æµ‹è¯•,请在 %(outdir)s/output.txt 中查看结果。"
#: sphinx/ext/doctest.py:446
@@ -2491,9 +2466,7 @@ msgstr "dot没有生æˆè¾“出文件:\n[stderr]\n%r\n[stdout]\n%r"
#: sphinx/ext/graphviz.py:254
#, python-format
-msgid ""
-"dot command %r cannot be run (needed for graphviz output), check the "
-"graphviz_dot setting"
+msgid "dot command %r cannot be run (needed for graphviz output), check the graphviz_dot setting"
msgstr "无法è¿è¡Œ Dot 命令 %r (Graphviz 输出所需),请检查 graphviz_dot é…ç½®"
#: sphinx/ext/graphviz.py:261
@@ -2511,8 +2484,7 @@ msgstr "点退出错误:\n[stderr]\n%r\n[stdout]\n%r"
msgid "graphviz_output_format must be one of 'png', 'svg', but is %r"
msgstr "graphviz_output_format 必须是 'png' 或 'svg' 中之一,现为 %r"
-#: sphinx/ext/graphviz.py:275 sphinx/ext/graphviz.py:329
-#: sphinx/ext/graphviz.py:367
+#: sphinx/ext/graphviz.py:275 sphinx/ext/graphviz.py:329 sphinx/ext/graphviz.py:367
#, python-format
msgid "dot code %r: %s"
msgstr "点 ä»£ç  %r: %s"
@@ -2543,16 +2515,12 @@ msgstr "转æ¢é€€å‡ºæ—¶å‡ºé”™:\n[stderr]\n%r\n[stdout]\n%r"
#: sphinx/ext/imgmath.py:139
#, python-format
-msgid ""
-"LaTeX command %r cannot be run (needed for math display), check the "
-"imgmath_latex setting"
+msgid "LaTeX command %r cannot be run (needed for math display), check the imgmath_latex setting"
msgstr "无法è¿è¡Œ LaTeX 命令 %r ï¼ˆæ•°å­¦å…¬å¼æ˜¾ç¤ºå¿…需),请检查 imgmath_latex 设置"
#: sphinx/ext/imgmath.py:154
#, python-format
-msgid ""
-"%s command %r cannot be run (needed for math display), check the imgmath_%s "
-"setting"
+msgid "%s command %r cannot be run (needed for math display), check the imgmath_%s setting"
msgstr "无法è¿è¡Œ %s 命令 %r ï¼ˆæ•°å­¦å…¬å¼æ˜¾ç¤ºå¿…需),请检查 imgmath_%s 设置"
#: sphinx/ext/imgmath.py:289
@@ -2680,14 +2648,15 @@ msgstr "属性 %s ä¸å­˜åœ¨ï¼Œåœ¨å¯¹è±¡ %s 上"
msgid ""
"autodoc: failed to determine %r to be documented.the following exception was raised:\n"
"%s"
-msgstr "autodoc:无法判断是å¦ç”Ÿæˆ %r 的文档。抛出了下列异常:\n%s"
+msgstr ""
+"autodoc:无法判断是å¦ç”Ÿæˆ %r 的文档。抛出了下列异常:\n"
+"%s"
#: sphinx/ext/autodoc/__init__.py:692
#, python-format
msgid ""
-"don't know which module to import for autodocumenting %r (try placing a "
-"\"module\" or \"currentmodule\" directive in the document, or giving an "
-"explicit module name)"
+"don't know which module to import for autodocumenting %r (try placing a \"module\" or \"currentmodule\" directive in the document, or giving an explicit module "
+"name)"
msgstr "æ— æ³•åˆ¤æ–­å¯¼å…¥å“ªä¸ªæ¨¡å—æ¥è‡ªåŠ¨ç”Ÿæˆæ–‡æ¡£ %r(å°è¯•在文档中使用“moduleâ€æˆ–“currentmoduleâ€æŒ‡ä»¤ï¼Œæˆ–者显å¼ç»™å®šæ¨¡å—å)"
#: sphinx/ext/autodoc/__init__.py:786
@@ -2701,15 +2670,12 @@ msgstr "automodule %s 给定了函数签å傿•°æˆ–返回类型标注"
#: sphinx/ext/autodoc/__init__.py:827
#, python-format
-msgid ""
-"__all__ should be a list of strings, not %r (in module %s) -- ignoring "
-"__all__"
+msgid "__all__ should be a list of strings, not %r (in module %s) -- ignoring __all__"
msgstr "__all__ åº”æ˜¯ä¸€ä¸ªå­—ç¬¦ä¸²åˆ—è¡¨ï¼Œè€Œä¸æ˜¯ %r ï¼ˆå‡ºçŽ°åœ¨æ¨¡å— %s 中) -- 已忽略__all__"
#: sphinx/ext/autodoc/__init__.py:842
#, python-format
-msgid ""
-"missing attribute mentioned in :members: or __all__: module %s, attribute %s"
+msgid "missing attribute mentioned in :members: or __all__: module %s, attribute %s"
msgstr ":members: 或 __all__ æåŠçš„属性ä¸å­˜åœ¨ï¼šæ¨¡å— %s,属性 %s"
#: sphinx/ext/autodoc/__init__.py:1126
@@ -2753,9 +2719,7 @@ msgid "failed to import object %s"
msgstr "无法导入对象 %s"
#: sphinx/ext/autosummary/__init__.py:702
-msgid ""
-"autosummary generats .rst files internally. But your source_suffix does not "
-"contain .rst. Skipped."
+msgid "autosummary generats .rst files internally. But your source_suffix does not contain .rst. Skipped."
msgstr "autosummary å†…éƒ¨ç”Ÿæˆ .rst 文件,但是 source_suffix 中ä¸åŒ…å« .rst,已跳过。"
#: sphinx/ext/autosummary/generate.py:100
@@ -2781,7 +2745,17 @@ msgid ""
"``sphinx.ext.autosummary`` Python module and can be read using::\n"
"\n"
" pydoc sphinx.ext.autosummary\n"
-msgstr "\n用 autosummary æŒ‡ä»¤ç”Ÿæˆ ReStructuredText\n\nsphinx-autogen 是 sphinx.ext.autosummary.generate çš„å‰ç«¯ï¼Œå®ƒæ ¹æ®ç»™å®š\n的输入文件中的 autosummary æŒ‡ä»¤ç”Ÿæˆ reStructuredText  文件\n\nautosummary 指令的格å¼è§ Python æ¨¡å— ``sphinx.ext.autosummary`` 的文\n档,并且å¯ä»¥è¿™æ ·è°ƒå‡ºæ–‡æ¡£é˜…读::\n\n pydoc sphinx.ext.autosummary\n"
+msgstr ""
+"\n"
+"用 autosummary æŒ‡ä»¤ç”Ÿæˆ ReStructuredText\n"
+"\n"
+"sphinx-autogen 是 sphinx.ext.autosummary.generate çš„å‰ç«¯ï¼Œå®ƒæ ¹æ®ç»™å®š\n"
+"的输入文件中的 autosummary æŒ‡ä»¤ç”Ÿæˆ reStructuredText  文件\n"
+"\n"
+"autosummary 指令的格å¼è§ Python æ¨¡å— ``sphinx.ext.autosummary`` 的文\n"
+"档,并且å¯ä»¥è¿™æ ·è°ƒå‡ºæ–‡æ¡£é˜…读::\n"
+"\n"
+" pydoc sphinx.ext.autosummary\n"
#: sphinx/ext/autosummary/generate.py:381
msgid "source files to generate rST files for"
@@ -2878,8 +2852,7 @@ msgstr "尿Ѐ巧"
msgid "Warning"
msgstr "警告"
-#: sphinx/templates/latex/longtable.tex_t:23
-#: sphinx/templates/latex/sphinxmessages.sty_t:8
+#: sphinx/templates/latex/longtable.tex_t:23 sphinx/templates/latex/sphinxmessages.sty_t:8
msgid "continued from previous page"
msgstr "续上页"
@@ -2903,13 +2876,11 @@ msgstr "数值"
msgid "page"
msgstr "页"
-#: sphinx/themes/agogo/layout.html:46 sphinx/themes/basic/globaltoc.html:10
-#: sphinx/themes/basic/localtoc.html:11 sphinx/themes/scrolls/layout.html:41
+#: sphinx/themes/agogo/layout.html:46 sphinx/themes/basic/globaltoc.html:10 sphinx/themes/basic/localtoc.html:11 sphinx/themes/scrolls/layout.html:41
msgid "Table of Contents"
msgstr "目录"
-#: sphinx/themes/agogo/layout.html:51 sphinx/themes/basic/layout.html:153
-#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:21
+#: sphinx/themes/agogo/layout.html:51 sphinx/themes/basic/layout.html:153 sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:21
#: sphinx/themes/basic/searchresults.html:10
msgid "Search"
msgstr "æœç´¢"
@@ -2971,9 +2942,7 @@ msgstr "所的函数,类,术语"
msgid "Index &ndash; %(key)s"
msgstr "索引 &ndash; %(key)s"
-#: sphinx/themes/basic/genindex-single.html:61
-#: sphinx/themes/basic/genindex-split.html:24
-#: sphinx/themes/basic/genindex-split.html:38
+#: sphinx/themes/basic/genindex-single.html:61 sphinx/themes/basic/genindex-split.html:24 sphinx/themes/basic/genindex-split.html:38
#: sphinx/themes/basic/genindex.html:72
msgid "Full index on one page"
msgstr "一页的全部索引"
@@ -3020,9 +2989,7 @@ msgstr "æœ€åŽæ›´æ–°äºŽ %(last_updated)s."
#: sphinx/themes/basic/layout.html:210
#, python-format
-msgid ""
-"Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> "
-"%(sphinx_version)s."
+msgid "Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s."
msgstr "由 <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s 创建。"
#: sphinx/themes/basic/opensearch.xml:4
@@ -3060,23 +3027,16 @@ msgid ""
" containing fewer words won't appear in the result list."
msgstr "在这儿,你å¯ä»¥å¯¹è¿™äº›æ–‡æ¡£è¿›è¡Œæœç´¢ã€‚呿œç´¢æ¡†ä¸­è¾“å…¥ä½ æ‰€è¦æœç´¢çš„关键字并点击“æœç´¢â€ã€‚注æ„:æœç´¢å¼•擎会自动æœç´¢æ‰€æœ‰çš„关键字。将ä¸ä¼šæœç´¢åˆ°éƒ¨åˆ†å…³é”®å­—的页é¢."
-#: sphinx/themes/basic/search.html:37
-#: sphinx/themes/basic/searchresults.html:17
+#: sphinx/themes/basic/search.html:37 sphinx/themes/basic/searchresults.html:17
msgid "search"
msgstr "æœç´¢"
-#: sphinx/themes/basic/search.html:41
-#: sphinx/themes/basic/searchresults.html:21
-#: sphinx/themes/basic/static/searchtools.js:285
+#: sphinx/themes/basic/search.html:41 sphinx/themes/basic/searchresults.html:21 sphinx/themes/basic/static/searchtools.js:285
msgid "Search Results"
msgstr "æœç´¢ç»“æžœ"
-#: sphinx/themes/basic/search.html:43
-#: sphinx/themes/basic/searchresults.html:23
-#: sphinx/themes/basic/static/searchtools.js:287
-msgid ""
-"Your search did not match any documents. Please make sure that all words are"
-" spelled correctly and that you've selected enough categories."
+#: sphinx/themes/basic/search.html:43 sphinx/themes/basic/searchresults.html:23 sphinx/themes/basic/static/searchtools.js:287
+msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
msgstr "æ²¡æœ‰ä»»ä½•æ–‡æ¡£åŒ¹é…æ‚¨çš„æœç´¢ã€‚è¯·ç¡®ä¿ä½ è¾“å…¥çš„è¯æ‹¼å†™æ­£ç¡®å¹¶é€‰æ‹©äº†åˆé€‚的分类。"
#: sphinx/themes/basic/searchbox.html:12
@@ -3087,8 +3047,7 @@ msgstr "快速æœç´¢"
msgid "This Page"
msgstr "本页"
-#: sphinx/themes/basic/changes/frameset.html:5
-#: sphinx/themes/basic/changes/versionchanges.html:12
+#: sphinx/themes/basic/changes/frameset.html:5 sphinx/themes/basic/changes/versionchanges.html:12
#, python-format
msgid "Changes in Version %(version)s &#8212; %(docstitle)s"
msgstr "更改å‘生在版本 %(version)s&#8212; %(docstitle)s"
@@ -3115,15 +3074,11 @@ msgstr "C API 更改"
msgid "Other changes"
msgstr "其他更改"
-#: sphinx/themes/basic/static/doctools.js:194 sphinx/writers/html.py:454
-#: sphinx/writers/html.py:459 sphinx/writers/html5.py:400
-#: sphinx/writers/html5.py:405
+#: sphinx/themes/basic/static/doctools.js:194 sphinx/writers/html.py:454 sphinx/writers/html.py:459 sphinx/writers/html5.py:400 sphinx/writers/html5.py:405
msgid "Permalink to this headline"
msgstr "永久链接至标题"
-#: sphinx/themes/basic/static/doctools.js:200 sphinx/writers/html.py:134
-#: sphinx/writers/html.py:145 sphinx/writers/html5.py:103
-#: sphinx/writers/html5.py:114
+#: sphinx/themes/basic/static/doctools.js:200 sphinx/writers/html.py:134 sphinx/writers/html.py:145 sphinx/writers/html5.py:103 sphinx/writers/html5.py:114
msgid "Permalink to this definition"
msgstr "永久链接至目标"
@@ -3152,8 +3107,7 @@ msgstr ", 在 "
msgid "Expand sidebar"
msgstr "展开边æ "
-#: sphinx/themes/classic/static/sidebar.js_t:96
-#: sphinx/themes/classic/static/sidebar.js_t:124
+#: sphinx/themes/classic/static/sidebar.js_t:96 sphinx/themes/classic/static/sidebar.js_t:124
msgid "Collapse sidebar"
msgstr "折å è¾¹æ "
@@ -3163,8 +3117,7 @@ msgstr "目录"
#: sphinx/transforms/__init__.py:261
#, python-format
-msgid ""
-"4 column based index found. It might be a bug of extensions you use: %r"
+msgid "4 column based index found. It might be a bug of extensions you use: %r"
msgstr "å‘现使用了 4 列布局的索引页。å¯èƒ½æ˜¯ä½ æ‰€ç”¨çš„æ‰©å±•出现了 Bug:%r"
#: sphinx/transforms/__init__.py:303
@@ -3177,27 +3130,19 @@ msgid "Footnote [#] is not referenced."
msgstr "脚注 [#] 没有被引用过。"
#: sphinx/transforms/i18n.py:293 sphinx/transforms/i18n.py:363
-msgid ""
-"inconsistent footnote references in translated message. original: {0}, "
-"translated: {1}"
+msgid "inconsistent footnote references in translated message. original: {0}, translated: {1}"
msgstr "译文中的脚注引用与原文ä¸ä¸€è‡´ã€‚原始为:{0},翻译åŽä¸ºï¼š{1}"
#: sphinx/transforms/i18n.py:335
-msgid ""
-"inconsistent references in translated message. original: {0}, translated: "
-"{1}"
+msgid "inconsistent references in translated message. original: {0}, translated: {1}"
msgstr "译文中的引用与原文ä¸ä¸€è‡´ã€‚原始为:{0},翻译åŽä¸ºï¼š{1}"
#: sphinx/transforms/i18n.py:382
-msgid ""
-"inconsistent citation references in translated message. original: {0}, "
-"translated: {1}"
+msgid "inconsistent citation references in translated message. original: {0}, translated: {1}"
msgstr "译文中的引文引用与原文ä¸ä¸€è‡´ã€‚原始为:{0},翻译åŽä¸ºï¼š{1}"
#: sphinx/transforms/i18n.py:402
-msgid ""
-"inconsistent term references in translated message. original: {0}, "
-"translated: {1}"
+msgid "inconsistent term references in translated message. original: {0}, translated: {1}"
msgstr "译文中的术语引用与原文ä¸ä¸€è‡´ã€‚原始为:{0},翻译åŽä¸ºï¼š{1}"
#: sphinx/transforms/post_transforms/__init__.py:110
@@ -3259,9 +3204,7 @@ msgstr "写入时å‘生错误:%s,%s"
#: sphinx/util/i18n.py:215
#, python-format
-msgid ""
-"Invalid date format. Quote the string by single quote if you want to output "
-"it directly: %s"
+msgid "Invalid date format. Quote the string by single quote if you want to output it directly: %s"
msgstr "无效的日期格å¼ã€‚如果你想直接输出日期字符串,请用å•引å·ï¼š%s"
#: sphinx/util/nodes.py:428
@@ -3330,12 +3273,10 @@ msgid "document title is not a single Text node"
msgstr "æ–‡æ¡£æ ‡é¢˜ä¸æ˜¯ä¸€ä¸ªå•纯文本节点"
#: sphinx/writers/latex.py:926 sphinx/writers/texinfo.py:658
-msgid ""
-"encountered title node not in section, topic, table, admonition or sidebar"
+msgid "encountered title node not in section, topic, table, admonition or sidebar"
msgstr "在节ã€è¯é¢˜ã€è¡¨æ ¼ã€è­¦ç¤ºæˆ–è¾¹æ ä»¥å¤–çš„ä½ç½®å‘现标题节点"
-#: sphinx/writers/latex.py:1102 sphinx/writers/manpage.py:277
-#: sphinx/writers/texinfo.py:675
+#: sphinx/writers/latex.py:1102 sphinx/writers/manpage.py:277 sphinx/writers/texinfo.py:675
msgid "Footnotes"
msgstr "脚注"
diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py
deleted file mode 100644
index 792f4ab85..000000000
--- a/sphinx/make_mode.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
- sphinx.make_mode
- ~~~~~~~~~~~~~~~~
-
- sphinx-build -M command-line handling.
-
- This replaces the old, platform-dependent and once-generated content
- of Makefile / make.bat.
-
- This is in its own module so that importing it is fast. It should not
- import the main Sphinx modules (like sphinx.applications, sphinx.builders).
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-
-from sphinx.cmd import make_mode
-from sphinx.deprecation import RemovedInSphinx30Warning
-
-
-BUILDERS = make_mode.BUILDERS
-
-
-class Make(make_mode.Make):
- def __init__(self, *args):
- warnings.warn('sphinx.make_mode.Make is deprecated. '
- 'Please use sphinx.cmd.make_mode.Make instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- super().__init__(*args)
-
-
-def run_make_mode(args):
- warnings.warn('sphinx.make_mode.run_make_mode() is deprecated. '
- 'Please use sphinx.cmd.make_mode.run_make_mode() instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- return make_mode.run_make_mode(args)
diff --git a/sphinx/parsers.py b/sphinx/parsers.py
index 2a61971f3..3974d1c66 100644
--- a/sphinx/parsers.py
+++ b/sphinx/parsers.py
@@ -8,6 +8,7 @@
:license: BSD, see LICENSE for details.
"""
+import warnings
from typing import Any, Dict, List, Union
import docutils.parsers
@@ -17,6 +18,7 @@ from docutils.parsers.rst import states
from docutils.statemachine import StringList
from docutils.transforms.universal import SmartQuotes
+from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.util.rst import append_epilog, prepend_prolog
if False:
@@ -47,6 +49,8 @@ class Parser(docutils.parsers.Parser):
.. deprecated:: 1.6
``warn()`` and ``info()`` is deprecated. Use :mod:`sphinx.util.logging` instead.
+ .. deprecated:: 3.0
+ parser.app is deprecated.
"""
def set_application(self, app: "Sphinx") -> None:
@@ -54,10 +58,15 @@ class Parser(docutils.parsers.Parser):
:param sphinx.application.Sphinx app: Sphinx application object
"""
- self.app = app
+ self._app = app
self.config = app.config
self.env = app.env
+ @property
+ def app(self) -> "Sphinx":
+ warnings.warn('parser.app is deprecated.', RemovedInSphinx50Warning)
+ return self._app
+
class RSTParser(docutils.parsers.rst.Parser, Parser):
"""A reST parser for Sphinx."""
diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py
index 22207b715..52617e3bc 100644
--- a/sphinx/pycode/ast.py
+++ b/sphinx/pycode/ast.py
@@ -9,6 +9,7 @@
"""
import sys
+from typing import List
if sys.version_info > (3, 8):
import ast
@@ -40,6 +41,13 @@ def unparse(node: ast.AST) -> str:
return None
elif isinstance(node, str):
return node
+ elif isinstance(node, ast.arg):
+ if node.annotation:
+ return "%s: %s" % (node.arg, unparse(node.annotation))
+ else:
+ return node.arg
+ elif isinstance(node, ast.arguments):
+ return unparse_arguments(node)
elif isinstance(node, ast.Attribute):
return "%s.%s" % (unparse(node.value), node.attr)
elif isinstance(node, ast.Bytes):
@@ -58,7 +66,7 @@ def unparse(node: ast.AST) -> str:
elif isinstance(node, ast.Index):
return unparse(node.value)
elif isinstance(node, ast.Lambda):
- return "<function <lambda>>" # TODO
+ return "lambda %s: ..." % unparse(node.args)
elif isinstance(node, ast.List):
return "[" + ", ".join(unparse(e) for e in node.elts) + "]"
elif isinstance(node, ast.Name):
@@ -80,3 +88,61 @@ def unparse(node: ast.AST) -> str:
return repr(node.value)
else:
raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
+
+
+def unparse_arguments(node: ast.arguments) -> str:
+ """Unparse an arguments to string."""
+ defaults = list(node.defaults)
+ positionals = len(node.args)
+ posonlyargs = 0
+ if hasattr(node, "posonlyargs"): # for py38+
+ posonlyargs += len(node.posonlyargs) # type:ignore
+ positionals += posonlyargs
+ for _ in range(len(defaults), positionals):
+ defaults.insert(0, None)
+
+ kw_defaults = list(node.kw_defaults)
+ for _ in range(len(kw_defaults), len(node.kwonlyargs)):
+ kw_defaults.insert(0, None)
+
+ args = [] # type: List[str]
+ if hasattr(node, "posonlyargs"): # for py38+
+ for i, arg in enumerate(node.posonlyargs): # type: ignore
+ name = unparse(arg)
+ if defaults[i]:
+ if arg.annotation:
+ name += " = %s" % unparse(defaults[i])
+ else:
+ name += "=%s" % unparse(defaults[i])
+ args.append(name)
+
+ if node.posonlyargs: # type: ignore
+ args.append('/')
+
+ for i, arg in enumerate(node.args):
+ name = unparse(arg)
+ if defaults[i + posonlyargs]:
+ if arg.annotation:
+ name += " = %s" % unparse(defaults[i + posonlyargs])
+ else:
+ name += "=%s" % unparse(defaults[i + posonlyargs])
+ args.append(name)
+
+ if node.vararg:
+ args.append("*" + unparse(node.vararg))
+
+ if node.kwonlyargs and not node.vararg:
+ args.append('*')
+ for i, arg in enumerate(node.kwonlyargs):
+ name = unparse(arg)
+ if kw_defaults[i]:
+ if arg.annotation:
+ name += " = %s" % unparse(kw_defaults[i])
+ else:
+ name += "=%s" % unparse(kw_defaults[i])
+ args.append(name)
+
+ if node.kwarg:
+ args.append("**" + unparse(node.kwarg))
+
+ return ", ".join(args)
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index 1f803e950..cb3cf0cc1 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -142,7 +142,7 @@ class TokenProcessor:
def fetch_token(self) -> Token:
"""Fetch a next token from source code.
- Returns ``False`` if sequence finished.
+ Returns ``None`` if sequence finished.
"""
try:
self.previous = self.current
diff --git a/sphinx/registry.py b/sphinx/registry.py
index ea423298f..93a7ebc25 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -9,9 +9,7 @@
"""
import traceback
-import warnings
from importlib import import_module
-from inspect import isclass
from types import MethodType
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union
@@ -25,18 +23,15 @@ from pkg_resources import iter_entry_points
from sphinx.builders import Builder
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.domains import Domain, Index, ObjType
from sphinx.domains.std import GenericObject, Target
from sphinx.environment import BuildEnvironment
from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
from sphinx.extension import Extension
-from sphinx.io import SphinxFileInput
from sphinx.locale import __
from sphinx.parsers import Parser as SphinxParser
from sphinx.roles import XRefRole
from sphinx.util import logging
-from sphinx.util.docutils import directive_helper
from sphinx.util.logging import prefixed_warnings
from sphinx.util.typing import RoleFunction, TitleGetter
@@ -176,17 +171,9 @@ class SphinxComponentRegistry:
yield domain
- def override_domain(self, domain: "Type[Domain]") -> None:
- warnings.warn('registry.override_domain() is deprecated. '
- 'Use app.add_domain(domain, override=True) instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- self.add_domain(domain, override=True)
-
- def add_directive_to_domain(self, domain: str, name: str, obj: Any,
- has_content: bool = None, argument_spec: Any = None,
- override: bool = False, **option_spec: Any) -> None:
- logger.debug('[app] adding directive to domain: %r',
- (domain, name, obj, has_content, argument_spec, option_spec))
+ def add_directive_to_domain(self, domain: str, name: str,
+ cls: "Type[Directive]", override: bool = False) -> None:
+ logger.debug('[app] adding directive to domain: %r', (domain, name, cls))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
@@ -194,10 +181,7 @@ class SphinxComponentRegistry:
if name in directives and not override:
raise ExtensionError(__('The %r directive is already registered to domain %s') %
(name, domain))
- if not isclass(obj) or not issubclass(obj, Directive):
- directives[name] = directive_helper(obj, has_content, argument_spec, **option_spec)
- else:
- directives[name] = obj
+ directives[name] = cls
def add_role_to_domain(self, domain: str, name: str,
role: Union[RoleFunction, XRefRole], override: bool = False
@@ -273,27 +257,8 @@ class SphinxComponentRegistry:
else:
self.source_suffix[suffix] = filetype
- def add_source_parser(self, *args: Any, **kwargs: Any) -> None:
- logger.debug('[app] adding search source_parser: %r', args)
- if len(args) == 1:
- # new sytle arguments: (source_parser)
- suffix = None # type: str
- parser = args[0] # type: Type[Parser]
- else:
- # old style arguments: (suffix, source_parser)
- warnings.warn('app.add_source_parser() does not support suffix argument. '
- 'Use app.add_source_suffix() instead.',
- RemovedInSphinx30Warning, stacklevel=3)
- suffix = args[0]
- parser = args[1]
-
- if suffix:
- self.add_source_suffix(suffix, suffix, override=True)
-
- if len(parser.supported) == 0:
- warnings.warn('Old source_parser has been detected. Please fill Parser.supported '
- 'attribute: %s' % parser.__name__,
- RemovedInSphinx30Warning, stacklevel=3)
+ def add_source_parser(self, parser: "Type[Parser]", **kwargs: Any) -> None:
+ logger.debug('[app] adding search source_parser: %r', parser)
# create a map from filetype to parser
for filetype in parser.supported:
@@ -303,12 +268,6 @@ class SphinxComponentRegistry:
else:
self.source_parsers[filetype] = parser
- # also maps suffix to parser
- #
- # This rescues old styled parsers which does not have ``supported`` filetypes.
- if suffix:
- self.source_parsers[suffix] = parser
-
def get_source_parser(self, filetype: str) -> "Type[Parser]":
try:
return self.source_parsers[filetype]
@@ -325,16 +284,6 @@ class SphinxComponentRegistry:
parser.set_application(app)
return parser
- def add_source_input(self, input_class: "Type[SphinxFileInput]", override: bool = False
- ) -> None:
- warnings.warn('registry.source_input() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- for filetype in input_class.supported:
- if filetype in self.source_inputs and not override:
- raise ExtensionError(__('source_input for %r is already registered') %
- filetype)
- self.source_inputs[filetype] = input_class
-
def get_source_input(self, filetype: str) -> "Type[Input]":
try:
return self.source_inputs[filetype]
@@ -407,7 +356,7 @@ class SphinxComponentRegistry:
attrgetter: Callable[[Any, str, Any], Any]) -> None:
self.autodoc_attrgettrs[typ] = attrgetter
- def add_css_files(self, filename, **attributes):
+ def add_css_files(self, filename: str, **attributes: str) -> None:
self.css_files.append((filename, attributes))
def add_js_file(self, filename: str, **attributes: str) -> None:
diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py
index 668248b6b..d1f444be1 100644
--- a/sphinx/search/ja.py
+++ b/sphinx/search/ja.py
@@ -19,7 +19,6 @@
import os
import re
import sys
-import warnings
from typing import Any, Dict, List
try:
@@ -34,7 +33,6 @@ try:
except ImportError:
janome_module = False
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import SphinxError, ExtensionError
from sphinx.search import SearchLanguage
from sphinx.util import import_object
@@ -525,21 +523,9 @@ class SearchJapanese(SearchLanguage):
"""
lang = 'ja'
language_name = 'Japanese'
- splitters = {
- 'default': 'sphinx.search.ja.DefaultSplitter',
- 'mecab': 'sphinx.search.ja.MecabSplitter',
- 'janome': 'sphinx.search.ja.JanomeSplitter',
- }
def init(self, options: Dict) -> None:
- type = options.get('type', 'sphinx.search.ja.DefaultSplitter')
- if type in self.splitters:
- dotted_path = self.splitters[type]
- warnings.warn('html_search_options["type"]: %s is deprecated. '
- 'Please give "%s" instead.' % (type, dotted_path),
- RemovedInSphinx30Warning, stacklevel=2)
- else:
- dotted_path = type
+ dotted_path = options.get('type', 'sphinx.search.ja.DefaultSplitter')
try:
self.splitter = import_object(dotted_path)(options)
except ExtensionError:
diff --git a/sphinx/templates/latex/longtable.tex_t b/sphinx/templates/latex/longtable.tex_t
index f9e8802e4..8d4cd748c 100644
--- a/sphinx/templates/latex/longtable.tex_t
+++ b/sphinx/templates/latex/longtable.tex_t
@@ -26,7 +26,7 @@
\endhead
\hline
-\multicolumn{<%= table.colcount %>}{r}{\makebox[0pt][r]{\sphinxtablecontinued{<%= _('Continued on next page') %>}}}\\
+\multicolumn{<%= table.colcount %>}{r}{\makebox[0pt][r]{\sphinxtablecontinued{<%= _('continues on next page') %>}}}\\
\endfoot
\endlastfoot
diff --git a/sphinx/testing/comparer.py b/sphinx/testing/comparer.py
index 0bcd194a8..ddd818394 100644
--- a/sphinx/testing/comparer.py
+++ b/sphinx/testing/comparer.py
@@ -9,7 +9,7 @@
"""
import difflib
import pathlib
-from typing import List, Union
+from typing import Any, List, Union
class PathComparer:
@@ -38,13 +38,13 @@ class PathComparer:
"""
self.path = pathlib.Path(path)
- def __str__(self):
+ def __str__(self) -> str:
return self.path.as_posix()
- def __repr__(self):
+ def __repr__(self) -> str:
return "<{0.__class__.__name__}: '{0}'>".format(self)
- def __eq__(self, other):
+ def __eq__(self, other: Union[str, pathlib.Path]) -> bool: # type: ignore
return not bool(self.ldiff(other))
def diff(self, other: Union[str, pathlib.Path]) -> List[str]:
@@ -94,8 +94,10 @@ class PathComparer:
return [line.strip() for line in difflib.Differ().compare([s_path], [o_path])]
-def pytest_assertrepr_compare(op, left, right):
+def pytest_assertrepr_compare(op: str, left: Any, right: Any) -> List[str]:
if isinstance(left, PathComparer) and op == "==":
return ['Comparing path:'] + left.ldiff(right)
- if isinstance(right, PathComparer) and op == "==":
+ elif isinstance(right, PathComparer) and op == "==":
return ['Comparing path:'] + right.rdiff(left)
+ else:
+ return []
diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py
index c6dd7ecdf..eec3b4208 100644
--- a/sphinx/testing/fixtures.py
+++ b/sphinx/testing/fixtures.py
@@ -14,20 +14,44 @@ import sys
from collections import namedtuple
from io import StringIO
from subprocess import PIPE
-from typing import Any, Dict
+from typing import Any, Callable, Dict, Generator, Tuple
import pytest
-from . import util
+from sphinx.testing import util
+from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding
@pytest.fixture(scope='session')
-def rootdir() -> None:
+def rootdir() -> str:
return None
+class SharedResult:
+ cache = {} # type: Dict[str, Dict[str, str]]
+
+ def store(self, key: str, app_: SphinxTestApp) -> Any:
+ if key in self.cache:
+ return
+ data = {
+ 'status': app_._status.getvalue(),
+ 'warning': app_._warning.getvalue(),
+ }
+ self.cache[key] = data
+
+ def restore(self, key: str) -> Dict[str, StringIO]:
+ if key not in self.cache:
+ return {}
+ data = self.cache[key]
+ return {
+ 'status': StringIO(data['status']),
+ 'warning': StringIO(data['warning']),
+ }
+
+
@pytest.fixture
-def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir):
+def app_params(request: Any, test_params: Dict, shared_result: SharedResult,
+ sphinx_test_tempdir: str, rootdir: str) -> Tuple[Dict, Dict]:
"""
parameters that is specified by 'pytest.mark.sphinx' for
sphinx.application.Sphinx initialization
@@ -40,7 +64,7 @@ def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir
else:
markers = request.node.get_marker("sphinx")
pargs = {}
- kwargs = {} # type: Dict[str, str]
+ kwargs = {} # type: Dict[str, Any]
if markers is not None:
# to avoid stacking positional args
@@ -75,7 +99,7 @@ def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir
@pytest.fixture
-def test_params(request):
+def test_params(request: Any) -> Dict:
"""
test parameters that is specified by 'pytest.mark.test_params'
@@ -102,7 +126,8 @@ def test_params(request):
@pytest.fixture(scope='function')
-def app(test_params, app_params, make_app, shared_result):
+def app(test_params: Dict, app_params: Tuple[Dict, Dict], make_app: Callable,
+ shared_result: SharedResult) -> Generator[SphinxTestApp, None, None]:
"""
provides sphinx.application.Sphinx object
"""
@@ -122,7 +147,7 @@ def app(test_params, app_params, make_app, shared_result):
@pytest.fixture(scope='function')
-def status(app):
+def status(app: SphinxTestApp) -> StringIO:
"""
compat for testing with previous @with_app decorator
"""
@@ -130,7 +155,7 @@ def status(app):
@pytest.fixture(scope='function')
-def warning(app):
+def warning(app: SphinxTestApp) -> StringIO:
"""
compat for testing with previous @with_app decorator
"""
@@ -138,7 +163,7 @@ def warning(app):
@pytest.fixture()
-def make_app(test_params, monkeypatch):
+def make_app(test_params: Dict, monkeypatch: Any) -> Generator[Callable, None, None]:
"""
provides make_app function to initialize SphinxTestApp instance.
if you want to initialize 'app' in your test function. please use this
@@ -153,10 +178,10 @@ def make_app(test_params, monkeypatch):
status, warning = StringIO(), StringIO()
kwargs.setdefault('status', status)
kwargs.setdefault('warning', warning)
- app_ = util.SphinxTestApp(*args, **kwargs) # type: Any
+ app_ = SphinxTestApp(*args, **kwargs) # type: Any
apps.append(app_)
if test_params['shared_result']:
- app_ = util.SphinxTestAppWrapperForSkipBuilding(app_)
+ app_ = SphinxTestAppWrapperForSkipBuilding(app_)
return app_
yield make
@@ -165,40 +190,18 @@ def make_app(test_params, monkeypatch):
app_.cleanup()
-class SharedResult:
- cache = {} # type: Dict[str, Dict[str, str]]
-
- def store(self, key, app_):
- if key in self.cache:
- return
- data = {
- 'status': app_._status.getvalue(),
- 'warning': app_._warning.getvalue(),
- }
- self.cache[key] = data
-
- def restore(self, key):
- if key not in self.cache:
- return {}
- data = self.cache[key]
- return {
- 'status': StringIO(data['status']),
- 'warning': StringIO(data['warning']),
- }
-
-
@pytest.fixture
-def shared_result():
+def shared_result() -> SharedResult:
return SharedResult()
@pytest.fixture(scope='module', autouse=True)
-def _shared_result_cache():
+def _shared_result_cache() -> None:
SharedResult.cache.clear()
@pytest.fixture
-def if_graphviz_found(app):
+def if_graphviz_found(app: SphinxTestApp) -> None:
"""
The test will be skipped when using 'if_graphviz_found' fixture and graphviz
dot command is not found.
@@ -215,7 +218,7 @@ def if_graphviz_found(app):
@pytest.fixture(scope='session')
-def sphinx_test_tempdir(tmpdir_factory):
+def sphinx_test_tempdir(tmpdir_factory: Any) -> "util.path":
"""
temporary directory that wrapped with `path` class.
"""
@@ -227,7 +230,7 @@ def sphinx_test_tempdir(tmpdir_factory):
@pytest.fixture
-def tempdir(tmpdir):
+def tempdir(tmpdir: str) -> "util.path":
"""
temporary directory that wrapped with `path` class.
this fixture is for compat with old test implementation.
diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py
index 1c883af2f..4c3702e3d 100644
--- a/sphinx/testing/path.py
+++ b/sphinx/testing/path.py
@@ -10,8 +10,11 @@ import builtins
import os
import shutil
import sys
+import warnings
from typing import Any, Callable, IO, List
+from sphinx.deprecation import RemovedInSphinx50Warning
+
FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
@@ -138,6 +141,14 @@ class path(str):
"""
Returns the text in the file.
"""
+ warnings.warn('Path.text() is deprecated. Please use read_text() instead.',
+ RemovedInSphinx50Warning, stacklevel=2)
+ return self.read_text(encoding, **kwargs)
+
+ def read_text(self, encoding: str = 'utf-8', **kwargs: Any) -> str:
+ """
+ Returns the text in the file.
+ """
with open(self, encoding=encoding, **kwargs) as f:
return f.read()
@@ -145,6 +156,14 @@ class path(str):
"""
Returns the bytes in the file.
"""
+ warnings.warn('Path.bytes() is deprecated. Please use read_bytes() instead.',
+ RemovedInSphinx50Warning, stacklevel=2)
+ return self.read_bytes()
+
+ def read_bytes(self) -> builtins.bytes:
+ """
+ Returns the bytes in the file.
+ """
with open(self, mode='rb') as f:
return f.read()
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index f4ef35d61..75cd0f411 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -11,10 +11,12 @@ import os
import re
import sys
import warnings
+from io import StringIO
from typing import Any, Dict, Generator, IO, List, Pattern
from xml.etree import ElementTree
from docutils import nodes
+from docutils.nodes import Node
from docutils.parsers.rst import directives, roles
from sphinx import application, locale
@@ -47,7 +49,7 @@ def assert_startswith(thing: str, prefix: str) -> None:
assert False, '%r does not start with %r' % (thing, prefix)
-def assert_node(node: nodes.Node, cls: Any = None, xpath: str = "", **kwargs: Any) -> None:
+def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: Any) -> None:
if cls:
if isinstance(cls, list):
assert_node(node, cls[0], xpath=xpath, **kwargs)
@@ -101,6 +103,8 @@ class SphinxTestApp(application.Sphinx):
A subclass of :class:`Sphinx` that runs on the test root, with some
better default values for the initialization parameters.
"""
+ _status = None # type: StringIO
+ _warning = None # type: StringIO
def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False,
confoverrides: Dict = None, status: IO = None, warning: IO = None,
diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t
index 08ddd2f0a..96bb0fed1 100644
--- a/sphinx/texinputs/Makefile_t
+++ b/sphinx/texinputs/Makefile_t
@@ -10,7 +10,6 @@ ALLDVI = $(addsuffix .dvi,$(ALLDOCS))
ALLXDV =
{% endif -%}
ALLPS = $(addsuffix .ps,$(ALLDOCS))
-ALLIMGS = $(wildcard *.png *.gif *.jpg *.jpeg)
# Prefix for archive names
ARCHIVEPREFIX =
@@ -46,15 +45,7 @@ LATEX = latexmk -dvi
PDFLATEX = latexmk -pdf -dvi- -ps-
{% endif %}
-%.png %.gif %.jpg %.jpeg: FORCE_MAKE
- extractbb '$@'
-
-{% if latex_engine in ('platex', 'uplatex') -%}
-%.dvi: %.tex $(ALLIMGS) FORCE_MAKE
- for f in *.pdf; do extractbb "$$f"; done
- $(LATEX) $(LATEXMKOPTS) '$<'
-
-{% elif latex_engine != 'xelatex' -%}
+{% if latex_engine != 'xelatex' -%}
%.dvi: %.tex FORCE_MAKE
$(LATEX) $(LATEXMKOPTS) '$<'
@@ -62,12 +53,7 @@ PDFLATEX = latexmk -pdf -dvi- -ps-
%.ps: %.dvi
dvips '$<'
-{% if latex_engine in ('platex', 'uplatex') -%}
-%.pdf: %.tex $(ALLIMGS) FORCE_MAKE
- for f in *.pdf; do extractbb "$$f"; done
-{%- else -%}
%.pdf: %.tex FORCE_MAKE
-{%- endif %}
$(PDFLATEX) $(LATEXMKOPTS) '$<'
all: $(ALLPDF)
diff --git a/sphinx/texinputs/make.bat_t b/sphinx/texinputs/make.bat_t
index 83dd449c2..9dfa38c13 100644
--- a/sphinx/texinputs/make.bat_t
+++ b/sphinx/texinputs/make.bat_t
@@ -31,11 +31,6 @@ if "%1" == "" goto all-pdf
if "%1" == "all-pdf" (
:all-pdf
-{%- if latex_engine == 'platex' %}
- for %%i in (*.png *.gif *.jpg *.jpeg *.pdf) do (
- extractbb %%i
- )
-{%- endif %}
for %%i in (*.tex) do (
%PDFLATEX% %LATEXMKOPTS% %%i
)
diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html
index 02ee85abc..2673369f2 100644
--- a/sphinx/themes/basic/search.html
+++ b/sphinx/themes/basic/search.html
@@ -27,10 +27,8 @@
</p>
</div>
<p>
- {% trans %}From here you can search these documents. Enter your search
- words into the box below and click "search". Note that the search
- function will automatically search for all of the words. Pages
- containing fewer words won't appear in the result list.{% endtrans %}
+ {% trans %}Searching for multiple words only shows matches that contain
+ all words.{% endtrans %}
</p>
<form action="" method="get">
<input type="text" name="q" aria-labelledby="search-documentation" value="" />
diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html
deleted file mode 100644
index 50700c158..000000000
--- a/sphinx/themes/basic/searchresults.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{#
- basic/searchresults.html
- ~~~~~~~~~~~~~~~~~~~~~~~~
-
- Template for the body of the search results page.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-#}
-<h1 id="search-documentation">{{ _('Search') }}</h1>
-<p>
- From here you can search these documents. Enter your search
- words into the box below and click "search".
-</p>
-<form action="" method="get">
- <input type="text" name="q" value="" />
- <input type="submit" value="{{ _('search') }}" />
- <span id="search-progress" style="padding-left: 10px"></span>
-</form>
-{%- if search_performed %}
- <h2>{{ _('Search Results') }}</h2>
- {%- if not search_results %}
- <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>
- {%- endif %}
-{%- endif %}
-<div id="search-results">
- {%- if search_results %}
- <ul class="search">
- {% for href, caption, context in search_results %}
- <li><a href="{{ docroot }}{{ href }}/?highlight={{ q }}">{{ caption }}</a>
- <div class="context">{{ context|e }}</div>
- </li>
- {% endfor %}
- </ul>
- {%- endif %}
-</div>
diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py
index ee459cc56..6d7c3b0eb 100644
--- a/sphinx/transforms/post_transforms/__init__.py
+++ b/sphinx/transforms/post_transforms/__init__.py
@@ -131,7 +131,7 @@ class ReferencesResolver(SphinxPostTransform):
if not results:
return None
if len(results) > 1:
- def stringify(name, node):
+ def stringify(name: str, node: Element) -> str:
reftitle = node.get('reftitle', node.astext())
return ':%s:`%s`' % (name, reftitle)
candidates = ' or '.join(stringify(name, role) for name, role in results)
diff --git a/sphinx/transforms/post_transforms/compat.py b/sphinx/transforms/post_transforms/compat.py
deleted file mode 100644
index c71191dec..000000000
--- a/sphinx/transforms/post_transforms/compat.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
- sphinx.transforms.post_transforms.compat
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Post transforms for compatibility
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from docutils import nodes
-from docutils.writers.docutils_xml import XMLTranslator
-
-from sphinx.addnodes import math_block, displaymath
-from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx30Warning
-from sphinx.transforms import SphinxTransform
-from sphinx.util import logging
-
-
-logger = logging.getLogger(__name__)
-
-
-class MathNodeMigrator(SphinxTransform):
- """Migrate a math node to docutils'.
-
- For a long time, Sphinx uses an original node for math. Since 1.8,
- Sphinx starts to use a math node of docutils'. This transform converts
- old and new nodes to keep compatibility.
- """
- default_priority = 999
-
- def apply(self, **kwargs: Any) -> None:
- for math_node in self.document.traverse(nodes.math):
- # case: old styled ``math`` node generated by old extensions
- if len(math_node) == 0:
- warnings.warn("math node for Sphinx was replaced by docutils'. "
- "Please use ``docutils.nodes.math`` instead.",
- RemovedInSphinx30Warning)
- equation = math_node['latex']
- math_node += nodes.Text(equation, equation)
-
- translator = self.app.builder.get_translator_class()
- if hasattr(translator, 'visit_displaymath') and translator != XMLTranslator:
- # case: old translators which does not support ``math_block`` node
- warnings.warn("Translator for %s does not support math_block node'. "
- "Please update your extension." % translator,
- RemovedInSphinx30Warning)
- for old_math_block_node in self.document.traverse(math_block):
- alt = displaymath(latex=old_math_block_node.astext(),
- number=old_math_block_node.get('number'),
- label=old_math_block_node.get('label'),
- nowrap=old_math_block_node.get('nowrap'),
- docname=old_math_block_node.get('docname'))
- old_math_block_node.replace_self(alt)
- elif getattr(self.app.builder, 'math_renderer_name', None) == 'unknown':
- # case: math extension provides old styled math renderer
- for math_block_node in self.document.traverse(nodes.math_block):
- math_block_node['latex'] = math_block_node.astext()
-
- # case: old styled ``displaymath`` node generated by old extensions
- for math_block_node in self.document.traverse(math_block):
- if len(math_block_node) == 0:
- warnings.warn("math node for Sphinx was replaced by docutils'. "
- "Please use ``docutils.nodes.math_block`` instead.",
- RemovedInSphinx30Warning)
- if isinstance(math_block_node, displaymath):
- newnode = nodes.math_block('', math_block_node['latex'],
- **math_block_node.attributes)
- math_block_node.replace_self(newnode)
- else:
- latex = math_block_node['latex']
- math_block_node += nodes.Text(latex, latex)
-
-
-def setup(app: Sphinx) -> Dict[str, Any]:
- app.add_post_transform(MathNodeMigrator)
-
- return {
- 'version': 'builtin',
- 'parallel_read_safe': True,
- 'parallel_write_safe': True,
- }
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 262837b50..12ae051f1 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -28,16 +28,13 @@ from time import mktime, strptime
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, Set, Tuple
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
-from docutils.utils import relative_path
-
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import (
PycodeError, SphinxParallelError, ExtensionError, FiletypeNotFoundError
)
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore
-from sphinx.util.fileutil import copy_asset_file
from sphinx.util.typing import PathMatcher
from sphinx.util import smartypants # noqa
@@ -45,7 +42,7 @@ from sphinx.util import smartypants # noqa
# prune unused ones indiscriminately
from sphinx.util.osutil import ( # noqa
SEP, os_path, relative_uri, ensuredir, walk, mtimes_of_files, movefile,
- copyfile, copytimes, make_filename, ustrftime)
+ copyfile, copytimes, make_filename)
from sphinx.util.nodes import ( # noqa
nested_parse_with_titles, split_explicit_title, explicit_title_re,
caption_ref_re)
@@ -56,7 +53,7 @@ if False:
# For type annotation
from typing import Type # for python3.5.1
from sphinx.application import Sphinx
- from sphinx.builders import Builder
+
logger = logging.getLogger(__name__)
@@ -201,35 +198,6 @@ class DownloadFiles(dict):
self.add_file(docname, filename)
-def copy_static_entry(source: str, targetdir: str, builder: "Builder", context: Dict = {},
- exclude_matchers: Tuple[PathMatcher, ...] = (), level: int = 0) -> None:
- """[DEPRECATED] Copy a HTML builder static_path entry from source to targetdir.
-
- Handles all possible cases of files, directories and subdirectories.
- """
- warnings.warn('sphinx.util.copy_static_entry is deprecated for removal',
- RemovedInSphinx30Warning, stacklevel=2)
-
- if exclude_matchers:
- relpath = relative_path(path.join(builder.srcdir, 'dummy'), source)
- for matcher in exclude_matchers:
- if matcher(relpath):
- return
- if path.isfile(source):
- copy_asset_file(source, targetdir, context, builder.templates)
- elif path.isdir(source):
- ensuredir(targetdir)
- for entry in os.listdir(source):
- if entry.startswith('.'):
- continue
- newtarget = targetdir
- if path.isdir(path.join(source, entry)):
- newtarget = path.join(targetdir, entry)
- copy_static_entry(path.join(source, entry), newtarget,
- builder, context, level=level + 1,
- exclude_matchers=exclude_matchers)
-
-
_DEBUG_HEADER = '''\
# Sphinx version: %s
# Python version: %s (%s)
@@ -681,7 +649,7 @@ class progress_message:
def __call__(self, f: Callable) -> Callable:
@functools.wraps(f)
- def wrapper(*args, **kwargs):
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
with self:
return f(*args, **kwargs)
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index ba3c4d27c..0135899eb 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -15,27 +15,14 @@ from typing import Any, Dict
from docutils.utils import get_source_line
from sphinx import addnodes
-from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.transforms import SphinxTransform
-from sphinx.util import import_object
if False:
# For type annotation
from sphinx.application import Sphinx
-def deprecate_source_parsers(app: "Sphinx", config: Config) -> None:
- if config.source_parsers:
- warnings.warn('The config variable "source_parsers" is deprecated. '
- 'Please update your extension for the parser and remove the setting.',
- RemovedInSphinx30Warning)
- for suffix, parser in config.source_parsers.items():
- if isinstance(parser, str):
- parser = import_object(parser, 'source parser')
- app.add_source_parser(suffix, parser)
-
-
def register_application_for_autosummary(app: "Sphinx") -> None:
"""Register application object to autosummary module.
@@ -65,7 +52,6 @@ class IndexEntriesMigrator(SphinxTransform):
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_transform(IndexEntriesMigrator)
- app.connect('config-inited', deprecate_source_parsers)
app.connect('builder-inited', register_application_for_autosummary)
return {
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index 8854a1f98..7b3f011d1 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -8,8 +8,38 @@
:license: BSD, see LICENSE for details.
"""
+import re
import sys
-from typing import List
+from typing import Dict, List
+
+from docutils.parsers.rst.states import Body
+
+
+field_list_item_re = re.compile(Body.patterns['field_marker'])
+
+
+def extract_metadata(s: str) -> Dict[str, str]:
+ """Extract metadata from docstring."""
+ in_other_element = False
+ metadata = {} # type: Dict[str, str]
+
+ if not s:
+ return metadata
+
+ for line in prepare_docstring(s):
+ if line.strip() == '':
+ in_other_element = False
+ else:
+ matched = field_list_item_re.match(line)
+ if matched and not in_other_element:
+ field_name = matched.group()[1:].split(':', 1)[0]
+ if field_name.startswith('meta '):
+ name = field_name[5:].strip()
+ metadata[name] = line[matched.end():].strip()
+ else:
+ in_other_element = True
+
+ return metadata
def prepare_docstring(s: str, ignore: int = 1, tabsize: int = 8) -> List[str]:
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 3fbd8a420..3b7b60201 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -10,8 +10,6 @@
import os
import re
-import types
-import warnings
from contextlib import contextmanager
from copy import copy
from distutils.version import LooseVersion
@@ -24,14 +22,12 @@ import docutils
from docutils import nodes
from docutils.io import FileOutput
from docutils.nodes import Element, Node, system_message
-from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
+from docutils.parsers.rst import Directive, directives, roles
from docutils.parsers.rst.states import Inliner
from docutils.statemachine import StateMachine, State, StringList
from docutils.utils import Reporter, unescape
-from sphinx.deprecation import RemovedInSphinx30Warning
-from sphinx.errors import ExtensionError, SphinxError
-from sphinx.locale import __
+from sphinx.errors import SphinxError
from sphinx.util import logging
from sphinx.util.typing import RoleFunction
@@ -279,25 +275,6 @@ def is_html5_writer_available() -> bool:
return __version_info__ > (0, 13, 0)
-def directive_helper(obj: Any, has_content: bool = None,
- argument_spec: Tuple[int, int, bool] = None, **option_spec: Any
- ) -> Any:
- warnings.warn('function based directive support is now deprecated. '
- 'Use class based directive instead.',
- RemovedInSphinx30Warning)
-
- if isinstance(obj, (types.FunctionType, types.MethodType)):
- obj.content = has_content # type: ignore
- obj.arguments = argument_spec or (0, 0, False) # type: ignore
- obj.options = option_spec # type: ignore
- return convert_directive_function(obj)
- else:
- if has_content or argument_spec or option_spec:
- raise ExtensionError(__('when adding directive classes, no '
- 'additional arguments may be given'))
- return obj
-
-
@contextmanager
def switch_source_input(state: State, content: StringList) -> Generator[None, None, None]:
"""Switch current source input of state temporarily."""
@@ -467,7 +444,7 @@ class SphinxTranslator(nodes.NodeVisitor):
self.config = builder.config
self.settings = document.settings
- def dispatch_visit(self, node):
+ def dispatch_visit(self, node: Node) -> None:
"""
Dispatch node to appropriate visitor method.
The priority of visitor method is:
@@ -481,11 +458,12 @@ class SphinxTranslator(nodes.NodeVisitor):
if method:
logger.debug('SphinxTranslator.dispatch_visit calling %s for %s',
method.__name__, node)
- return method(node)
+ method(node)
+ break
else:
super().dispatch_visit(node)
- def dispatch_departure(self, node):
+ def dispatch_departure(self, node: Node) -> None:
"""
Dispatch node to appropriate departure method.
The priority of departure method is:
@@ -499,7 +477,8 @@ class SphinxTranslator(nodes.NodeVisitor):
if method:
logger.debug('SphinxTranslator.dispatch_departure calling %s for %s',
method.__name__, node)
- return method(node)
+ method(node)
+ break
else:
super().dispatch_departure(node)
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 096a200bf..1cb75637c 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -20,7 +20,7 @@ import babel.dates
from babel.messages.mofile import write_mo
from babel.messages.pofile import read_po
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import SphinxError
from sphinx.locale import __
from sphinx.util import logging
@@ -151,9 +151,8 @@ def find_catalog_files(docname: str, srcdir: str, locale_dirs: List[str],
def find_catalog_source_files(locale_dirs: List[str], locale: str, domains: List[str] = None,
- gettext_compact: bool = None, charset: str = 'utf-8',
- force_all: bool = False, excluded: Matcher = Matcher([])
- ) -> Set[CatalogInfo]:
+ charset: str = 'utf-8', force_all: bool = False,
+ excluded: Matcher = Matcher([])) -> Set[CatalogInfo]:
"""
:param list locale_dirs:
list of path as `['locale_dir1', 'locale_dir2', ...]` to find
@@ -169,9 +168,6 @@ def find_catalog_source_files(locale_dirs: List[str], locale: str, domains: List
"""
warnings.warn('find_catalog_source_files() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
- if gettext_compact is not None:
- warnings.warn('gettext_compact argument for find_catalog_source_files() '
- 'is deprecated.', RemovedInSphinx30Warning, stacklevel=2)
catalogs = set() # type: Set[CatalogInfo]
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 506e4a931..568682b37 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -10,16 +10,12 @@
import base64
import imghdr
-import warnings
from collections import OrderedDict
-from io import BytesIO
from os import path
from typing import IO, NamedTuple, Tuple
import imagesize
-from sphinx.deprecation import RemovedInSphinx30Warning
-
try:
from PIL import Image
except ImportError:
@@ -49,12 +45,8 @@ def get_image_size(filename: str) -> Tuple[int, int]:
size = (int(size[0]), int(size[1]))
if size is None and Image: # fallback to Pillow
- im = Image.open(filename)
- size = im.size
- try:
- im.fp.close()
- except Exception:
- pass
+ with Image.open(filename) as im:
+ size = im.size
return size
except Exception:
@@ -69,16 +61,10 @@ def guess_mimetype_for_stream(stream: IO, default: str = None) -> str:
return default
-def guess_mimetype(filename: str = '', content: bytes = None, default: str = None) -> str:
+def guess_mimetype(filename: str = '', default: str = None) -> str:
_, ext = path.splitext(filename.lower())
if ext in mime_suffixes:
return mime_suffixes[ext]
- elif content:
- # TODO: When the content argument is removed, make filename a required
- # argument.
- warnings.warn('The content argument of guess_mimetype() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return guess_mimetype_for_stream(BytesIO(content), default=default)
elif path.exists(filename):
with open(filename, 'rb') as f:
return guess_mimetype_for_stream(f, default=default)
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 2bcccdd60..281ef4493 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -17,12 +17,15 @@ import typing
import warnings
from functools import partial, partialmethod
from inspect import ( # NOQA
- isclass, ismethod, ismethoddescriptor, isroutine
+ Parameter, isclass, ismethod, ismethoddescriptor, isroutine
)
from io import StringIO
from typing import Any, Callable, Mapping, List, Tuple
+from typing import cast
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
+from sphinx.pycode.ast import ast # for py35-37
+from sphinx.pycode.ast import unparse as ast_unparse
from sphinx.util import logging
from sphinx.util.typing import stringify as stringify_annotation
@@ -51,9 +54,11 @@ memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software
# Foundation; All Rights Reserved
-def getargspec(func):
+def getargspec(func: Callable) -> Any:
"""Like inspect.getfullargspec but supports bound methods, and wrapped
methods."""
+ warnings.warn('sphinx.ext.inspect.getargspec() is deprecated',
+ RemovedInSphinx50Warning)
# On 3.5+, signature(int) or similar raises ValueError. On 3.4, it
# succeeds with a bogus signature. We want a TypeError uniformly, to
# match historical behavior.
@@ -81,19 +86,19 @@ def getargspec(func):
kind = param.kind
name = param.name
- if kind is inspect.Parameter.POSITIONAL_ONLY:
+ if kind is Parameter.POSITIONAL_ONLY:
args.append(name)
- elif kind is inspect.Parameter.POSITIONAL_OR_KEYWORD:
+ elif kind is Parameter.POSITIONAL_OR_KEYWORD:
args.append(name)
if param.default is not param.empty:
defaults += (param.default,) # type: ignore
- elif kind is inspect.Parameter.VAR_POSITIONAL:
+ elif kind is Parameter.VAR_POSITIONAL:
varargs = name
- elif kind is inspect.Parameter.KEYWORD_ONLY:
+ elif kind is Parameter.KEYWORD_ONLY:
kwonlyargs.append(name)
if param.default is not param.empty:
kwdefaults[name] = param.default
- elif kind is inspect.Parameter.VAR_KEYWORD:
+ elif kind is Parameter.VAR_KEYWORD:
varkw = name
if param.annotation is not param.empty:
@@ -359,7 +364,7 @@ def signature(subject: Callable, bound_method: bool = False) -> inspect.Signatur
# https://bugs.python.org/issue33009
if hasattr(subject, '_partialmethod'):
parameters = []
- return_annotation = inspect.Parameter.empty
+ return_annotation = Parameter.empty
else:
raise
@@ -427,11 +432,11 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
args.append(arg.getvalue())
last_kind = param.kind
- if last_kind == inspect.Parameter.POSITIONAL_ONLY:
+ if last_kind == Parameter.POSITIONAL_ONLY:
# PEP-570: Separator for Positional Only Parameter: /
args.append('/')
- if (sig.return_annotation is inspect.Parameter.empty or
+ if (sig.return_annotation is Parameter.empty or
show_annotation is False or
show_return_annotation is False):
return '(%s)' % ', '.join(args)
@@ -440,24 +445,50 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
return '(%s) -> %s' % (', '.join(args), annotation)
-class Parameter:
- """Fake parameter class for python2."""
- POSITIONAL_ONLY = 0
- POSITIONAL_OR_KEYWORD = 1
- VAR_POSITIONAL = 2
- KEYWORD_ONLY = 3
- VAR_KEYWORD = 4
- empty = object()
+def signature_from_str(signature: str) -> inspect.Signature:
+ """Create a Signature object from string."""
+ module = ast.parse('def func' + signature + ': pass')
+ definition = cast(ast.FunctionDef, module.body[0]) # type: ignore
- def __init__(self, name: str, kind: int = POSITIONAL_OR_KEYWORD,
- default: Any = empty) -> None:
- self.name = name
- self.kind = kind
- self.default = default
- self.annotation = self.empty
+ # parameters
+ args = definition.args
+ params = []
- warnings.warn('sphinx.util.inspect.Parameter is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
+ if hasattr(args, "posonlyargs"):
+ for arg in args.posonlyargs: # type: ignore
+ annotation = ast_unparse(arg.annotation) or Parameter.empty
+ params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
+ annotation=annotation))
+
+ for i, arg in enumerate(args.args):
+ if len(args.args) - i <= len(args.defaults):
+ default = ast_unparse(args.defaults[-len(args.args) + i])
+ else:
+ default = Parameter.empty
+
+ annotation = ast_unparse(arg.annotation) or Parameter.empty
+ params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
+ default=default, annotation=annotation))
+
+ if args.vararg:
+ annotation = ast_unparse(args.vararg.annotation) or Parameter.empty
+ params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
+ annotation=annotation))
+
+ for i, arg in enumerate(args.kwonlyargs):
+ default = ast_unparse(args.kw_defaults[i])
+ annotation = ast_unparse(arg.annotation) or Parameter.empty
+ params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
+ annotation=annotation))
+
+ if args.kwarg:
+ annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty
+ params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD,
+ annotation=annotation))
+
+ return_annotation = ast_unparse(definition.returns) or Parameter.empty
+
+ return inspect.Signature(params, return_annotation=return_annotation)
class Signature:
@@ -528,12 +559,12 @@ class Signature:
if self.has_retval:
return self.signature.return_annotation
else:
- return inspect.Parameter.empty
+ return Parameter.empty
else:
return None
def format_args(self, show_annotation: bool = True) -> str:
- def get_annotation(param: inspect.Parameter) -> Any:
+ def get_annotation(param: Parameter) -> Any:
if isinstance(param.annotation, str) and param.name in self.annotations:
return self.annotations[param.name]
else:
@@ -585,7 +616,7 @@ class Signature:
args.append(arg.getvalue())
last_kind = param.kind
- if self.return_annotation is inspect.Parameter.empty or show_annotation is False:
+ if self.return_annotation is Parameter.empty or show_annotation is False:
return '(%s)' % ', '.join(args)
else:
if 'return' in self.annotations:
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 7c7300c60..474a704a1 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -443,7 +443,7 @@ def make_id(env: "BuildEnvironment", document: nodes.document,
if prefix:
idformat = prefix + "-%s"
else:
- idformat = document.settings.id_prefix + "%s"
+ idformat = (document.settings.id_prefix or "id") + "%s"
# try to generate node_id by *term*
if prefix and term:
@@ -451,6 +451,10 @@ def make_id(env: "BuildEnvironment", document: nodes.document,
if node_id == prefix:
# *term* is not good to generate a node_id.
node_id = None
+ elif term:
+ node_id = nodes.make_id(term)
+ if node_id == '':
+ node_id = None # fallback to None
while node_id is None or node_id in document.ids:
node_id = idformat % env.new_serialno(prefix)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 23fa208d7..e44211cf9 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -15,13 +15,12 @@ import os
import re
import shutil
import sys
-import time
import warnings
from io import StringIO
from os import path
from typing import Any, Generator, Iterator, List, Tuple
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
try:
# for ALT Linux (#6712)
@@ -144,27 +143,6 @@ def make_filename_from_project(project: str) -> str:
return make_filename(project_suffix_re.sub('', project)).lower()
-def ustrftime(format: str, *args: Any) -> str:
- """[DEPRECATED] strftime for unicode strings."""
- warnings.warn('sphinx.util.osutil.ustrtime is deprecated for removal',
- RemovedInSphinx30Warning, stacklevel=2)
-
- if not args:
- # If time is not specified, try to use $SOURCE_DATE_EPOCH variable
- # See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal
- source_date_epoch = os.getenv('SOURCE_DATE_EPOCH')
- if source_date_epoch is not None:
- time_struct = time.gmtime(float(source_date_epoch))
- args = [time_struct] # type: ignore
- # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError.
- # https://bugs.python.org/issue8304
- try:
- return time.strftime(format, *args)
- except UnicodeEncodeError:
- r = time.strftime(format.encode('unicode-escape').decode(), *args)
- return r.encode().decode('unicode-escape')
-
-
def relpath(path: str, start: str = os.curdir) -> str:
"""Return a relative filepath to *path* either from the current directory or
from an optional *start* directory.
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index ba4f83a71..061bbcb6d 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -52,7 +52,7 @@ class UnicodeMixin:
.. deprecated:: 2.0
"""
- def __str__(self):
+ def __str__(self) -> str:
warnings.warn('UnicodeMixin is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self.__unicode__() # type: ignore
diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py
index 884e7c997..d9d1d1b2c 100644
--- a/sphinx/util/requests.py
+++ b/sphinx/util/requests.py
@@ -14,7 +14,6 @@ from contextlib import contextmanager
from typing import Any, Generator, Union
from urllib.parse import urlsplit
-import pkg_resources
import requests
import sphinx
@@ -37,28 +36,6 @@ except ImportError:
# for requests < 2.4.0
InsecureRequestWarning = None # type: ignore
-try:
- from requests.packages.urllib3.exceptions import InsecurePlatformWarning
-except ImportError:
- try:
- # for Debian-jessie
- from urllib3.exceptions import InsecurePlatformWarning # type: ignore
- except ImportError:
- # for requests < 2.4.0
- InsecurePlatformWarning = None # type: ignore
-
-# try to load requests[security] (but only if SSL is available)
-try:
- import ssl # NOQA
-except ImportError:
- pass
-else:
- try:
- pkg_resources.require(['requests[security]'])
- except (pkg_resources.DistributionNotFound,
- pkg_resources.VersionConflict):
- pass # ignored
-
useragent_header = [('User-Agent',
'Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0')]
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index e5fa3b9b3..502bf361c 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -9,17 +9,14 @@
:license: BSD, see LICENSE for details.
"""
import pickle
-import warnings
from itertools import product, zip_longest
from operator import itemgetter
from os import path
from typing import Any, Dict, Iterator
from uuid import uuid4
-from docutils import nodes
from docutils.nodes import Node
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.transforms import SphinxTransform
if False:
@@ -177,14 +174,6 @@ class UIDTransform(SphinxTransform):
list(merge_doctrees(old_doctree, self.document, env.versioning_condition))
-def prepare(document: nodes.document) -> None:
- """Simple wrapper for UIDTransform."""
- warnings.warn('versioning.prepare() is deprecated. Use UIDTransform instead.',
- RemovedInSphinx30Warning, stacklevel=2)
- transform = UIDTransform(document)
- transform.apply()
-
-
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_transform(UIDTransform)
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index ab5712410..e74c0334f 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -11,7 +11,6 @@
import copy
import os
import posixpath
-import sys
import warnings
from typing import Any, Iterable, Tuple
from typing import cast
@@ -22,7 +21,7 @@ from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import admonitionlabels, _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
@@ -116,10 +115,6 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def visit_desc_signature(self, node: Element) -> None:
# the id is set automatically
self.body.append(self.starttag(node, 'dt'))
- # anchor for per-desc interactive data
- if node.parent['objtype'] != 'describe' \
- and node['ids'] and node['first']:
- self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node: Element) -> None:
if not node.get('is_multiline'):
@@ -831,29 +826,3 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
-
- # --------- METHODS FOR COMPATIBILITY --------------------------------------
-
- @property
- def highlightlang(self) -> str:
- warnings.warn('HTMLTranslator.highlightlang is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.builder.config.highlight_language
-
- @property
- def highlightlang_base(self) -> str:
- warnings.warn('HTMLTranslator.highlightlang_base is deprecated.',
- RemovedInSphinx30Warning)
- return self.builder.config.highlight_language
-
- @property
- def highlightopts(self) -> str:
- warnings.warn('HTMLTranslator.highlightopts is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.builder.config.highlight_options
-
- @property
- def highlightlinenothreshold(self) -> int:
- warnings.warn('HTMLTranslator.highlightlinenothreshold is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return sys.maxsize
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 9f38bf4f3..0c00a1fa4 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -10,7 +10,6 @@
import os
import posixpath
-import sys
import warnings
from typing import Any, Iterable, Tuple
from typing import cast
@@ -21,7 +20,7 @@ from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import admonitionlabels, _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
@@ -88,10 +87,6 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def visit_desc_signature(self, node: Element) -> None:
# the id is set automatically
self.body.append(self.starttag(node, 'dt'))
- # anchor for per-desc interactive data
- if node.parent['objtype'] != 'describe' \
- and node['ids'] and node['first']:
- self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node: Element) -> None:
if not node.get('is_multiline'):
@@ -777,29 +772,3 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
-
- # --------- METHODS FOR COMPATIBILITY --------------------------------------
-
- @property
- def highlightlang(self) -> str:
- warnings.warn('HTMLTranslator.highlightlang is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.builder.config.highlight_language
-
- @property
- def highlightlang_base(self) -> str:
- warnings.warn('HTMLTranslator.highlightlang_base is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.builder.config.highlight_language
-
- @property
- def highlightopts(self) -> str:
- warnings.warn('HTMLTranslator.highlightopts is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return self.builder.config.highlight_options
-
- @property
- def highlightlinenothreshold(self) -> int:
- warnings.warn('HTMLTranslator.highlightlinenothreshold is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return sys.maxsize
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 85579a65a..7a8822585 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -12,11 +12,10 @@
"""
import re
-import sys
import warnings
from collections import defaultdict
from os import path
-from typing import Any, Callable, Dict, Iterable, Iterator, List, Pattern, Tuple, Set, Union
+from typing import Any, Dict, Iterable, Iterator, List, Tuple, Set, Union
from typing import cast
from docutils import nodes, writers
@@ -24,9 +23,7 @@ from docutils.nodes import Element, Node, Text
from sphinx import addnodes
from sphinx import highlighting
-from sphinx.deprecation import (
- RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
-)
+from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.domains import IndexEntry
from sphinx.domains.std import StandardDomain
from sphinx.errors import SphinxError
@@ -132,18 +129,6 @@ class Table:
# (cell = rectangular area)
self.cell_id = 0 # last assigned cell_id
- @property
- def caption_footnotetexts(self) -> List[str]:
- warnings.warn('table.caption_footnotetexts is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return []
-
- @property
- def header_footnotetexts(self) -> List[str]:
- warnings.warn('table.header_footnotetexts is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return []
-
def is_longtable(self) -> bool:
"""True if and only if table uses longtable environment."""
return self.row > 30 or 'longtable' in self.classes
@@ -484,25 +469,6 @@ class LaTeXTranslator(SphinxTranslator):
self.body = self.bodystack.pop()
return body
- def restrict_footnote(self, node: Element) -> None:
- warnings.warn('LaTeXWriter.restrict_footnote() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- if self.footnote_restricted is None:
- self.footnote_restricted = node
- self.pending_footnotes = []
-
- def unrestrict_footnote(self, node: Element) -> None:
- warnings.warn('LaTeXWriter.unrestrict_footnote() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- if self.footnote_restricted == node:
- self.footnote_restricted = None
- for footnode in self.pending_footnotes:
- footnode['footnotetext'] = True
- footnode.walkabout(self)
- self.pending_footnotes = []
-
def format_docclass(self, docclass: str) -> str:
""" prepends prefix to sphinx document classes
"""
@@ -1501,8 +1467,8 @@ class LaTeXTranslator(SphinxTranslator):
def depart_attribution(self, node: Element) -> None:
self.body.append('\n\\end{flushright}\n')
- def visit_index(self, node: Element, scre: Pattern = None) -> None:
- def escape(value):
+ def visit_index(self, node: Element) -> None:
+ def escape(value: str) -> str:
value = self.encode(value)
value = value.replace(r'\{', r'\sphinxleftcurlybrace{}')
value = value.replace(r'\}', r'\sphinxrightcurlybrace{}')
@@ -1512,17 +1478,13 @@ class LaTeXTranslator(SphinxTranslator):
value = value.replace('|', r'\textbar{}')
return value
- def style(string):
+ def style(string: str) -> str:
match = EXTRA_RE.match(string)
if match:
return match.expand(r'\\spxentry{\1}\\spxextra{\2}')
else:
return '\\spxentry{%s}' % string
- if scre:
- warnings.warn(('LaTeXTranslator.visit_index() optional argument '
- '"scre" is deprecated. It is ignored.'),
- RemovedInSphinx30Warning, stacklevel=2)
if not node.get('inline', True):
self.body.append('\n')
entries = node['entries']
@@ -2122,61 +2084,6 @@ class LaTeXTranslator(SphinxTranslator):
RemovedInSphinx40Warning, stacklevel=2)
return 0
- @property
- def footnotestack(self) -> List[Dict[str, List[Union[collected_footnote, bool]]]]:
- warnings.warn('LaTeXWriter.footnotestack is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return []
-
- @property
- def bibitems(self) -> List[List[str]]:
- warnings.warn('LaTeXTranslator.bibitems() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return []
-
- @property
- def in_container_literal_block(self) -> int:
- warnings.warn('LaTeXTranslator.in_container_literal_block is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return 0
-
- @property
- def next_section_ids(self) -> Set[str]:
- warnings.warn('LaTeXTranslator.next_section_ids is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return set()
-
- @property
- def next_hyperlink_ids(self) -> Dict:
- warnings.warn('LaTeXTranslator.next_hyperlink_ids is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return {}
-
- def push_hyperlink_ids(self, figtype: str, ids: Set[str]) -> None:
- warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- pass
-
- def pop_hyperlink_ids(self, figtype: str) -> Set[str]:
- warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return set()
-
- @property
- def hlsettingstack(self) -> List[List[Union[str, int]]]:
- warnings.warn('LaTeXTranslator.hlsettingstack is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
- return [[self.builder.config.highlight_language, sys.maxsize]]
-
- def check_latex_elements(self) -> None:
- warnings.warn('check_latex_elements() is deprecated.',
- RemovedInSphinx30Warning, stacklevel=2)
-
- for key in self.builder.config.latex_elements:
- if key not in self.elements:
- msg = __("Unknown configure key: latex_elements[%r] is ignored.")
- logger.warning(msg % key)
-
def babel_defmacro(self, name: str, definition: str) -> str:
warnings.warn('babel_defmacro() is deprecated.',
RemovedInSphinx40Warning)
@@ -2190,15 +2097,6 @@ class LaTeXTranslator(SphinxTranslator):
return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix))
- def _make_visit_admonition(name: str) -> Callable[["LaTeXTranslator", Element], None]: # type: ignore # NOQA
- warnings.warn('LaTeXTranslator._make_visit_admonition() is deprecated.',
- RemovedInSphinx30Warning)
-
- def visit_admonition(self: "LaTeXTranslator", node: Element) -> None:
- self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' %
- (name, admonitionlabels[name]))
- return visit_admonition
-
def generate_numfig_format(self, builder: "LaTeXBuilder") -> str:
warnings.warn('generate_numfig_format() is deprecated.',
RemovedInSphinx40Warning)
@@ -2239,18 +2137,11 @@ class LaTeXTranslator(SphinxTranslator):
# Import old modules here for compatibility
from sphinx.builders.latex import constants # NOQA
-from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA
from sphinx.builders.latex.util import ExtBabel # NOQA
deprecated_alias('sphinx.writers.latex',
{
- 'ShowUrlsTransform': ShowUrlsTransform,
- 'URI_SCHEMES': URI_SCHEMES,
- },
- RemovedInSphinx30Warning)
-deprecated_alias('sphinx.writers.latex',
- {
'ADDITIONAL_SETTINGS': constants.ADDITIONAL_SETTINGS,
'DEFAULT_SETTINGS': constants.DEFAULT_SETTINGS,
'LUALATEX_DEFAULT_FONTPKG': constants.LUALATEX_DEFAULT_FONTPKG,
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 65b4f1a0e..9c30244e9 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -10,16 +10,14 @@
import re
import textwrap
-import warnings
from os import path
-from typing import Any, Callable, Dict, Iterable, Iterator, List, Pattern, Set, Tuple, Union
+from typing import Any, Dict, Iterable, Iterator, List, Pattern, Set, Tuple, Union
from typing import cast
from docutils import nodes, writers
from docutils.nodes import Element, Node, Text
from sphinx import addnodes, __display_version__
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.domains import IndexEntry
from sphinx.domains.index import IndexDomain
from sphinx.errors import ExtensionError
@@ -1528,11 +1526,3 @@ class TexinfoTranslator(SphinxTranslator):
self.body.append('\n\n@example\n%s\n@end example\n\n' %
self.escape_arg(node.astext()))
raise nodes.SkipNode
-
- def _make_visit_admonition(name: str) -> Callable[["TexinfoTranslator", Element], None]: # type: ignore # NOQA
- warnings.warn('TexinfoTranslator._make_visit_admonition() is deprecated.',
- RemovedInSphinx30Warning)
-
- def visit(self: "TexinfoTranslator", node: Element) -> None:
- self.visit_admonition(node, admonitionlabels[name])
- return visit
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 882007d7e..b2ccd7b89 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -11,9 +11,8 @@ import math
import os
import re
import textwrap
-import warnings
from itertools import groupby, chain
-from typing import Any, Callable, Dict, List, Iterable, Optional, Set, Tuple, Union
+from typing import Any, Dict, Generator, List, Iterable, Optional, Set, Tuple, Union
from typing import cast
from docutils import nodes, writers
@@ -21,7 +20,6 @@ from docutils.nodes import Element, Node, Text
from docutils.utils import column_width
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.locale import admonitionlabels, _
from sphinx.util.docutils import SphinxTranslator
@@ -34,23 +32,23 @@ class Cell:
"""Represents a cell in a table.
It can span on multiple columns or on multiple lines.
"""
- def __init__(self, text="", rowspan=1, colspan=1):
+ def __init__(self, text: str = "", rowspan: int = 1, colspan: int = 1) -> None:
self.text = text
self.wrapped = [] # type: List[str]
self.rowspan = rowspan
self.colspan = colspan
- self.col = None
- self.row = None
+ self.col = None # type: Optional[int]
+ self.row = None # type: Optional[int]
- def __repr__(self):
+ def __repr__(self) -> str:
return "<Cell {!r} {}v{}/{}>{}>".format(
self.text, self.row, self.rowspan, self.col, self.colspan
)
- def __hash__(self):
+ def __hash__(self) -> int:
return hash((self.col, self.row))
- def wrap(self, width):
+ def wrap(self, width: int) -> None:
self.wrapped = my_wrap(self.text, width)
@@ -100,7 +98,7 @@ class Table:
+--------+--------+
"""
- def __init__(self, colwidth=None):
+ def __init__(self, colwidth: List[int] = None) -> None:
self.lines = [] # type: List[List[Cell]]
self.separator = 0
self.colwidth = (colwidth if colwidth is not None
@@ -108,19 +106,19 @@ class Table:
self.current_line = 0
self.current_col = 0
- def add_row(self):
+ def add_row(self) -> None:
"""Add a row to the table, to use with ``add_cell()``. It is not needed
to call ``add_row()`` before the first ``add_cell()``.
"""
self.current_line += 1
self.current_col = 0
- def set_separator(self):
+ def set_separator(self) -> None:
"""Sets the separator below the current line.
"""
self.separator = len(self.lines)
- def add_cell(self, cell):
+ def add_cell(self, cell: Cell) -> None:
"""Add a cell to the current line, to use with ``add_row()``. To add
a cell spanning on multiple lines or rows, simply set the
``cell.colspan`` or ``cell.rowspan`` BEFORE inserting it to
@@ -131,13 +129,13 @@ class Table:
self[self.current_line, self.current_col] = cell
self.current_col += cell.colspan
- def __getitem__(self, pos):
+ def __getitem__(self, pos: Tuple[int, int]) -> Cell:
line, col = pos
self._ensure_has_line(line + 1)
self._ensure_has_column(col + 1)
return self.lines[line][col]
- def __setitem__(self, pos, cell):
+ def __setitem__(self, pos: Tuple[int, int], cell: Cell) -> None:
line, col = pos
self._ensure_has_line(line + cell.rowspan)
self._ensure_has_column(col + cell.colspan)
@@ -147,19 +145,19 @@ class Table:
cell.row = line
cell.col = col
- def _ensure_has_line(self, line):
+ def _ensure_has_line(self, line: int) -> None:
while len(self.lines) < line:
self.lines.append([])
- def _ensure_has_column(self, col):
+ def _ensure_has_column(self, col: int) -> None:
for line in self.lines:
while len(line) < col:
line.append(None)
- def __repr__(self):
+ def __repr__(self) -> str:
return "\n".join(repr(line) for line in self.lines)
- def cell_width(self, cell, source):
+ def cell_width(self, cell: Cell, source: List[int]) -> int:
"""Give the cell width, according to the given source (either
``self.colwidth`` or ``self.measured_widths``).
This take into account cells spanning on multiple columns.
@@ -170,7 +168,7 @@ class Table:
return width + (cell.colspan - 1) * 3
@property
- def cells(self):
+ def cells(self) -> Generator[Cell, None, None]:
seen = set() # type: Set[Cell]
for lineno, line in enumerate(self.lines):
for colno, cell in enumerate(line):
@@ -178,7 +176,7 @@ class Table:
yield cell
seen.add(cell)
- def rewrap(self):
+ def rewrap(self) -> None:
"""Call ``cell.wrap()`` on all cells, and measure each column width
after wrapping (result written in ``self.measured_widths``).
"""
@@ -191,7 +189,7 @@ class Table:
for col in range(cell.col, cell.col + cell.colspan):
self.measured_widths[col] = max(self.measured_widths[col], width)
- def physical_lines_for_line(self, line):
+ def physical_lines_for_line(self, line: List[Cell]) -> int:
"""From a given line, compute the number of physical lines it spans
due to text wrapping.
"""
@@ -200,7 +198,7 @@ class Table:
physical_lines = max(physical_lines, len(cell.wrapped))
return physical_lines
- def __str__(self):
+ def __str__(self) -> str:
out = []
self.rewrap()
@@ -1176,11 +1174,3 @@ class TextTranslator(SphinxTranslator):
def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
-
- def _make_depart_admonition(name: str) -> Callable[["TextTranslator", Element], None]: # type: ignore # NOQA
- warnings.warn('TextTranslator._make_depart_admonition() is deprecated.',
- RemovedInSphinx30Warning)
-
- def depart_admonition(self: "TextTranslator", node: Element) -> None:
- self.end_state(first=admonitionlabels[name] + ': ')
- return depart_admonition