diff --git a/CHANGES b/CHANGES
index c4f4c3c52..10b188ca0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,8 @@ Dependencies
Incompatible changes
--------------------
+* apidoc: template files are renamed to ``.rst_t``
+
Deprecated
----------
@@ -25,22 +27,37 @@ Features added
--------------
* #5124: graphviz: ``:graphviz_dot:`` option is renamed to ``:layout:``
+* #1464: html: emit a warning if :confval:`html_static_path` and
+ :confval:`html_extra_path` directories are inside output directory
+* #6514: html: Add a label to search input for accessability purposes
+* #5602: apidoc: Add ``--templatedir`` option
+* #6475: Add ``override`` argument to ``app.add_autodocumenter()``
+* #6533: LaTeX: refactor visit_enumerated_list() to use ``\sphinxsetlistlabels``
Bugs fixed
----------
* py domain: duplicated warning does not point the location of source code
-* #5592: std domain: :rst:dir:`option` directive registers an index entry for
- each comma separated option
+* #6499: html: Sphinx never updates a copy of :confval:`html_logo` even if
+ original file has changed
* #1125: html theme: scrollbar is hard to see on classic theme and macOS
* #5502: linkcheck: Consider HTTP 503 response as not an error
* #6439: Make generated download links reproducible
* #6486: UnboundLocalError is raised if broken extension installed
+* #6498: autosummary: crashed with wrong autosummary_generate setting
+* #6507: autosummary: crashes without no autosummary_generate setting
+* #6511: LaTeX: autonumbered list can not be customized in LaTeX
+ since Sphinx 1.8.0 (refs: #6533)
+* #6531: Failed to load last environment object when extension added
+* #736: Invalid sort in pair index
+* #6527: :confval:`last_updated` wrongly assumes timezone as UTC
+* #5592: std domain: :rst:dir:`option` directive registers an index entry for
+ each comma separated option
Testing
--------
-Release 2.1.2 (in development)
+Release 2.1.3 (in development)
==============================
Dependencies
@@ -61,6 +78,15 @@ Bugs fixed
Testing
--------
+Release 2.1.2 (released Jun 19, 2019)
+=====================================
+
+Bugs fixed
+----------
+
+* #6497: custom lexers fails highlighting when syntax error
+* #6478, #6488: info field lists are incorrectly recognized
+
Release 2.1.1 (released Jun 10, 2019)
=====================================
diff --git a/doc/man/sphinx-apidoc.rst b/doc/man/sphinx-apidoc.rst
index aef497e0d..78c0735cb 100644
--- a/doc/man/sphinx-apidoc.rst
+++ b/doc/man/sphinx-apidoc.rst
@@ -126,6 +126,30 @@ These options are used when :option:`--full` is specified:
Sets the project release to put in generated files (see :confval:`release`).
+.. rubric:: Project templating
+
+.. versionadded:: 2.2
+ Project templating options for sphinx-apidoc
+
+.. option:: -t, --templatedir=TEMPLATEDIR
+
+ Template directory for template files. You can modify the templates of
+ sphinx project files generated by apidoc. Following Jinja2 template
+ files are allowed:
+
+ * ``module.rst_t``
+ * ``package.rst_t``
+ * ``toc.rst_t``
+ * ``master_doc.rst_t``
+ * ``conf.py_t``
+ * ``Makefile_t``
+ * ``Makefile.new_t``
+ * ``make.bat_t``
+ * ``make.bat.new_t``
+
+ In detail, please refer the system template files Sphinx provides.
+ (``sphinx/templates/apidoc`` and ``sphinx/templates/quickstart``)
+
Environment
-----------
diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst
index 8fdee214b..74c0b609d 100644
--- a/doc/usage/extensions/autodoc.rst
+++ b/doc/usage/extensions/autodoc.rst
@@ -243,21 +243,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
These work exactly like :rst:dir:`autoclass` etc.,
but do not offer the options used for automatic member documentation.
- :rst:dir:`autodata` and :rst:dir:`autoattribute` support
- the ``annotation`` option.
- Without this option, the representation of the object
- will be shown in the documentation.
- When the option is given without arguments,
- only the name of the object will be printed::
+ :rst:dir:`autodata` and :rst:dir:`autoattribute` support the ``annotation``
+ option. The option controls how the value of variable is shown. If specified
+ without arguments, only the name of the variable will be printed, and its value
+ is not shown::
.. autodata:: CD_DRIVE
:annotation:
- You can tell sphinx what should be printed after the name::
+ If the option specified with arguments, it is printed after the name as a value
+ of the variable::
.. autodata:: CD_DRIVE
:annotation: = your CD device name
+ By default, without ``annotation`` option, Sphinx tries to obtain the value of
+ the variable and print it after the name.
+
For module data members and class attributes, documentation can either be put
into a comment with special formatting (using a ``#:`` to start the comment
instead of just ``#``), or in a docstring *after* the definition. Comments
diff --git a/setup.py b/setup.py
index 91b3e12cc..96bffccfa 100644
--- a/setup.py
+++ b/setup.py
@@ -47,7 +47,7 @@ extras_require = {
'html5lib',
'flake8>=3.5.0',
'flake8-import-order',
- 'mypy>=0.590',
+ 'mypy>=0.711',
'docutils-stubs',
],
}
diff --git a/sphinx/application.py b/sphinx/application.py
index 96471be0c..a841f52b1 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -1054,8 +1054,8 @@ class Sphinx:
else:
lexer_classes[alias] = lexer
- def add_autodocumenter(self, cls):
- # type: (Any) -> None
+ def add_autodocumenter(self, cls, override=False):
+ # type: (Any, bool) -> None
"""Register a new documenter class for the autodoc extension.
Add *cls* as a new documenter class for the :mod:`sphinx.ext.autodoc`
@@ -1067,11 +1067,13 @@ class Sphinx:
.. todo:: Add real docs for Documenter and subclassing
.. versionadded:: 0.6
+ .. versionchanged:: 2.2
+ Add *override* keyword.
"""
logger.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext.autodoc.directive import AutodocDirective
self.registry.add_documenter(cls.objtype, cls)
- self.add_directive('auto' + cls.objtype, AutodocDirective)
+ self.add_directive('auto' + cls.objtype, AutodocDirective, override=override)
def add_autodoc_attrgetter(self, typ, getter):
# type: (Type, Callable[[Any, str, Any], Any]) -> None
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 1fd9e6cac..1a7054bdc 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -38,8 +38,7 @@ from sphinx.highlighting import PygmentsBridge
from sphinx.locale import _, __
from sphinx.search import js_index
from sphinx.theming import HTMLThemeFactory
-from sphinx.util import logging, status_iterator
-from sphinx.util.console import bold # type: ignore
+from sphinx.util import logging, progress_message, status_iterator
from sphinx.util.docutils import is_html5_writer_available, new_document
from sphinx.util.fileutil import copy_asset
from sphinx.util.i18n import format_date
@@ -626,6 +625,7 @@ class StandaloneHTMLBuilder(Builder):
def finish(self) -> None:
self.finish_tasks.add_task(self.gen_indices)
+ self.finish_tasks.add_task(self.gen_pages_from_extensions)
self.finish_tasks.add_task(self.gen_additional_pages)
self.finish_tasks.add_task(self.copy_image_files)
self.finish_tasks.add_task(self.copy_download_files)
@@ -636,9 +636,8 @@ class StandaloneHTMLBuilder(Builder):
# dump the search index
self.handle_finish()
+ @progress_message(__('generating indices'))
def gen_indices(self) -> None:
- logger.info(bold(__('generating indices...')), nonl=True)
-
# the global general index
if self.use_index:
self.write_genindex()
@@ -646,16 +645,14 @@ class StandaloneHTMLBuilder(Builder):
# the global domain-specific indices
self.write_domain_indices()
- logger.info('')
-
- def gen_additional_pages(self) -> None:
+ def gen_pages_from_extensions(self) -> None:
# pages from extensions
for pagelist in self.events.emit('html-collect-pages'):
for pagename, context, template in pagelist:
self.handle_page(pagename, context, template)
- logger.info(bold(__('writing additional pages...')), nonl=True)
-
+ @progress_message(__('writing additional pages'))
+ def gen_additional_pages(self) -> None:
# additional pages from conf.py
for pagename, template in self.config.html_additional_pages.items():
logger.info(' ' + pagename, nonl=True)
@@ -672,8 +669,6 @@ class StandaloneHTMLBuilder(Builder):
fn = path.join(self.outdir, '_static', 'opensearch.xml')
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
- logger.info('')
-
def write_genindex(self) -> None:
# the total count of lines for each index letter, used to distribute
# the entries into two columns
@@ -746,84 +741,77 @@ class StandaloneHTMLBuilder(Builder):
logger.warning(__('cannot copy downloadable file %r: %s'),
path.join(self.srcdir, src), err)
+ def create_pygments_style_file(self) -> None:
+ """create a style file for pygments."""
+ with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
+ f.write(self.highlighter.get_stylesheet())
+
+ def copy_translation_js(self) -> None:
+ """Copy a JavaScript file for translations."""
+ if self.config.language is not None:
+ jsfile = self._get_translations_js()
+ if jsfile:
+ copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
+
+ def copy_stemmer_js(self) -> None:
+ """Copy a JavaScript file for stemmer."""
+ if self.indexer is not None:
+ jsfile = self.indexer.get_js_stemmer_rawcode()
+ if jsfile:
+ copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
+
+ def copy_theme_static_files(self, context: Dict) -> None:
+ if self.theme:
+ for entry in self.theme.get_theme_dirs()[::-1]:
+ copy_asset(path.join(entry, 'static'),
+ path.join(self.outdir, '_static'),
+ excluded=DOTFILES, context=context, renderer=self.templates)
+
+ def copy_html_static_files(self, context: Dict) -> None:
+ excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
+ for entry in self.config.html_static_path:
+ copy_asset(path.join(self.confdir, entry),
+ path.join(self.outdir, '_static'),
+ excluded, context=context, renderer=self.templates)
+
+ def copy_html_logo(self) -> None:
+ if self.config.html_logo:
+ copy_asset(path.join(self.confdir, self.config.html_logo),
+ path.join(self.outdir, '_static'))
+
+ def copy_html_favicon(self) -> None:
+ if self.config.html_favicon:
+ copy_asset(path.join(self.confdir, self.config.html_favicon),
+ path.join(self.outdir, '_static'))
+
def copy_static_files(self) -> None:
try:
- # copy static files
- logger.info(bold(__('copying static files... ')), nonl=True)
- ensuredir(path.join(self.outdir, '_static'))
- # first, create pygments style file
- with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
- f.write(self.highlighter.get_stylesheet())
- # then, copy translations JavaScript file
- if self.config.language is not None:
- jsfile = self._get_translations_js()
- if jsfile:
- copyfile(jsfile, path.join(self.outdir, '_static',
- 'translations.js'))
+ with progress_message(__('copying static files... ')):
+ ensuredir(path.join(self.outdir, '_static'))
- # copy non-minified stemmer JavaScript file
- if self.indexer is not None:
- jsfile = self.indexer.get_js_stemmer_rawcode()
- if jsfile:
- copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
+ # prepare context for templates
+ context = self.globalcontext.copy()
+ if self.indexer is not None:
+ context.update(self.indexer.context_for_searchtool())
- ctx = self.globalcontext.copy()
-
- # add context items for search function used in searchtools.js_t
- if self.indexer is not None:
- ctx.update(self.indexer.context_for_searchtool())
-
- # then, copy over theme-supplied static files
- if self.theme:
- for theme_path in self.theme.get_theme_dirs()[::-1]:
- entry = path.join(theme_path, 'static')
- copy_asset(entry, path.join(self.outdir, '_static'), excluded=DOTFILES,
- context=ctx, renderer=self.templates)
- # then, copy over all user-supplied static files
- excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
- for static_path in self.config.html_static_path:
- entry = path.join(self.confdir, static_path)
- if not path.exists(entry):
- logger.warning(__('html_static_path entry %r does not exist'), entry)
- continue
- copy_asset(entry, path.join(self.outdir, '_static'), excluded,
- context=ctx, renderer=self.templates)
- # copy logo and favicon files if not already in static path
- if self.config.html_logo:
- logobase = path.basename(self.config.html_logo)
- logotarget = path.join(self.outdir, '_static', logobase)
- if not path.isfile(path.join(self.confdir, self.config.html_logo)):
- logger.warning(__('logo file %r does not exist'), self.config.html_logo)
- elif not path.isfile(logotarget):
- copyfile(path.join(self.confdir, self.config.html_logo),
- logotarget)
- if self.config.html_favicon:
- iconbase = path.basename(self.config.html_favicon)
- icontarget = path.join(self.outdir, '_static', iconbase)
- if not path.isfile(path.join(self.confdir, self.config.html_favicon)):
- logger.warning(__('favicon file %r does not exist'),
- self.config.html_favicon)
- elif not path.isfile(icontarget):
- copyfile(path.join(self.confdir, self.config.html_favicon),
- icontarget)
- logger.info(__('done'))
+ self.create_pygments_style_file()
+ self.copy_translation_js()
+ self.copy_stemmer_js()
+ self.copy_theme_static_files(context)
+ self.copy_html_static_files(context)
+ self.copy_html_logo()
+ self.copy_html_favicon()
except OSError as err:
logger.warning(__('cannot copy static file %r'), err)
def copy_extra_files(self) -> None:
+ """copy html_extra_path files."""
try:
- # copy html_extra_path files
- logger.info(bold(__('copying extra files... ')), nonl=True)
- excluded = Matcher(self.config.exclude_patterns)
-
- for extra_path in self.config.html_extra_path:
- entry = path.join(self.confdir, extra_path)
- if not path.exists(entry):
- logger.warning(__('html_extra_path entry %r does not exist'), entry)
- continue
-
- copy_asset(entry, self.outdir, excluded)
- logger.info(__('done'))
+ with progress_message(__('copying extra files')):
+ excluded = Matcher(self.config.exclude_patterns)
+ for extra_path in self.config.html_extra_path:
+ entry = path.join(self.confdir, extra_path)
+ copy_asset(entry, self.outdir, excluded)
except OSError as err:
logger.warning(__('cannot copy extra file %r'), err)
@@ -1067,27 +1055,23 @@ class StandaloneHTMLBuilder(Builder):
self.finish_tasks.add_task(self.dump_search_index)
self.finish_tasks.add_task(self.dump_inventory)
+ @progress_message(__('dumping object inventory'))
def dump_inventory(self) -> None:
- logger.info(bold(__('dumping object inventory... ')), nonl=True)
InventoryFile.dump(path.join(self.outdir, INVENTORY_FILENAME), self.env, self)
- logger.info(__('done'))
def dump_search_index(self) -> None:
- logger.info(
- bold(__('dumping search index in %s ... ') % self.indexer.label()),
- nonl=True)
- self.indexer.prune(self.env.all_docs)
- searchindexfn = path.join(self.outdir, self.searchindex_filename)
- # first write to a temporary file, so that if dumping fails,
- # the existing index won't be overwritten
- if self.indexer_dumps_unicode:
- with open(searchindexfn + '.tmp', 'w', encoding='utf-8') as ft:
- self.indexer.dump(ft, self.indexer_format)
- else:
- with open(searchindexfn + '.tmp', 'wb') as fb:
- self.indexer.dump(fb, self.indexer_format)
- movefile(searchindexfn + '.tmp', searchindexfn)
- logger.info(__('done'))
+ with progress_message(__('dumping search index in %s') % self.indexer.label()):
+ self.indexer.prune(self.env.all_docs)
+ searchindexfn = path.join(self.outdir, self.searchindex_filename)
+ # first write to a temporary file, so that if dumping fails,
+ # the existing index won't be overwritten
+ if self.indexer_dumps_unicode:
+ with open(searchindexfn + '.tmp', 'w', encoding='utf-8') as ft:
+ self.indexer.dump(ft, self.indexer_format)
+ else:
+ with open(searchindexfn + '.tmp', 'wb') as fb:
+ self.indexer.dump(fb, self.indexer_format)
+ movefile(searchindexfn + '.tmp', searchindexfn)
def convert_html_css_files(app: Sphinx, config: Config) -> None:
@@ -1166,6 +1150,44 @@ def validate_math_renderer(app: Sphinx) -> None:
raise ConfigError(__('Unknown math_renderer %r is given.') % name)
+def validate_html_extra_path(app: Sphinx, config: Config) -> None:
+ """Check html_extra_paths setting."""
+ for entry in config.html_extra_path[:]:
+ extra_path = path.normpath(path.join(app.confdir, entry))
+ if not path.exists(extra_path):
+ logger.warning(__('html_extra_path entry %r does not exist'), entry)
+ config.html_extra_path.remove(entry)
+ elif path.commonpath([app.outdir, extra_path]) == app.outdir:
+ logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry)
+ config.html_extra_path.remove(entry)
+
+
+def validate_html_static_path(app: Sphinx, config: Config) -> None:
+ """Check html_static_paths setting."""
+ for entry in config.html_static_path[:]:
+ static_path = path.normpath(path.join(app.confdir, entry))
+ if not path.exists(static_path):
+ logger.warning(__('html_static_path entry %r does not exist'), entry)
+ config.html_static_path.remove(entry)
+ elif path.commonpath([app.outdir, static_path]) == app.outdir:
+ logger.warning(__('html_static_path entry %r is placed inside outdir'), entry)
+ config.html_static_path.remove(entry)
+
+
+def validate_html_logo(app: Sphinx, config: Config) -> None:
+ """Check html_logo setting."""
+ if config.html_logo and not path.isfile(path.join(app.confdir, config.html_logo)):
+ logger.warning(__('logo file %r does not exist'), config.html_logo)
+ config.html_logo = None # type: ignore
+
+
+def validate_html_favicon(app: Sphinx, config: Config) -> None:
+ """Check html_favicon setting."""
+ if config.html_favicon and not path.isfile(path.join(app.confdir, config.html_favicon)):
+ logger.warning(__('favicon file %r does not exist'), config.html_favicon)
+ config.html_favicon = None # type: ignore
+
+
# for compatibility
import sphinx.builders.dirhtml # NOQA
import sphinx.builders.singlehtml # NOQA
@@ -1221,6 +1243,10 @@ def setup(app: Sphinx) -> Dict[str, Any]:
# event handlers
app.connect('config-inited', convert_html_css_files)
app.connect('config-inited', convert_html_js_files)
+ app.connect('config-inited', validate_html_extra_path)
+ app.connect('config-inited', validate_html_static_path)
+ app.connect('config-inited', validate_html_logo)
+ app.connect('config-inited', validate_html_favicon)
app.connect('builder-inited', validate_math_renderer)
app.connect('html-page-context', setup_js_tag_helper)
diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py
index 810d69078..d2f6c13b7 100644
--- a/sphinx/cmd/build.py
+++ b/sphinx/cmd/build.py
@@ -14,6 +14,7 @@ import multiprocessing
import os
import sys
import traceback
+from typing import Any, IO, List
from docutils.utils import SystemMessage
@@ -26,13 +27,8 @@ from sphinx.util import Tee, format_exception_cut_frames, save_traceback
from sphinx.util.console import red, nocolor, color_terminal, terminal_safe # type: ignore
from sphinx.util.docutils import docutils_namespace, patch_docutils
-if False:
- # For type annotation
- from typing import Any, IO, List, Union # NOQA
-
-def handle_exception(app, args, exception, stderr=sys.stderr):
- # type: (Sphinx, Any, Union[Exception, KeyboardInterrupt], IO) -> None
+def handle_exception(app: Sphinx, args: Any, exception: BaseException, stderr: IO = sys.stderr) -> None: # NOQA
if args.pdb:
import pdb
print(red(__('Exception occurred while building, starting debugger:')),
@@ -82,8 +78,7 @@ def handle_exception(app, args, exception, stderr=sys.stderr):
file=stderr)
-def jobs_argument(value):
- # type: (str) -> int
+def jobs_argument(value: str) -> int:
"""
Special type to handle 'auto' flags passed to 'sphinx-build' via -j flag. Can
be expanded to handle other special scaling requests, such as setting job count
@@ -99,8 +94,7 @@ def jobs_argument(value):
return jobs
-def get_parser():
- # type: () -> argparse.ArgumentParser
+def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]',
epilog=__('For more information, visit .'),
@@ -195,15 +189,13 @@ files can be built by specifying individual filenames.
return parser
-def make_main(argv=sys.argv[1:]):
- # type: (List[str]) -> int
+def make_main(argv: List[str] = sys.argv[1:]) -> int:
"""Sphinx build "make mode" entry."""
from sphinx.cmd import make_mode
return make_mode.run_make_mode(argv[1:])
-def build_main(argv=sys.argv[1:]):
- # type: (List[str]) -> int
+def build_main(argv: List[str] = sys.argv[1:]) -> int:
"""Sphinx build "main" command-line entry."""
parser = get_parser()
@@ -288,8 +280,7 @@ def build_main(argv=sys.argv[1:]):
return 2
-def main(argv=sys.argv[1:]):
- # type: (List[str]) -> int
+def main(argv: List[str] = sys.argv[1:]) -> int:
sphinx.locale.setlocale(locale.LC_ALL, '')
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py
index e87aa02fc..508b60959 100644
--- a/sphinx/cmd/make_mode.py
+++ b/sphinx/cmd/make_mode.py
@@ -18,16 +18,13 @@ import os
import subprocess
import sys
from os import path
+from typing import List
import sphinx
from sphinx.cmd.build import build_main
from sphinx.util.console import color_terminal, nocolor, bold, blue # type: ignore
from sphinx.util.osutil import cd, rmtree
-if False:
- # For type annotation
- from typing import List # NOQA
-
BUILDERS = [
("", "html", "to make standalone HTML files"),
@@ -58,20 +55,16 @@ BUILDERS = [
class Make:
-
- def __init__(self, srcdir, builddir, opts):
- # type: (str, str, List[str]) -> None
+ def __init__(self, srcdir: str, builddir: str, opts: List[str]) -> None:
self.srcdir = srcdir
self.builddir = builddir
self.opts = opts
self.makecmd = os.environ.get('MAKE', 'make') # refer $MAKE to determine make command
- def builddir_join(self, *comps):
- # type: (str) -> str
+ def builddir_join(self, *comps: str) -> str:
return path.join(self.builddir, *comps)
- def build_clean(self):
- # type: () -> int
+ def build_clean(self) -> int:
srcdir = path.abspath(self.srcdir)
builddir = path.abspath(self.builddir)
if not path.exists(self.builddir):
@@ -90,8 +83,7 @@ class Make:
rmtree(self.builddir_join(item))
return 0
- def build_help(self):
- # type: () -> None
+ def build_help(self) -> None:
if not color_terminal():
nocolor()
@@ -101,8 +93,7 @@ class Make:
if not osname or os.name == osname:
print(' %s %s' % (blue(bname.ljust(10)), description))
- def build_latexpdf(self):
- # type: () -> int
+ def build_latexpdf(self) -> int:
if self.run_generic_build('latex') > 0:
return 1
@@ -117,8 +108,7 @@ class Make:
print('Error: Failed to run: %s' % makecmd)
return 1
- def build_latexpdfja(self):
- # type: () -> int
+ def build_latexpdfja(self) -> int:
if self.run_generic_build('latex') > 0:
return 1
@@ -133,8 +123,7 @@ class Make:
print('Error: Failed to run: %s' % makecmd)
return 1
- def build_info(self):
- # type: () -> int
+ def build_info(self) -> int:
if self.run_generic_build('texinfo') > 0:
return 1
try:
@@ -144,15 +133,13 @@ class Make:
print('Error: Failed to run: %s' % self.makecmd)
return 1
- def build_gettext(self):
- # type: () -> int
+ def build_gettext(self) -> int:
dtdir = self.builddir_join('gettext', '.doctrees')
if self.run_generic_build('gettext', doctreedir=dtdir) > 0:
return 1
return 0
- def run_generic_build(self, builder, doctreedir=None):
- # type: (str, str) -> int
+ def run_generic_build(self, builder: str, doctreedir: str = None) -> int:
# compatibility with old Makefile
papersize = os.getenv('PAPER', '')
opts = self.opts
@@ -168,8 +155,7 @@ class Make:
return build_main(args + opts)
-def run_make_mode(args):
- # type: (List[str]) -> int
+def run_make_mode(args: List[str]) -> int:
if len(args) < 3:
print('Error: at least 3 arguments (builder, source '
'dir, build dir) are required.', file=sys.stderr)
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index 2a509bacb..846750a68 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -17,6 +17,7 @@ import time
import warnings
from collections import OrderedDict
from os import path
+from typing import Any, Callable, Dict, List, Pattern, Union
# try to import readline, unix specific enhancement
try:
@@ -42,10 +43,6 @@ from sphinx.util.console import ( # type: ignore
from sphinx.util.osutil import ensuredir
from sphinx.util.template import SphinxRenderer
-if False:
- # For type annotation
- from typing import Any, Callable, Dict, List, Pattern, Union # NOQA
-
TERM_ENCODING = getattr(sys.stdin, 'encoding', None) # RemovedInSphinx40Warning
EXTENSIONS = OrderedDict([
@@ -84,8 +81,7 @@ else:
# function to get input from terminal -- overridden by the test suite
-def term_input(prompt):
- # type: (str) -> str
+def term_input(prompt: str) -> str:
if sys.platform == 'win32':
# Important: On windows, readline is not enabled by default. In these
# environment, escape sequences have been broken. To avoid the
@@ -100,58 +96,49 @@ class ValidationError(Exception):
"""Raised for validation errors."""
-def is_path(x):
- # type: (str) -> str
+def is_path(x: str) -> str:
x = path.expanduser(x)
if not path.isdir(x):
raise ValidationError(__("Please enter a valid path name."))
return x
-def allow_empty(x):
- # type: (str) -> str
+def allow_empty(x: str) -> str:
return x
-def nonempty(x):
- # type: (str) -> str
+def nonempty(x: str) -> str:
if not x:
raise ValidationError(__("Please enter some text."))
return x
-def choice(*l):
- # type: (str) -> Callable[[str], str]
- def val(x):
- # type: (str) -> str
+def choice(*l: str) -> Callable[[str], str]:
+ def val(x: str) -> str:
if x not in l:
raise ValidationError(__('Please enter one of %s.') % ', '.join(l))
return x
return val
-def boolean(x):
- # type: (str) -> bool
+def boolean(x: str) -> bool:
if x.upper() not in ('Y', 'YES', 'N', 'NO'):
raise ValidationError(__("Please enter either 'y' or 'n'."))
return x.upper() in ('Y', 'YES')
-def suffix(x):
- # type: (str) -> str
+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'."))
return x
-def ok(x):
- # type: (str) -> str
+def ok(x: str) -> str:
return x
-def term_decode(text):
- # type: (Union[bytes,str]) -> str
+def term_decode(text: Union[bytes, str]) -> str:
warnings.warn('term_decode() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
@@ -175,8 +162,7 @@ def term_decode(text):
return text.decode('latin1')
-def do_prompt(text, default=None, validator=nonempty):
- # type: (str, str, Callable[[str], Any]) -> Union[str, bool]
+def do_prompt(text: str, default: str = None, validator: Callable[[str], Any] = nonempty) -> Union[str, bool]: # NOQA
while True:
if default is not None:
prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default)
@@ -201,8 +187,7 @@ def do_prompt(text, default=None, validator=nonempty):
return x
-def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")):
- # type: (str, Pattern) -> str
+def convert_python_source(source: str, rex: Pattern = re.compile(r"[uU]('.*?')")) -> str:
# remove Unicode literal prefixes
warnings.warn('convert_python_source() is deprecated.',
RemovedInSphinx40Warning)
@@ -210,13 +195,11 @@ def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")):
class QuickstartRenderer(SphinxRenderer):
- def __init__(self, templatedir):
- # type: (str) -> None
+ def __init__(self, templatedir: str) -> None:
self.templatedir = templatedir or ''
super().__init__()
- def render(self, template_name, context):
- # type: (str, Dict) -> str
+ def render(self, template_name: str, context: Dict) -> str:
user_template = path.join(self.templatedir, path.basename(template_name))
if self.templatedir and path.exists(user_template):
return self.render_from_file(user_template, context)
@@ -224,8 +207,7 @@ class QuickstartRenderer(SphinxRenderer):
return super().render(template_name, context)
-def ask_user(d):
- # type: (Dict) -> None
+def ask_user(d: Dict) -> None:
"""Ask the user for quickstart values missing from *d*.
Values are:
@@ -367,8 +349,8 @@ directly.'''))
print()
-def generate(d, overwrite=True, silent=False, templatedir=None):
- # type: (Dict, bool, bool, str) -> None
+def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir: str = None
+ ) -> None:
"""Generate project based on values in *d*."""
template = QuickstartRenderer(templatedir=templatedir)
@@ -401,8 +383,7 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
ensuredir(path.join(srcdir, d['dot'] + 'templates'))
ensuredir(path.join(srcdir, d['dot'] + 'static'))
- def write_file(fpath, content, newline=None):
- # type: (str, str, str) -> None
+ def write_file(fpath: str, content: str, newline: str = None) -> None:
if overwrite or not path.isfile(fpath):
if 'quiet' not in d:
print(__('Creating file %s.') % fpath)
@@ -460,8 +441,7 @@ where "builder" is one of the supported builders, e.g. html, latex or linkcheck.
'''))
-def valid_dir(d):
- # type: (Dict) -> bool
+def valid_dir(d: Dict) -> bool:
dir = d['path']
if not path.exists(dir):
return True
@@ -490,8 +470,7 @@ def valid_dir(d):
return True
-def get_parser():
- # type: () -> argparse.ArgumentParser
+def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] ',
epilog=__("For more information, visit ."),
@@ -572,8 +551,7 @@ Makefile to be used with sphinx-build.
return parser
-def main(argv=sys.argv[1:]):
- # type: (List[str]) -> int
+def main(argv: List[str] = sys.argv[1:]) -> int:
sphinx.locale.setlocale(locale.LC_ALL, '')
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 13f48e827..393df0ca9 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -73,6 +73,7 @@ class ObjectDescription(SphinxDirective):
def get_field_type_map(self) -> Dict[str, Tuple[Field, bool]]:
if self._doc_field_type_map == {}:
+ self._doc_field_type_map = {}
for field in self.doc_field_types:
for name in field.names:
self._doc_field_type_map[name] = (field, False)
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index 07e05568a..1fe98950b 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -10,21 +10,22 @@
"""
import copy
-from typing import NamedTuple
+from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Type, Union
+from docutils import nodes
+from docutils.nodes import Element, Node, system_message
+from docutils.parsers.rst.states import Inliner
+
+from sphinx.addnodes import pending_xref
from sphinx.errors import SphinxError
from sphinx.locale import _
+from sphinx.roles import XRefRole
+from sphinx.util.typing import RoleFunction
if False:
# For type annotation
- from typing import Any, Callable, Dict, Iterable, List, Tuple, Type, Union # NOQA
- from docutils import nodes # NOQA
- from docutils.parsers.rst.states import Inliner # NOQA
- from sphinx import addnodes # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.roles import XRefRole # NOQA
- from sphinx.util.typing import RoleFunction # NOQA
+ from sphinx.builders import Builder
+ from sphinx.environment import BuildEnvironment
class ObjType:
@@ -46,8 +47,7 @@ class ObjType:
'searchprio': 1,
}
- def __init__(self, lname, *roles, **attrs):
- # type: (str, Any, Any) -> None
+ def __init__(self, lname: str, *roles, **attrs) -> None:
self.lname = lname
self.roles = roles # type: Tuple
self.attrs = self.known_attrs.copy() # type: Dict
@@ -82,15 +82,14 @@ class Index:
localname = None # type: str
shortname = None # type: str
- def __init__(self, domain):
- # type: (Domain) -> None
+ def __init__(self, domain: "Domain") -> None:
if self.name is None or self.localname is None:
raise SphinxError('Index subclass %s has no valid name or localname'
% self.__class__.__name__)
self.domain = domain
- def generate(self, docnames=None):
- # type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]
+ def generate(self, docnames: Iterable[str] = None
+ ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
"""Get entries for the index.
If ``docnames`` is given, restrict to entries referring to these
@@ -181,7 +180,7 @@ class Domain:
#: role name -> a warning message if reference is missing
dangling_warnings = {} # type: Dict[str, str]
#: node_class -> (enum_node_type, title_getter)
- enumerable_nodes = {} # type: Dict[Type[nodes.Node], Tuple[str, Callable]]
+ enumerable_nodes = {} # type: Dict[Type[Node], Tuple[str, Callable]]
#: data value for a fresh environment
initial_data = {} # type: Dict
@@ -190,8 +189,7 @@ class Domain:
#: data version, bump this when the format of `self.data` changes
data_version = 0
- def __init__(self, env):
- # type: (BuildEnvironment) -> None
+ def __init__(self, env: "BuildEnvironment") -> None:
self.env = env # type: BuildEnvironment
self._role_cache = {} # type: Dict[str, Callable]
self._directive_cache = {} # type: Dict[str, Callable]
@@ -220,8 +218,7 @@ 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 add_object_type(self, name, objtype):
- # type: (str, ObjType) -> None
+ def add_object_type(self, name: str, objtype: ObjType) -> None:
"""Add an object type."""
self.object_types[name] = objtype
if objtype.roles:
@@ -232,8 +229,7 @@ class Domain:
for role in objtype.roles:
self._role2type.setdefault(role, []).append(name)
- def role(self, name):
- # type: (str) -> RoleFunction
+ def role(self, name: str) -> RoleFunction:
"""Return a role adapter function that always gives the registered
role its full name ('domain:name') as the first argument.
"""
@@ -243,15 +239,15 @@ class Domain:
return None
fullname = '%s:%s' % (self.name, name)
- def role_adapter(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
+ def role_adapter(typ: str, rawtext: str, text: str, lineno: int,
+ inliner: Inliner, options: Dict = {}, content: List[str] = []
+ ) -> Tuple[List[Node], List[system_message]]:
return self.roles[name](fullname, rawtext, text, lineno,
inliner, options, content)
self._role_cache[name] = role_adapter
return role_adapter
- def directive(self, name):
- # type: (str) -> Callable
+ def directive(self, name: str) -> Callable:
"""Return a directive adapter class that always gives the registered
directive its full name ('domain:name') as ``self.name``.
"""
@@ -263,8 +259,7 @@ class Domain:
BaseDirective = self.directives[name]
class DirectiveAdapter(BaseDirective): # type: ignore
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
self.name = fullname
return super().run()
self._directive_cache[name] = DirectiveAdapter
@@ -272,13 +267,11 @@ class Domain:
# methods that should be overwritten
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
"""Remove traces of a document in the domain-specific inventories."""
pass
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
"""Merge in data regarding *docnames* from a different domaindata
inventory (coming from a subprocess in parallel builds).
"""
@@ -286,26 +279,24 @@ class Domain:
'to be able to do parallel builds!' %
self.__class__)
- def process_doc(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
+ def process_doc(self, env: "BuildEnvironment", docname: str,
+ document: nodes.document) -> None:
"""Process a document after it is read by the environment."""
pass
- def check_consistency(self):
- # type: () -> None
+ def check_consistency(self) -> None:
"""Do consistency checks (**experimental**)."""
pass
- def process_field_xref(self, pnode):
- # type: (addnodes.pending_xref) -> None
+ def process_field_xref(self, pnode: pending_xref) -> None:
"""Process a pending xref created in a doc field.
For example, attach information about the current scope.
"""
pass
- def resolve_xref(self, env, fromdocname, builder,
- typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
"""Resolve the pending_xref *node* with the given *typ* and *target*.
This method should return a new node, to replace the xref node,
@@ -321,8 +312,9 @@ class Domain:
"""
pass
- def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
"""Resolve the pending_xref *node* with the given *target*.
The reference comes from an "any" or similar role, which means that we
@@ -338,8 +330,7 @@ class Domain:
"""
raise NotImplementedError
- def get_objects(self):
- # type: () -> Iterable[Tuple[str, str, str, str, str, int]]
+ def get_objects(self) -> Iterable[Tuple[str, str, str, str, str, int]]:
"""Return an iterable of "object descriptions".
Object descriptions are tuples with six items:
@@ -374,20 +365,17 @@ class Domain:
"""
return []
- def get_type_name(self, type, primary=False):
- # type: (ObjType, bool) -> str
+ def get_type_name(self, type: ObjType, primary: bool = False) -> str:
"""Return full name for given ObjType."""
if primary:
return type.lname
return _('%s %s') % (self.label, type.lname)
- def get_enumerable_node_type(self, node):
- # type: (nodes.Node) -> str
+ def get_enumerable_node_type(self, node: Node) -> str:
"""Get type of enumerable nodes (experimental)."""
enum_node_type, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return enum_node_type
- def get_full_qualified_name(self, node):
- # type: (nodes.Element) -> str
+ def get_full_qualified_name(self, node: Element) -> str:
"""Return full qualified name for given node."""
return None
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index ec311cfc7..e319771be 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -10,24 +10,27 @@
import re
import string
+from typing import Any, Dict, Iterator, List, Tuple
+from typing import cast
from docutils import nodes
+from docutils.nodes import Element
from sphinx import addnodes
+from sphinx.addnodes import pending_xref, desc_signature
+from sphinx.application import Sphinx
+from sphinx.builders import Builder
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
-from sphinx.locale import _
+from sphinx.environment import BuildEnvironment
+from sphinx.locale import _, __
from sphinx.roles import XRefRole
+from sphinx.util import logging
from sphinx.util.docfields import Field, TypedField
from sphinx.util.nodes import make_refnode
-if False:
- # For type annotation
- from typing import Any, Dict, Iterator, List, Tuple # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+logger = logging.getLogger(__name__)
# RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)')
@@ -79,23 +82,20 @@ class CObject(ObjectDescription):
'struct', '_Bool',
}
- def _parse_type(self, node, ctype):
- # type: (nodes.Element, str) -> None
+ def _parse_type(self, node: Element, ctype: str) -> None:
# add cross-ref nodes for all words
for part in [_f for _f in wsplit_re.split(ctype) if _f]:
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters + '_' and \
part not in self.stopwords:
- pnode = addnodes.pending_xref(
- '', refdomain='c', reftype='type', reftarget=part,
- modname=None, classname=None)
+ pnode = pending_xref('', refdomain='c', reftype='type', reftarget=part,
+ modname=None, classname=None)
pnode += tnode
node += pnode
else:
node += tnode
- def _parse_arglist(self, arglist):
- # type: (str) -> Iterator[str]
+ def _parse_arglist(self, arglist: str) -> Iterator[str]:
while True:
m = c_funcptr_arg_sig_re.match(arglist)
if m:
@@ -113,8 +113,7 @@ class CObject(ObjectDescription):
yield arglist
break
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
"""Transform a C signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig)
@@ -183,8 +182,7 @@ class CObject(ObjectDescription):
signode += addnodes.desc_addname(const, const)
return fullname
- def get_index_text(self, name):
- # type: (str) -> str
+ def get_index_text(self, name: str) -> str:
if self.objtype == 'function':
return _('%s (C function)') % name
elif self.objtype == 'member':
@@ -198,8 +196,7 @@ class CObject(ObjectDescription):
else:
return ''
- def add_target_and_index(self, name, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
# for C API items we add a prefix since names are usually not qualified
# by a module name and so easily clash with e.g. section titles
targetname = 'c.' + name
@@ -208,36 +205,30 @@ class CObject(ObjectDescription):
signode['ids'].append(targetname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
- inv = self.env.domaindata['c']['objects']
- if name in inv:
- self.state_machine.reporter.warning(
- 'duplicate C object description of %s, ' % name +
- 'other instance in ' + self.env.doc2path(inv[name][0]),
- line=self.lineno)
- inv[name] = (self.env.docname, self.objtype)
+
+ domain = cast(CDomain, self.env.get_domain('c'))
+ domain.note_object(name, self.objtype)
indextext = self.get_index_text(name)
if indextext:
self.indexnode['entries'].append(('single', indextext,
targetname, '', None))
- def before_content(self):
- # type: () -> None
+ def before_content(self) -> None:
self.typename_set = False
if self.name == 'c:type':
if self.names:
self.env.ref_context['c:type'] = self.names[0]
self.typename_set = True
- def after_content(self):
- # type: () -> None
+ def after_content(self) -> None:
if self.typename_set:
self.env.ref_context.pop('c:type', None)
class CXRefRole(XRefRole):
- def process_link(self, env, refnode, has_explicit_title, title, target):
- # type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
+ def process_link(self, env: BuildEnvironment, refnode: Element,
+ has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
if not has_explicit_title:
target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class
@@ -280,53 +271,61 @@ class CDomain(Domain):
'objects': {}, # fullname -> docname, objtype
} # type: Dict[str, Dict[str, Tuple[str, Any]]]
- def clear_doc(self, docname):
- # type: (str) -> None
- for fullname, (fn, _l) in list(self.data['objects'].items()):
- if fn == docname:
- del self.data['objects'][fullname]
+ @property
+ def objects(self) -> Dict[str, Tuple[str, str]]:
+ return self.data.setdefault('objects', {}) # fullname -> docname, objtype
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def note_object(self, name: str, objtype: str, location: Any = None) -> None:
+ if name in self.objects:
+ docname = self.objects[name][0]
+ logger.warning(__('duplicate C object description of %s, '
+ 'other instance in %s, use :noindex: for one of them'),
+ name, docname, location=location)
+ self.objects[name] = (self.env.docname, objtype)
+
+ def clear_doc(self, docname: str) -> None:
+ for fullname, (fn, _l) in list(self.objects.items()):
+ if fn == docname:
+ del self.objects[fullname]
+
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates
for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames:
self.data['objects'][fullname] = (fn, objtype)
- def resolve_xref(self, env, fromdocname, builder,
- typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
# strip pointer asterisk
target = target.rstrip(' *')
# becase TypedField can generate xrefs
if target in CObject.stopwords:
return contnode
- if target not in self.data['objects']:
+ if target not in self.objects:
return None
- obj = self.data['objects'][target]
+ obj = self.objects[target]
return make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target)
- def resolve_any_xref(self, env, fromdocname, builder, target,
- node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
# strip pointer asterisk
target = target.rstrip(' *')
- if target not in self.data['objects']:
+ if target not in self.objects:
return []
- obj = self.data['objects'][target]
+ obj = self.objects[target]
return [('c:' + self.role_for_objtype(obj[1]),
make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target))]
- def get_objects(self):
- # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
- for refname, (docname, type) in list(self.data['objects'].items()):
+ 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, 'c.' + refname, 1)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(CDomain)
return {
diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py
index aeea645c1..53cdec41c 100644
--- a/sphinx/domains/changeset.py
+++ b/sphinx/domains/changeset.py
@@ -9,9 +9,11 @@
"""
from collections import namedtuple
+from typing import Any, Dict, List
from typing import cast
from docutils import nodes
+from docutils.nodes import Node
from sphinx import addnodes
from sphinx import locale
@@ -23,9 +25,8 @@ from sphinx.util.docutils import SphinxDirective
if False:
# For type annotation
- from typing import Any, Dict, List # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.application import Sphinx
+ from sphinx.environment import BuildEnvironment
versionlabels = {
@@ -63,8 +64,7 @@ class VersionChange(SphinxDirective):
final_argument_whitespace = True
option_spec = {} # type: Dict
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
node = addnodes.versionmodified()
node.document = self.state.document
self.set_source_info(node)
@@ -102,7 +102,7 @@ class VersionChange(SphinxDirective):
domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
domain.note_changeset(node)
- ret = [node] # type: List[nodes.Node]
+ ret = [node] # type: List[Node]
ret += messages
return ret
@@ -117,42 +117,40 @@ class ChangeSetDomain(Domain):
'changes': {}, # version -> list of ChangeSet
} # type: Dict
- def clear_doc(self, docname):
- # type: (str) -> None
- for version, changes in self.data['changes'].items():
- for changeset in changes[:]:
- if changeset.docname == docname:
- changes.remove(changeset)
+ @property
+ def changesets(self) -> Dict[str, List[ChangeSet]]:
+ return self.data.setdefault('changes', {}) # version -> list of ChangeSet
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
- # XXX duplicates?
- for version, otherchanges in otherdata['changes'].items():
- changes = self.data['changes'].setdefault(version, [])
- for changeset in otherchanges:
- if changeset.docname in docnames:
- changes.append(changeset)
-
- def process_doc(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
- pass # nothing to do here. All changesets are registered on calling directive.
-
- def note_changeset(self, node):
- # type: (addnodes.versionmodified) -> None
+ def note_changeset(self, node: addnodes.versionmodified) -> None:
version = node['version']
module = self.env.ref_context.get('py:module')
objname = self.env.temp_data.get('object')
changeset = ChangeSet(node['type'], self.env.docname, node.line,
module, objname, node.astext())
- self.data['changes'].setdefault(version, []).append(changeset)
+ self.changesets.setdefault(version, []).append(changeset)
- def get_changesets_for(self, version):
- # type: (str) -> List[ChangeSet]
- return self.data['changes'].get(version, [])
+ def clear_doc(self, docname: str) -> None:
+ for version, changes in self.changesets.items():
+ for changeset in changes[:]:
+ if changeset.docname == docname:
+ changes.remove(changeset)
+
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
+ # XXX duplicates?
+ for version, otherchanges in otherdata['changes'].items():
+ changes = self.changesets.setdefault(version, [])
+ for changeset in otherchanges:
+ if changeset.docname in docnames:
+ changes.append(changeset)
+
+ def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
+ pass # nothing to do here. All changesets are registered on calling directive.
+
+ def get_changesets_for(self, version: str) -> List[ChangeSet]:
+ return self.changesets.get(version, [])
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_domain(ChangeSetDomain)
app.add_directive('deprecated', VersionChange)
app.add_directive('versionadded', VersionChange)
diff --git a/sphinx/domains/citation.py b/sphinx/domains/citation.py
index 2bb49def9..0b512c365 100644
--- a/sphinx/domains/citation.py
+++ b/sphinx/domains/citation.py
@@ -8,11 +8,13 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, List, Set, Tuple
from typing import cast
from docutils import nodes
+from docutils.nodes import Element
-from sphinx import addnodes
+from sphinx.addnodes import pending_xref
from sphinx.domains import Domain
from sphinx.locale import __
from sphinx.transforms import SphinxTransform
@@ -21,10 +23,10 @@ from sphinx.util.nodes import copy_source_info, make_refnode
if False:
# For type annotation
- from typing import Any, Dict, List, Set, Tuple, Union # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.application import Sphinx
+ from sphinx.builders import Builder
+ from sphinx.environment import BuildEnvironment
+
logger = logging.getLogger(__name__)
@@ -40,17 +42,14 @@ class CitationDomain(Domain):
}
@property
- def citations(self):
- # type: () -> Dict[str, Tuple[str, str, int]]
+ def citations(self) -> Dict[str, Tuple[str, str, int]]:
return self.data.setdefault('citations', {})
@property
- def citation_refs(self):
- # type: () -> Dict[str, Set[str]]
+ def citation_refs(self) -> Dict[str, Set[str]]:
return self.data.setdefault('citation_refs', {})
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
for key, (fn, _l, lineno) in list(self.citations.items()):
if fn == docname:
del self.citations[key]
@@ -60,8 +59,7 @@ class CitationDomain(Domain):
elif docname in docnames:
docnames.remove(docname)
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX duplicates?
for key, data in otherdata['citations'].items():
if data[0] in docnames:
@@ -72,8 +70,7 @@ class CitationDomain(Domain):
if docname in docnames:
citation_refs.add(docname)
- def note_citation(self, node):
- # type: (nodes.citation) -> None
+ def note_citation(self, node: nodes.citation) -> None:
label = node[0].astext()
if label in self.citations:
path = self.env.doc2path(self.citations[label][0])
@@ -81,20 +78,19 @@ class CitationDomain(Domain):
location=node, type='ref', subtype='citation')
self.citations[label] = (node['docname'], node['ids'][0], node.line)
- def note_citation_reference(self, node):
- # type: (addnodes.pending_xref) -> None
+ def note_citation_reference(self, node: pending_xref) -> None:
docnames = self.citation_refs.setdefault(node['reftarget'], set())
docnames.add(self.env.docname)
- def check_consistency(self):
- # type: () -> None
+ def check_consistency(self) -> None:
for name, (docname, labelid, lineno) in self.citations.items():
if name not in self.citation_refs:
logger.warning(__('Citation [%s] is not referenced.'), name,
type='ref', subtype='citation', location=(docname, lineno))
- def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
docname, labelid, lineno = self.citations.get(target, ('', '', 0))
if not docname:
return None
@@ -102,8 +98,9 @@ class CitationDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'ref', target, node, contnode)
if refnode is None:
return []
@@ -115,8 +112,7 @@ class CitationDefinitionTransform(SphinxTransform):
"""Mark citation definition labels as not smartquoted."""
default_priority = 619
- def apply(self, **kwargs):
- # type: (Any) -> None
+ def apply(self, **kwargs) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
for node in self.document.traverse(nodes.citation):
# register citation node to domain
@@ -135,16 +131,15 @@ class CitationReferenceTransform(SphinxTransform):
"""
default_priority = 619
- def apply(self, **kwargs):
- # type: (Any) -> None
+ def apply(self, **kwargs) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
for node in self.document.traverse(nodes.citation_reference):
target = node.astext()
- ref = addnodes.pending_xref(target, refdomain='citation', reftype='ref',
- reftarget=target, refwarn=True,
- support_smartquotes=False,
- ids=node["ids"],
- classes=node.get('classes', []))
+ ref = pending_xref(target, refdomain='citation', reftype='ref',
+ reftarget=target, refwarn=True,
+ support_smartquotes=False,
+ ids=node["ids"],
+ classes=node.get('classes', []))
ref += nodes.inline(target, '[%s]' % target)
copy_source_info(node, ref)
node.replace_self(ref)
@@ -153,8 +148,7 @@ class CitationReferenceTransform(SphinxTransform):
domain.note_citation_reference(ref)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_domain(CitationDomain)
app.add_transform(CitationDefinitionTransform)
app.add_transform(CitationReferenceTransform)
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index 5d8b4f1e0..121d5582d 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -8,25 +8,30 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, Iterator, List, Tuple
+from typing import cast
+
from docutils import nodes
+from docutils.nodes import Element, Node
from docutils.parsers.rst import directives
from sphinx import addnodes
+from sphinx.addnodes import desc_signature, pending_xref
+from sphinx.application import Sphinx
+from sphinx.builders import Builder
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.domains.python import _pseudo_parse_arglist
-from sphinx.locale import _
+from sphinx.environment import BuildEnvironment
+from sphinx.locale import _, __
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
-if False:
- # For type annotation
- from typing import Any, Dict, Iterator, List, Tuple # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+
+logger = logging.getLogger(__name__)
class JSObject(ObjectDescription):
@@ -44,8 +49,7 @@ class JSObject(ObjectDescription):
#: based on directive nesting
allow_nesting = False
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> Tuple[str, str]
+ def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
"""Breaks down construct signatures
Parses out prefix and argument list from construct definition. The
@@ -98,8 +102,8 @@ class JSObject(ObjectDescription):
_pseudo_parse_arglist(signode, arglist)
return fullname, prefix
- def add_target_and_index(self, name_obj, sig, signode):
- # type: (Tuple[str, str], str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name_obj: Tuple[str, str], sig: str,
+ signode: desc_signature) -> None:
mod_name = self.env.ref_context.get('js:module')
fullname = (mod_name and mod_name + '.' or '') + name_obj[0]
if fullname not in self.state.document.ids:
@@ -107,14 +111,10 @@ class JSObject(ObjectDescription):
signode['ids'].append(fullname.replace('$', '_S_'))
signode['first'] = not self.names
self.state.document.note_explicit_target(signode)
- objects = self.env.domaindata['js']['objects']
- if fullname in objects:
- self.state_machine.reporter.warning(
- 'duplicate object description of %s, ' % fullname +
- 'other instance in ' +
- self.env.doc2path(objects[fullname][0]),
- line=self.lineno)
- objects[fullname] = self.env.docname, self.objtype
+
+ domain = cast(JavaScriptDomain, self.env.get_domain('js'))
+ domain.note_object(fullname, self.objtype,
+ location=(self.env.docname, self.lineno))
indextext = self.get_index_text(mod_name, name_obj)
if indextext:
@@ -122,8 +122,7 @@ class JSObject(ObjectDescription):
fullname.replace('$', '_S_'),
'', None))
- def get_index_text(self, objectname, name_obj):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, objectname: str, name_obj: Tuple[str, str]) -> str:
name, obj = name_obj
if self.objtype == 'function':
if not obj:
@@ -137,8 +136,7 @@ class JSObject(ObjectDescription):
return _('%s (%s attribute)') % (name, obj)
return ''
- def before_content(self):
- # type: () -> None
+ def before_content(self) -> None:
"""Handle object nesting before content
:py:class:`JSObject` represents JavaScript language constructs. For
@@ -174,8 +172,7 @@ class JSObject(ObjectDescription):
objects = self.env.ref_context.setdefault('js:objects', [])
objects.append(prefix)
- def after_content(self):
- # type: () -> None
+ def after_content(self) -> None:
"""Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix
@@ -246,17 +243,19 @@ class JSModule(SphinxDirective):
'noindex': directives.flag
}
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
mod_name = self.arguments[0].strip()
self.env.ref_context['js:module'] = mod_name
noindex = 'noindex' in self.options
- ret = [] # type: List[nodes.Node]
+ ret = [] # type: List[Node]
if not noindex:
- self.env.domaindata['js']['modules'][mod_name] = self.env.docname
+ domain = cast(JavaScriptDomain, self.env.get_domain('js'))
+
+ domain.note_module(mod_name)
# Make a duplicate entry in 'objects' to facilitate searching for
# the module in JavaScriptDomain.find_obj()
- self.env.domaindata['js']['objects'][mod_name] = (self.env.docname, 'module')
+ domain.note_object(mod_name, 'module', location=(self.env.docname, self.lineno))
+
targetnode = nodes.target('', '', ids=['module-' + mod_name],
ismod=True)
self.state.document.note_explicit_target(targetnode)
@@ -269,8 +268,8 @@ class JSModule(SphinxDirective):
class JSXRefRole(XRefRole):
- def process_link(self, env, refnode, has_explicit_title, title, target):
- # type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
+ def process_link(self, env: BuildEnvironment, refnode: Element,
+ has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
# basically what sphinx.domains.python.PyXRefRole does
refnode['js:object'] = env.ref_context.get('js:object')
refnode['js:module'] = env.ref_context.get('js:module')
@@ -319,33 +318,48 @@ class JavaScriptDomain(Domain):
}
initial_data = {
'objects': {}, # fullname -> docname, objtype
- 'modules': {}, # mod_name -> docname
+ 'modules': {}, # modname -> docname
} # type: Dict[str, Dict[str, Tuple[str, str]]]
- def clear_doc(self, docname):
- # type: (str) -> None
- for fullname, (pkg_docname, _l) in list(self.data['objects'].items()):
- if pkg_docname == docname:
- del self.data['objects'][fullname]
- for mod_name, pkg_docname in list(self.data['modules'].items()):
- if pkg_docname == docname:
- del self.data['modules'][mod_name]
+ @property
+ def objects(self) -> Dict[str, Tuple[str, str]]:
+ return self.data.setdefault('objects', {}) # fullname -> docname, objtype
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def note_object(self, fullname: str, objtype: 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)
+
+ @property
+ def modules(self) -> Dict[str, str]:
+ return self.data.setdefault('modules', {}) # modname -> docname
+
+ def note_module(self, modname: str) -> None:
+ self.modules[modname] = self.env.docname
+
+ def clear_doc(self, docname: str) -> None:
+ for fullname, (pkg_docname, _l) in list(self.objects.items()):
+ if pkg_docname == docname:
+ del self.objects[fullname]
+ for modname, pkg_docname 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():
if fn in docnames:
- self.data['objects'][fullname] = (fn, objtype)
+ self.objects[fullname] = (fn, objtype)
for mod_name, pkg_docname in otherdata['modules'].items():
if pkg_docname in docnames:
- self.data['modules'][mod_name] = pkg_docname
+ self.modules[mod_name] = pkg_docname
- def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0):
- # type: (BuildEnvironment, str, str, str, str, int) -> Tuple[str, Tuple[str, str]]
+ def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str,
+ typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str]]:
if name[-2:] == '()':
name = name[:-2]
- objects = self.data['objects']
searches = []
if mod_name and prefix:
@@ -361,14 +375,14 @@ class JavaScriptDomain(Domain):
newname = None
for search_name in searches:
- if search_name in objects:
+ if search_name in self.objects:
newname = search_name
- return newname, objects.get(newname)
+ return newname, self.objects.get(newname)
- def resolve_xref(self, env, fromdocname, builder, typ, target, node,
- contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
mod_name = node.get('js:module')
prefix = node.get('js:object')
searchorder = node.hasattr('refspecific') and 1 or 0
@@ -378,9 +392,9 @@ class JavaScriptDomain(Domain):
return make_refnode(builder, fromdocname, obj[0],
name.replace('$', '_S_'), contnode, name)
- def resolve_any_xref(self, env, fromdocname, builder, target, node,
- contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
mod_name = node.get('js:module')
prefix = node.get('js:object')
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
@@ -390,14 +404,11 @@ class JavaScriptDomain(Domain):
make_refnode(builder, fromdocname, obj[0],
name.replace('$', '_S_'), contnode, name))]
- def get_objects(self):
- # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
- for refname, (docname, type) in list(self.data['objects'].items()):
- yield refname, refname, type, docname, \
- refname.replace('$', '_S_'), 1
+ 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
- def get_full_qualified_name(self, node):
- # type: (nodes.Element) -> str
+ def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('js:module')
prefix = node.get('js:object')
target = node.get('reftarget')
@@ -407,8 +418,7 @@ class JavaScriptDomain(Domain):
return '.'.join(filter(None, [modname, prefix, target]))
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(JavaScriptDomain)
return {
diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py
index 45e33ea6f..d3cacc5ba 100644
--- a/sphinx/domains/math.py
+++ b/sphinx/domains/math.py
@@ -9,13 +9,17 @@
"""
import warnings
+from typing import Any, Dict, Iterable, List, Tuple
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
+from sphinx.environment import BuildEnvironment
from sphinx.locale import __
from sphinx.roles import XRefRole
from sphinx.util import logging
@@ -23,18 +27,16 @@ from sphinx.util.nodes import make_refnode
if False:
# For type annotation
- from typing import Any, Dict, Iterable, List, Tuple # NOQA
- from sphinx import addnodes # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.application import Sphinx
+ from sphinx.builders import Builder
+
logger = logging.getLogger(__name__)
class MathReferenceRole(XRefRole):
- def result_nodes(self, document, env, node, is_ref):
- # type: (nodes.document, BuildEnvironment, nodes.Element, bool) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
+ def result_nodes(self, document: nodes.document, env: BuildEnvironment, node: Element,
+ is_ref: bool) -> Tuple[List[Node], List[system_message]]:
node['refdomain'] = 'math'
return [node], []
@@ -60,12 +62,10 @@ class MathDomain(Domain):
}
@property
- def equations(self):
- # type: () -> Dict[str, Tuple[str, int]]
+ def equations(self) -> Dict[str, Tuple[str, int]]:
return self.data.setdefault('objects', {}) # labelid -> (docname, eqno)
- def note_equation(self, docname, labelid, location=None):
- # type: (str, str, Any) -> None
+ def note_equation(self, docname: str, labelid: str, location: Any = None) -> None:
if labelid in self.equations:
other = self.equations[labelid][0]
logger.warning(__('duplicate label of equation %s, other instance in %s') %
@@ -73,31 +73,27 @@ class MathDomain(Domain):
self.equations[labelid] = (docname, self.env.new_serialno('eqno') + 1)
- def get_equation_number_for(self, labelid):
- # type: (str) -> int
+ def get_equation_number_for(self, labelid: str) -> int:
if labelid in self.equations:
return self.equations[labelid][1]
else:
return None
- def process_doc(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
- def math_node(node):
- # type: (nodes.Node) -> bool
+ def process_doc(self, env: BuildEnvironment, docname: str,
+ document: nodes.document) -> None:
+ def math_node(node: Node) -> bool:
return isinstance(node, (nodes.math, nodes.math_block))
self.data['has_equations'][docname] = any(document.traverse(math_node))
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
for equation_id, (doc, eqno) in list(self.equations.items()):
if doc == docname:
del self.equations[equation_id]
self.data['has_equations'].pop(docname, None)
- def merge_domaindata(self, docnames, otherdata):
- # type: (Iterable[str], Dict) -> None
+ def merge_domaindata(self, docnames: Iterable[str], otherdata: Dict) -> None:
for labelid, (doc, eqno) in otherdata['objects'].items():
if doc in docnames:
self.equations[labelid] = (doc, eqno)
@@ -105,8 +101,9 @@ class MathDomain(Domain):
for docname in docnames:
self.data['has_equations'][docname] = otherdata['has_equations'][docname]
- def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: "Builder",
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
assert typ in ('eq', 'numref')
docname, number = self.equations.get(target, (None, None))
if docname:
@@ -133,20 +130,19 @@ class MathDomain(Domain):
else:
return None
- def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: "Builder",
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode)
if refnode is None:
return []
else:
return [('eq', refnode)]
- def get_objects(self):
- # type: () -> List
+ def get_objects(self) -> List:
return []
- def add_equation(self, env, docname, labelid):
- # type: (BuildEnvironment, str, str) -> int
+ def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int:
warnings.warn('MathDomain.add_equation() is deprecated.',
RemovedInSphinx40Warning)
if labelid in self.equations:
@@ -158,20 +154,17 @@ class MathDomain(Domain):
self.equations[labelid] = (docname, eqno)
return eqno
- def get_next_equation_number(self, docname):
- # type: (str) -> int
+ def get_next_equation_number(self, docname: str) -> int:
warnings.warn('MathDomain.get_next_equation_number() is deprecated.',
RemovedInSphinx40Warning)
targets = [eq for eq in self.equations.values() if eq[0] == docname]
return len(targets) + 1
- def has_equations(self):
- # type: () -> bool
+ def has_equations(self) -> bool:
return any(self.data['has_equations'].values())
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_domain(MathDomain)
app.add_role('eq', MathReferenceRole(warn_dangling=True))
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 2a9a120ef..b70481198 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -10,31 +10,31 @@
import re
import warnings
+from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type
from typing import cast
from docutils import nodes
+from docutils.nodes import Element, Node
from docutils.parsers.rst import directives
from sphinx import addnodes, locale
+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.directives import ObjectDescription
from sphinx.domains import Domain, ObjType, Index, IndexEntry
+from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
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.typing import TextlikeNode
-if False:
- # For type annotation
- from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.util.typing import TextlikeNode # NOQA
logger = logging.getLogger(__name__)
@@ -67,8 +67,7 @@ locale.pairindextypes = DeprecatedDict(
)
-def _pseudo_parse_arglist(signode, arglist):
- # type: (addnodes.desc_signature, str) -> None
+def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
""""Parse" a list of arguments separated by commas.
Arguments can have "optional" annotations given by enclosing them in
@@ -76,7 +75,7 @@ def _pseudo_parse_arglist(signode, arglist):
string literal (e.g. default argument value).
"""
paramlist = addnodes.desc_parameterlist()
- stack = [paramlist] # type: List[nodes.Element]
+ stack = [paramlist] # type: List[Element]
try:
for argument in arglist.split(','):
argument = argument.strip()
@@ -119,15 +118,9 @@ def _pseudo_parse_arglist(signode, arglist):
# This override allows our inline type specifiers to behave like :class: link
# when it comes to handling "." and "~" prefixes.
class PyXrefMixin:
- def make_xref(self,
- rolename, # type: str
- domain, # type: str
- target, # type: str
- innernode=nodes.emphasis, # type: Type[TextlikeNode]
- contnode=None, # type: nodes.Node
- env=None, # type: BuildEnvironment
- ):
- # type: (...) -> nodes.Node
+ def make_xref(self, rolename: str, domain: str, target: str,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
+ contnode: Node = None, env: BuildEnvironment = None) -> Node:
result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode, env)
result['refspecific'] = True
@@ -142,15 +135,9 @@ class PyXrefMixin:
break
return result
- def make_xrefs(self,
- rolename, # type: str
- domain, # type: str
- target, # type: str
- innernode=nodes.emphasis, # type: Type[TextlikeNode]
- contnode=None, # type: nodes.Node
- env=None, # type: BuildEnvironment
- ):
- # type: (...) -> List[nodes.Node]
+ def make_xrefs(self, rolename: str, domain: str, target: str,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
+ contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
delims_re = re.compile(delims)
sub_targets = re.split(delims, target)
@@ -172,9 +159,9 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field):
- def make_xref(self, rolename, domain, target,
- innernode=nodes.emphasis, contnode=None, env=None):
- # type: (str, str, str, Type[TextlikeNode], nodes.Node, BuildEnvironment) -> nodes.Node # NOQA
+ def make_xref(self, rolename: str, domain: str, target: str,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
+ contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
rolename = 'obj'
@@ -187,9 +174,9 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField):
- def make_xref(self, rolename, domain, target,
- innernode=nodes.emphasis, contnode=None, env=None):
- # type: (str, str, str, Type[TextlikeNode], nodes.Node, BuildEnvironment) -> nodes.Node # NOQA
+ def make_xref(self, rolename: str, domain: str, target: str,
+ innernode: Type[TextlikeNode] = nodes.emphasis,
+ contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
rolename = 'obj'
@@ -231,22 +218,19 @@ class PyObject(ObjectDescription):
allow_nesting = False
- def get_signature_prefix(self, sig):
- # type: (str) -> str
+ def get_signature_prefix(self, sig: str) -> str:
"""May return a prefix to put before the object name in the
signature.
"""
return ''
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
"""May return true if an empty argument list is to be generated even if
the document contains none.
"""
return False
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> Tuple[str, str]
+ def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
"""Transform a Python signature into RST nodes.
Return (fully qualified name of the thing, classname if any).
@@ -320,13 +304,12 @@ class PyObject(ObjectDescription):
return fullname, prefix
- def get_index_text(self, modname, name):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name: Tuple[str, str]) -> str:
"""Return the text for the index entry of the object."""
raise NotImplementedError('must be implemented in subclasses')
- def add_target_and_index(self, name_cls, sig, signode):
- # type: (Tuple[str, str], str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name_cls: Tuple[str, str], sig: str,
+ signode: desc_signature) -> None:
modname = self.options.get('module', self.env.ref_context.get('py:module'))
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
@@ -345,8 +328,7 @@ class PyObject(ObjectDescription):
self.indexnode['entries'].append(('single', indextext,
fullname, '', None))
- def before_content(self):
- # type: () -> None
+ def before_content(self) -> None:
"""Handle object nesting before content
:py:class:`PyObject` represents Python language constructs. For
@@ -379,8 +361,7 @@ class PyObject(ObjectDescription):
modules.append(self.env.ref_context.get('py:module'))
self.env.ref_context['py:module'] = self.options['module']
- def after_content(self):
- # type: () -> None
+ def after_content(self) -> None:
"""Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix
@@ -411,19 +392,16 @@ class PyModulelevel(PyObject):
Description of an object on module level (functions, data).
"""
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
warnings.warn('PyClassmember is deprecated.',
RemovedInSphinx40Warning)
return super().run()
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
return self.objtype == 'function'
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
if self.objtype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
@@ -444,19 +422,16 @@ class PyFunction(PyObject):
'async': directives.flag,
})
- def get_signature_prefix(self, sig):
- # type: (str) -> str
+ def get_signature_prefix(self, sig: str) -> str:
if 'async' in self.options:
return 'async '
else:
return ''
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
return True
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
if modname:
return _('%s() (in module %s)') % (name, modname)
@@ -467,8 +442,7 @@ class PyFunction(PyObject):
class PyVariable(PyObject):
"""Description of a variable."""
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
if modname:
return _('%s (in module %s)') % (name, modname)
@@ -483,12 +457,10 @@ class PyClasslike(PyObject):
allow_nesting = True
- def get_signature_prefix(self, sig):
- # type: (str) -> str
+ def get_signature_prefix(self, sig: str) -> str:
return self.objtype + ' '
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
if self.objtype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
@@ -504,27 +476,23 @@ class PyClassmember(PyObject):
Description of a class member (methods, attributes).
"""
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
warnings.warn('PyClassmember is deprecated.',
RemovedInSphinx40Warning)
return super().run()
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
return self.objtype.endswith('method')
- def get_signature_prefix(self, sig):
- # type: (str) -> str
+ def get_signature_prefix(self, sig: str) -> str:
if self.objtype == 'staticmethod':
return 'static '
elif self.objtype == 'classmethod':
return 'classmethod '
return ''
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.objtype == 'method':
@@ -593,15 +561,13 @@ class PyMethod(PyObject):
'staticmethod': directives.flag,
})
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
if 'property' in self.options:
return False
else:
return True
- def get_signature_prefix(self, sig):
- # type: (str) -> str
+ def get_signature_prefix(self, sig: str) -> str:
prefix = []
if 'abstractmethod' in self.options:
prefix.append('abstract')
@@ -619,8 +585,7 @@ class PyMethod(PyObject):
else:
return ''
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
try:
clsname, methname = name.rsplit('.', 1)
@@ -647,8 +612,7 @@ class PyClassMethod(PyMethod):
option_spec = PyObject.option_spec.copy()
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
self.name = 'py:method'
self.options['classmethod'] = True
@@ -660,8 +624,7 @@ class PyStaticMethod(PyMethod):
option_spec = PyObject.option_spec.copy()
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
self.name = 'py:method'
self.options['staticmethod'] = True
@@ -671,8 +634,7 @@ class PyStaticMethod(PyMethod):
class PyAttribute(PyObject):
"""Description of an attribute."""
- def get_index_text(self, modname, name_cls):
- # type: (str, Tuple[str, str]) -> str
+ def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
try:
clsname, attrname = name.rsplit('.', 1)
@@ -691,14 +653,12 @@ class PyDecoratorMixin:
"""
Mixin for decorator directives.
"""
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> Tuple[str, str]
+ def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
ret = super().handle_signature(sig, signode) # type: ignore
signode.insert(0, addnodes.desc_addname('@', '@'))
return ret
- def needs_arglist(self):
- # type: () -> bool
+ def needs_arglist(self) -> bool:
return False
@@ -706,8 +666,7 @@ class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
"""
Directive to mark functions meant to be used as decorators.
"""
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
# a decorator function is a function after all
self.name = 'py:function'
return super().run()
@@ -717,8 +676,7 @@ class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
"""
Directive to mark methods meant to be used as decorators.
"""
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
self.name = 'py:method'
return super().run()
@@ -739,14 +697,13 @@ class PyModule(SphinxDirective):
'deprecated': directives.flag,
}
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
domain = cast(PythonDomain, self.env.get_domain('py'))
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
self.env.ref_context['py:module'] = modname
- ret = [] # type: List[nodes.Node]
+ ret = [] # type: List[Node]
if not noindex:
# note module to the domain
domain.note_module(modname,
@@ -780,8 +737,7 @@ class PyCurrentModule(SphinxDirective):
final_argument_whitespace = False
option_spec = {} # type: Dict
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
modname = self.arguments[0].strip()
if modname == 'None':
self.env.ref_context.pop('py:module', None)
@@ -791,8 +747,8 @@ class PyCurrentModule(SphinxDirective):
class PyXRefRole(XRefRole):
- def process_link(self, env, refnode, has_explicit_title, title, target):
- # type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
+ def process_link(self, env: BuildEnvironment, refnode: Element,
+ has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
refnode['py:module'] = env.ref_context.get('py:module')
refnode['py:class'] = env.ref_context.get('py:class')
if not has_explicit_title:
@@ -822,8 +778,8 @@ class PythonModuleIndex(Index):
localname = _('Python Module Index')
shortname = _('modules')
- def generate(self, docnames=None):
- # type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]
+ def generate(self, docnames: Iterable[str] = None
+ ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
content = {} # type: Dict[str, List[IndexEntry]]
# list of prefixes to ignore
ignores = None # type: List[str]
@@ -937,12 +893,10 @@ class PythonDomain(Domain):
]
@property
- def objects(self):
- # type: () -> Dict[str, Tuple[str, str]]
+ def objects(self) -> Dict[str, Tuple[str, str]]:
return self.data.setdefault('objects', {}) # fullname -> docname, objtype
- def note_object(self, name, objtype, location=None):
- # type: (str, str, Any) -> None
+ def note_object(self, name: str, objtype: str, location: Any = None) -> None:
"""Note a python object for cross reference.
.. versionadded:: 2.1
@@ -955,20 +909,17 @@ class PythonDomain(Domain):
self.objects[name] = (self.env.docname, objtype)
@property
- def modules(self):
- # type: () -> Dict[str, Tuple[str, str, str, bool]]
+ def modules(self) -> Dict[str, Tuple[str, str, str, bool]]:
return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA
- def note_module(self, name, synopsis, platform, deprecated):
- # type: (str, str, str, bool) -> None
+ def note_module(self, name: str, synopsis: str, platform: str, deprecated: bool) -> None:
"""Note a python module for cross reference.
.. versionadded:: 2.1
"""
self.modules[name] = (self.env.docname, synopsis, platform, deprecated)
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
for fullname, (fn, _l) in list(self.objects.items()):
if fn == docname:
del self.objects[fullname]
@@ -976,8 +927,7 @@ class PythonDomain(Domain):
if fn == docname:
del self.modules[modname]
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates?
for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames:
@@ -986,8 +936,8 @@ class PythonDomain(Domain):
if data[0] in docnames:
self.modules[modname] = data
- def find_obj(self, env, modname, classname, name, type, searchmode=0):
- # type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]]
+ def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
+ name: str, type: str, searchmode: int = 0) -> List[Tuple[str, Any]]:
"""Find a Python object for "name", perhaps using the given module
and/or classname. Returns a list of (name, object entry) tuples.
"""
@@ -1049,9 +999,9 @@ class PythonDomain(Domain):
matches.append((newname, self.objects[newname]))
return matches
- def resolve_xref(self, env, fromdocname, builder,
- type, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ type: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
modname = node.get('py:module')
clsname = node.get('py:class')
searchmode = node.hasattr('refspecific') and 1 or 0
@@ -1070,12 +1020,12 @@ class PythonDomain(Domain):
else:
return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
- def resolve_any_xref(self, env, fromdocname, builder, target,
- node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
modname = node.get('py:module')
clsname = node.get('py:class')
- results = [] # type: List[Tuple[str, nodes.Element]]
+ results = [] # type: List[Tuple[str, Element]]
# always search in "refspecific" mode with the :any: role
matches = self.find_obj(env, modname, clsname, target, None, 1)
@@ -1090,8 +1040,8 @@ class PythonDomain(Domain):
contnode, name)))
return results
- def _make_module_refnode(self, builder, fromdocname, name, contnode):
- # type: (Builder, str, str, nodes.Node) -> nodes.Element
+ def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
+ contnode: Node) -> Element:
# get additional info for modules
docname, synopsis, platform, deprecated = self.modules[name]
title = name
@@ -1104,16 +1054,14 @@ class PythonDomain(Domain):
return make_refnode(builder, fromdocname, docname,
'module-' + name, contnode, title)
- def get_objects(self):
- # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
+ def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for modname, info in self.modules.items():
yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
for refname, (docname, type) in self.objects.items():
if type != 'module': # modules are already handled
yield (refname, refname, type, docname, refname, 1)
- def get_full_qualified_name(self, node):
- # type: (nodes.Element) -> str
+ def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('py:module')
clsname = node.get('py:class')
target = node.get('reftarget')
@@ -1123,8 +1071,7 @@ class PythonDomain(Domain):
return '.'.join(filter(None, [modname, clsname, target]))
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(PythonDomain)
return {
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 349043294..85f10e15c 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -9,26 +9,24 @@
"""
import re
+from typing import Any, Dict, Iterator, List, Tuple
from typing import cast
+from docutils.nodes import Element
from docutils.parsers.rst import directives
from sphinx import addnodes
+from sphinx.addnodes import desc_signature, pending_xref
+from sphinx.application import Sphinx
+from sphinx.builders import Builder
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
+from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.nodes import make_refnode
-if False:
- # For type annotation
- from typing import Any, Dict, Iterator, List, Tuple # NOQA
- from docutils import nodes # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
-
logger = logging.getLogger(__name__)
@@ -40,8 +38,7 @@ class ReSTMarkup(ObjectDescription):
Description of generic reST markup.
"""
- def add_target_and_index(self, name, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
targetname = self.objtype + '-' + name
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
@@ -57,13 +54,11 @@ class ReSTMarkup(ObjectDescription):
self.indexnode['entries'].append(('single', indextext,
targetname, '', None))
- def get_index_text(self, objectname, name):
- # type: (str, str) -> str
+ def get_index_text(self, objectname: str, name: str) -> str:
return ''
-def parse_directive(d):
- # type: (str) -> Tuple[str, str]
+def parse_directive(d: str) -> Tuple[str, str]:
"""Parse a directive signature.
Returns (directive, arguments) string tuple. If no arguments are given,
@@ -87,8 +82,7 @@ class ReSTDirective(ReSTMarkup):
"""
Description of a reST directive.
"""
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
name, args = parse_directive(sig)
desc_name = '.. %s::' % name
signode += addnodes.desc_name(desc_name, desc_name)
@@ -96,18 +90,15 @@ class ReSTDirective(ReSTMarkup):
signode += addnodes.desc_addname(args, args)
return name
- def get_index_text(self, objectname, name):
- # type: (str, str) -> str
+ def get_index_text(self, objectname: str, name: str) -> str:
return _('%s (directive)') % name
- def before_content(self):
- # type: () -> None
+ def before_content(self) -> None:
if self.names:
directives = self.env.ref_context.setdefault('rst:directives', [])
directives.append(self.names[0])
- def after_content(self):
- # type: () -> None
+ def after_content(self) -> None:
directives = self.env.ref_context.setdefault('rst:directives', [])
if directives:
directives.pop()
@@ -122,8 +113,7 @@ class ReSTDirectiveOption(ReSTMarkup):
'type': directives.unchanged,
})
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
try:
name, argument = re.split(r'\s*:\s+', sig.strip(), 1)
except ValueError:
@@ -137,8 +127,7 @@ class ReSTDirectiveOption(ReSTMarkup):
signode += addnodes.desc_annotation(text, text)
return name
- def add_target_and_index(self, name, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
directive_name = self.current_directive
targetname = '-'.join([self.objtype, self.current_directive, name])
if targetname not in self.state.document.ids:
@@ -162,8 +151,7 @@ class ReSTDirectiveOption(ReSTMarkup):
self.indexnode['entries'].append(('single', text, targetname, '', key))
@property
- def current_directive(self):
- # type: () -> str
+ def current_directive(self) -> str:
directives = self.env.ref_context.get('rst:directives')
if directives:
return directives[-1]
@@ -175,13 +163,11 @@ class ReSTRole(ReSTMarkup):
"""
Description of a reST role.
"""
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig)
return sig
- def get_index_text(self, objectname, name):
- # type: (str, str) -> str
+ def get_index_text(self, objectname: str, name: str) -> str:
return _('%s (role)') % name
@@ -209,12 +195,10 @@ class ReSTDomain(Domain):
} # type: Dict[str, Dict[Tuple[str, str], str]]
@property
- def objects(self):
- # type: () -> Dict[Tuple[str, str], str]
+ def objects(self) -> Dict[Tuple[str, str], str]:
return self.data.setdefault('objects', {}) # (objtype, fullname) -> docname
- def note_object(self, objtype, name, location=None):
- # type: (str, str, Any) -> None
+ def note_object(self, objtype: str, name: str, location: Any = None) -> None:
if (objtype, name) in self.objects:
docname = self.objects[objtype, name]
logger.warning(__('duplicate description of %s %s, other instance in %s') %
@@ -222,21 +206,20 @@ class ReSTDomain(Domain):
self.objects[objtype, name] = self.env.docname
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
for (typ, name), doc in list(self.objects.items()):
if doc == docname:
del self.objects[typ, name]
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates
for (typ, name), doc in otherdata['objects'].items():
if doc in docnames:
self.objects[typ, name] = doc
- def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ typ: str, target: str, node: pending_xref, contnode: Element
+ ) -> Element:
objtypes = self.objtypes_for_role(typ)
for objtype in objtypes:
todocname = self.objects.get((objtype, target))
@@ -246,9 +229,10 @@ class ReSTDomain(Domain):
contnode, target + ' ' + objtype)
return None
- def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
- results = [] # type: List[Tuple[str, nodes.Element]]
+ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
+ target: str, node: pending_xref, contnode: Element
+ ) -> List[Tuple[str, Element]]:
+ results = [] # type: List[Tuple[str, Element]]
for objtype in self.object_types:
todocname = self.objects.get((objtype, target))
if todocname:
@@ -258,14 +242,12 @@ class ReSTDomain(Domain):
contnode, target + ' ' + objtype)))
return results
- def get_objects(self):
- # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
+ def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for (typ, name), docname in self.data['objects'].items():
yield name, name, typ, docname, typ + '-' + name, 1
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(ReSTDomain)
return {
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 5c35083fd..66171646a 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -12,13 +12,16 @@ import re
import unicodedata
import warnings
from copy import copy
+from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union
from typing import cast
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.nodes import Element, Node, system_message
+from docutils.parsers.rst import Directive, directives
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.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
@@ -28,15 +31,13 @@ from sphinx.roles import XRefRole
from sphinx.util import ws_re, logging, docname_join
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import clean_astext, make_refnode
+from sphinx.util.typing import RoleFunction
if False:
# For type annotation
- from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple, Type, Union # NOQA
- from docutils.parsers.rst import Directive # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.util.typing import RoleFunction # NOQA
+ from sphinx.application import Sphinx
+ from sphinx.builders import Builder
+ from sphinx.environment import BuildEnvironment
logger = logging.getLogger(__name__)
@@ -52,10 +53,9 @@ class GenericObject(ObjectDescription):
A generic x-ref directive registered with Sphinx.add_object_type().
"""
indextemplate = ''
- parse_node = None # type: Callable[[GenericObject, BuildEnvironment, str, addnodes.desc_signature], str] # NOQA
+ parse_node = None # type: Callable[[GenericObject, BuildEnvironment, str, desc_signature], str] # NOQA
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
if self.parse_node:
name = self.parse_node(self.env, sig, signode)
else:
@@ -65,8 +65,7 @@ class GenericObject(ObjectDescription):
name = ws_re.sub('', sig)
return name
- def add_target_and_index(self, name, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
targetname = '%s-%s' % (self.objtype, name)
signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode)
@@ -94,8 +93,8 @@ class EnvVarXRefRole(XRefRole):
Cross-referencing role for environment variables (adds an index entry).
"""
- def result_nodes(self, document, env, node, is_ref):
- # type: (nodes.document, BuildEnvironment, nodes.Element, bool) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
+ def result_nodes(self, document: nodes.document, env: "BuildEnvironment", node: Element,
+ is_ref: bool) -> Tuple[List[Node], List[system_message]]:
if not is_ref:
return [node], []
varname = node['reftarget']
@@ -122,8 +121,7 @@ class Target(SphinxDirective):
final_argument_whitespace = True
option_spec = {} # type: Dict
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
# normalize whitespace in fullname like XRefRole does
fullname = ws_re.sub(' ', self.arguments[0].strip())
targetname = '%s-%s' % (self.name, fullname)
@@ -155,8 +153,7 @@ class Cmdoption(ObjectDescription):
Description of a command-line option (.. option).
"""
- def handle_signature(self, sig, signode):
- # type: (str, addnodes.desc_signature) -> str
+ def handle_signature(self, sig: str, signode: desc_signature) -> str:
"""Transform an option description into RST nodes."""
count = 0
firstname = ''
@@ -184,8 +181,7 @@ class Cmdoption(ObjectDescription):
raise ValueError
return firstname
- def add_target_and_index(self, firstname, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
+ def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature) -> None:
currprogram = self.env.ref_context.get('std:program')
for optname in signode.get('allnames', []):
targetname = optname.replace('/', '-')
@@ -223,8 +219,7 @@ class Program(SphinxDirective):
final_argument_whitespace = True
option_spec = {} # type: Dict
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
program = ws_re.sub('-', self.arguments[0].strip())
if program == 'None':
self.env.ref_context.pop('std:program', None)
@@ -234,21 +229,20 @@ class Program(SphinxDirective):
class OptionXRefRole(XRefRole):
- def process_link(self, env, refnode, has_explicit_title, title, target):
- # type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
+ def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
+ title: str, target: str) -> Tuple[str, str]:
refnode['std:program'] = env.ref_context.get('std:program')
return title, target
-def split_term_classifiers(line):
- # type: (str) -> List[Union[str, None]]
+def split_term_classifiers(line: str) -> List[Optional[str]]:
# split line into a term and classifiers. if no classifier, None is used..
parts = re.split(' +: +', line) + [None]
return parts
-def make_glossary_term(env, textnodes, index_key, source, lineno, new_id=None):
- # type: (BuildEnvironment, Iterable[nodes.Node], str, str, int, str) -> nodes.term
+def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index_key: str,
+ source: str, lineno: int, new_id: str = None) -> nodes.term:
# get a text-only representation of the term and register it
# as a cross-reference target
term = nodes.term('', '', *textnodes)
@@ -294,8 +288,7 @@ class Glossary(SphinxDirective):
'sorted': directives.flag,
}
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
node = addnodes.glossary()
node.document = self.state.document
@@ -401,16 +394,15 @@ class Glossary(SphinxDirective):
return messages + [node]
-def token_xrefs(text):
- # type: (str) -> List[nodes.Node]
+def token_xrefs(text: str) -> List[Node]:
retnodes = [] # type: List[nodes.Node]
pos = 0
for m in token_re.finditer(text):
if m.start() > pos:
txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt))
- refnode = addnodes.pending_xref(
- m.group(1), reftype='token', refdomain='std', reftarget=m.group(1))
+ refnode = pending_xref(m.group(1), reftype='token', refdomain='std',
+ reftarget=m.group(1))
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
retnodes.append(refnode)
pos = m.end()
@@ -430,8 +422,7 @@ class ProductionList(SphinxDirective):
final_argument_whitespace = True
option_spec = {} # type: Dict
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
domain = cast(StandardDomain, self.env.get_domain('std'))
node = addnodes.productionlist() # type: nodes.Element
i = 0
@@ -536,8 +527,7 @@ class StandardDomain(Domain):
nodes.container: ('code-block', None),
} # type: Dict[Type[nodes.Node], Tuple[str, Callable]]
- def __init__(self, env):
- # type: (BuildEnvironment) -> None
+ def __init__(self, env: "BuildEnvironment") -> None:
super().__init__(env)
# set up enumerable nodes
@@ -546,27 +536,22 @@ class StandardDomain(Domain):
self.enumerable_nodes[node] = settings
@property
- def objects(self):
- # type: () -> Dict[Tuple[str, str], Tuple[str, str]]
+ def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid
@property
- def progoptions(self):
- # type: () -> Dict[Tuple[str, str], Tuple[str, str]]
+ def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
@property
- def labels(self):
- # type: () -> Dict[str, Tuple[str, str, str]]
+ def labels(self) -> Dict[str, Tuple[str, str, str]]:
return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname
@property
- def anonlabels(self):
- # type: () -> Dict[str, Tuple[str, str]]
+ def anonlabels(self) -> Dict[str, Tuple[str, str]]:
return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
key = None # type: Any
for key, (fn, _l) in list(self.progoptions.items()):
if fn == docname:
@@ -581,8 +566,7 @@ class StandardDomain(Domain):
if fn == docname:
del self.anonlabels[key]
- def merge_domaindata(self, docnames, otherdata):
- # type: (List[str], Dict) -> None
+ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX duplicates?
for key, data in otherdata['progoptions'].items():
if data[0] in docnames:
@@ -597,8 +581,7 @@ class StandardDomain(Domain):
if data[0] in docnames:
self.anonlabels[key] = data
- def process_doc(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
+ def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
for name, explicit in document.nametypes.items():
if not explicit:
continue
@@ -639,17 +622,15 @@ class StandardDomain(Domain):
continue
self.labels[name] = docname, labelid, sectname
- def add_object(self, objtype, name, docname, labelid):
- # type: (str, str, str, str) -> None
+ 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, name, docname, labelid):
- # type: (str, str, str, str) -> None
+ def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
self.progoptions[program, name] = (docname, labelid)
- def build_reference_node(self, fromdocname, builder, docname, labelid,
- sectname, rolename, **options):
- # type: (str, Builder, str, str, str, str, Any) -> nodes.Element
+ def build_reference_node(self, fromdocname: str, builder: "Builder", docname: str,
+ labelid: str, sectname: str, rolename: str, **options
+ ) -> Element:
nodeclass = options.pop('nodeclass', nodes.reference)
newnode = nodeclass('', '', internal=True, **options)
innernode = nodes.inline(sectname, sectname)
@@ -662,7 +643,7 @@ class StandardDomain(Domain):
# set more info in contnode; in case the
# get_relative_uri call raises NoUri,
# the builder will then have to resolve these
- contnode = addnodes.pending_xref('')
+ contnode = pending_xref('')
contnode['refdocname'] = docname
contnode['refsectname'] = sectname
newnode['refuri'] = builder.get_relative_uri(
@@ -672,8 +653,8 @@ class StandardDomain(Domain):
newnode.append(innernode)
return newnode
- def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
+ typ: str, target: str, node: pending_xref, contnode: Element) -> Element:
if typ == 'ref':
resolver = self._resolve_ref_xref
elif typ == 'numref':
@@ -694,8 +675,9 @@ class StandardDomain(Domain):
return resolver(env, fromdocname, builder, typ, target, node, contnode)
- def _resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_ref_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str, node: pending_xref,
+ contnode: Element) -> Element:
if node['refexplicit']:
# reference to anonymous label; the reference uses
# the supplied link caption
@@ -711,8 +693,9 @@ class StandardDomain(Domain):
return self.build_reference_node(fromdocname, builder,
docname, labelid, sectname, 'ref')
- def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_numref_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str,
+ node: pending_xref, contnode: Element) -> Element:
if target in self.labels:
docname, labelid, figname = self.labels.get(target, ('', '', ''))
else:
@@ -772,8 +755,9 @@ class StandardDomain(Domain):
nodeclass=addnodes.number_reference,
title=title)
- def _resolve_keyword_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_keyword_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str,
+ node: pending_xref, contnode: Element) -> Element:
# keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('', '', ''))
if not docname:
@@ -781,8 +765,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def _resolve_doc_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_doc_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str,
+ node: pending_xref, contnode: Element) -> Element:
# directly reference to document by source name; can be absolute or relative
refdoc = node.get('refdoc', fromdocname)
docname = docname_join(refdoc, node['reftarget'])
@@ -797,8 +782,9 @@ class StandardDomain(Domain):
innernode = nodes.inline(caption, caption, classes=['doc'])
return make_refnode(builder, fromdocname, docname, None, innernode)
- def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_option_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str,
+ node: pending_xref, contnode: Element) -> Element:
progname = node.get('std:program')
target = target.strip()
docname, labelid = self.progoptions.get((progname, target), ('', ''))
@@ -818,8 +804,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def _resolve_citation_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ 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))
@@ -841,8 +828,9 @@ class StandardDomain(Domain):
del node['ids'][:]
raise
- def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", typ: str, target: str,
+ node: pending_xref, contnode: Element) -> Element:
objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes:
if (objtype, target) in self.objects:
@@ -855,8 +843,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str,
+ builder: "Builder", target: str, node: pending_xref,
+ contnode: Element) -> List[Tuple[str, Element]]:
results = [] # type: List[Tuple[str, nodes.Element]]
ltarget = target.lower() # :ref: lowercases its target automatically
for role in ('ref', 'option'): # do not try "keyword"
@@ -877,8 +866,7 @@ class StandardDomain(Domain):
labelid, contnode)))
return results
- def get_objects(self):
- # type: () -> Iterator[Tuple[str, str, str, str, str, int]]
+ def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# handle the special 'doc' reference here
for doc in self.env.all_docs:
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
@@ -899,34 +887,30 @@ class StandardDomain(Domain):
if name not in non_anon_labels:
yield (name, name, 'label', docname, labelid, -1)
- def get_type_name(self, type, primary=False):
- # type: (ObjType, bool) -> str
+ def get_type_name(self, type: ObjType, primary: bool = False) -> str:
# never prepend "Default"
return type.lname
- def is_enumerable_node(self, node):
- # type: (nodes.Node) -> bool
+ def is_enumerable_node(self, node: Node) -> bool:
return node.__class__ in self.enumerable_nodes
- def get_numfig_title(self, node):
- # type: (nodes.Node) -> str
+ def get_numfig_title(self, node: Node) -> str:
"""Get the title of enumerable nodes to refer them using its title"""
if self.is_enumerable_node(node):
- _, title_getter = self.enumerable_nodes.get(node.__class__, (None, None))
+ elem = cast(nodes.Element, node)
+ _, title_getter = self.enumerable_nodes.get(elem.__class__, (None, None))
if title_getter:
- return title_getter(node)
+ return title_getter(elem)
else:
- for subnode in node:
- if subnode.tagname in ('caption', 'title'):
+ for subnode in elem:
+ if isinstance(subnode, (nodes.caption, nodes.title)):
return clean_astext(subnode)
return None
- def get_enumerable_node_type(self, node):
- # type: (nodes.Node) -> str
+ def get_enumerable_node_type(self, node: Node) -> str:
"""Get type of enumerable nodes."""
- def has_child(node, cls):
- # type: (nodes.Element, Type) -> bool
+ def has_child(node: Element, cls: Type) -> bool:
return any(isinstance(child, cls) for child in node)
if isinstance(node, nodes.section):
@@ -940,8 +924,7 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype
- def get_figtype(self, node):
- # type: (nodes.Node) -> str
+ def get_figtype(self, node: Node) -> str:
"""Get figure type of nodes.
.. deprecated:: 1.8
@@ -951,8 +934,8 @@ class StandardDomain(Domain):
RemovedInSphinx30Warning, stacklevel=2)
return self.get_enumerable_node_type(node)
- def get_fignumber(self, env, builder, figtype, docname, target_node):
- # type: (BuildEnvironment, Builder, str, str, nodes.Element) -> Tuple[int, ...]
+ def get_fignumber(self, env: "BuildEnvironment", builder: "Builder",
+ figtype: str, docname: str, target_node: Element) -> Tuple[int, ...]:
if figtype == 'section':
if builder.name == 'latex':
return tuple()
@@ -974,8 +957,7 @@ class StandardDomain(Domain):
# Maybe it is defined in orphaned document.
raise ValueError
- def get_full_qualified_name(self, node):
- # type: (nodes.Element) -> str
+ def get_full_qualified_name(self, node: Element) -> str:
if node.get('reftype') == 'option':
progname = node.get('std:program')
command = ws_re.split(node.get('reftarget'))
@@ -989,24 +971,20 @@ class StandardDomain(Domain):
else:
return None
- def note_citations(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
+ def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_citations() is deprecated.',
RemovedInSphinx40Warning)
- def note_citation_refs(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
+ def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
RemovedInSphinx40Warning)
- def note_labels(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
+ def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_labels() is deprecated.',
RemovedInSphinx40Warning)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_domain(StandardDomain)
return {
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index af3b6cf1f..59d120e82 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -15,14 +15,22 @@ 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 docutils import nodes
+from docutils.nodes import Node
from sphinx import addnodes
+from sphinx.config import Config
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
+from sphinx.domains import Domain
from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
+from sphinx.events import EventManager
from sphinx.locale import __
+from sphinx.project import Project
from sphinx.transforms import SphinxTransformer
from sphinx.util import DownloadFiles, FilenameUniqDict
from sphinx.util import logging
@@ -32,14 +40,8 @@ from sphinx.util.nodes import is_translatable
if False:
# For type annotation
- from typing import Any, Callable, Dict, IO, Iterator, List, Optional, Set, Tuple, Union # NOQA
- from docutils import nodes # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.config import Config # NOQA
- from sphinx.event import EventManager # NOQA
- from sphinx.domains import Domain # NOQA
- from sphinx.project import Project # NOQA
+ from sphinx.application import Sphinx
+ from sphinx.builders import Builder
logger = logging.getLogger(__name__)
@@ -92,8 +94,7 @@ class BuildEnvironment:
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
- def __init__(self, app=None):
- # type: (Sphinx) -> None
+ def __init__(self, app: "Sphinx" = None):
self.app = None # type: Sphinx
self.doctreedir = None # type: str
self.srcdir = None # type: str
@@ -191,19 +192,16 @@ class BuildEnvironment:
if app:
self.setup(app)
- def __getstate__(self):
- # type: () -> Dict
+ def __getstate__(self) -> Dict:
"""Obtains serializable data for pickling."""
__dict__ = self.__dict__.copy()
__dict__.update(app=None, domains={}, events=None) # clear unpickable attributes
return __dict__
- def __setstate__(self, state):
- # type: (Dict) -> None
+ def __setstate__(self, state: Dict) -> None:
self.__dict__.update(state)
- def setup(self, app):
- # type: (Sphinx) -> None
+ def setup(self, app: "Sphinx") -> None:
"""Set up BuildEnvironment object."""
if self.version and self.version != app.registry.get_envversion(app):
raise BuildEnvironmentError(__('build environment version not current'))
@@ -231,12 +229,13 @@ class BuildEnvironment:
# initialie settings
self._update_settings(app.config)
- def _update_config(self, config):
- # type: (Config) -> None
+ def _update_config(self, config: Config) -> None:
"""Update configurations by new one."""
self.config_status = CONFIG_OK
if self.config is None:
self.config_status = CONFIG_NEW
+ elif self.config.extensions != config.extensions:
+ self.config_status = CONFIG_EXTENSIONS_CHANGED
else:
# check if a config value was changed that affects how
# doctrees are read
@@ -245,15 +244,9 @@ class BuildEnvironment:
self.config_status = CONFIG_CHANGED
break
- # this value is not covered by the above loop because it is handled
- # specially by the config class
- if self.config.extensions != config.extensions:
- self.config_status = CONFIG_EXTENSIONS_CHANGED
-
self.config = config
- def _update_settings(self, config):
- # type: (Config) -> None
+ def _update_settings(self, config: Config) -> None:
"""Update settings by new config."""
self.settings['input_encoding'] = config.source_encoding
self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space
@@ -262,8 +255,7 @@ class BuildEnvironment:
# Allow to disable by 3rd party extension (workaround)
self.settings.setdefault('smart_quotes', True)
- def set_versioning_method(self, method, compare):
- # type: (Union[str, Callable], bool) -> None
+ def set_versioning_method(self, method: Union[str, Callable], compare: bool) -> None:
"""This sets the doctree versioning method for this environment.
Versioning methods are a builder property; only builders with the same
@@ -286,8 +278,7 @@ class BuildEnvironment:
self.versioning_condition = condition
self.versioning_compare = compare
- def clear_doc(self, docname):
- # type: (str) -> None
+ def clear_doc(self, docname: str) -> None:
"""Remove all traces of a source file in the inventory."""
if docname in self.all_docs:
self.all_docs.pop(docname, None)
@@ -297,8 +288,8 @@ class BuildEnvironment:
for domain in self.domains.values():
domain.clear_doc(docname)
- def merge_info_from(self, docnames, other, app):
- # type: (List[str], BuildEnvironment, Sphinx) -> None
+ def merge_info_from(self, docnames: List[str], other: "BuildEnvironment",
+ app: "Sphinx") -> None:
"""Merge global information gathered about *docnames* while reading them
from the *other* environment.
@@ -315,16 +306,14 @@ class BuildEnvironment:
domain.merge_domaindata(docnames, other.domaindata[domainname])
self.events.emit('env-merge-info', self, docnames, other)
- def path2doc(self, filename):
- # type: (str) -> Optional[str]
+ def path2doc(self, filename: str) -> str:
"""Return the docname for the filename if the file is document.
*filename* should be absolute or relative to the source directory.
"""
return self.project.path2doc(filename)
- def doc2path(self, docname, base=True, suffix=None):
- # type: (str, Union[bool, str], str) -> str
+ def doc2path(self, docname: str, base: Union[bool, str] = True, suffix: str = None) -> str:
"""Return the filename for the document name.
If *base* is True, return absolute path under self.srcdir.
@@ -347,8 +336,7 @@ class BuildEnvironment:
pathname = path.join(base, pathname) # type: ignore
return pathname
- def relfn2path(self, filename, docname=None):
- # type: (str, str) -> Tuple[str, str]
+ def relfn2path(self, filename: str, docname: str = None) -> Tuple[str, str]:
"""Return paths to a file referenced from a document, relative to
documentation root and absolute.
@@ -367,13 +355,11 @@ class BuildEnvironment:
return rel_fn, path.abspath(path.join(self.srcdir, rel_fn))
@property
- def found_docs(self):
- # type: () -> Set[str]
+ def found_docs(self) -> Set[str]:
"""contains all existing docnames."""
return self.project.docnames
- def find_files(self, config, builder):
- # type: (Config, Builder) -> None
+ def find_files(self, config: Config, builder: "Builder") -> None:
"""Find all source files in the source dir and put them in
self.found_docs.
"""
@@ -401,8 +387,7 @@ class BuildEnvironment:
except OSError as exc:
raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc))
- def get_outdated_files(self, config_changed):
- # type: (bool) -> Tuple[Set[str], Set[str], Set[str]]
+ def get_outdated_files(self, config_changed: bool) -> Tuple[Set[str], Set[str], Set[str]]:
"""Return (added, changed, removed) sets."""
# clear all files no longer present
removed = set(self.all_docs) - self.found_docs
@@ -452,8 +437,7 @@ class BuildEnvironment:
return added, changed, removed
- def check_dependents(self, app, already):
- # type: (Sphinx, Set[str]) -> Iterator[str]
+ def check_dependents(self, app: "Sphinx", already: Set[str]) -> Generator[str, None, None]:
to_rewrite = [] # type: List[str]
for docnames in self.events.emit('env-get-updated', self):
to_rewrite.extend(docnames)
@@ -463,8 +447,7 @@ class BuildEnvironment:
# --------- SINGLE FILE READING --------------------------------------------
- def prepare_settings(self, docname):
- # type: (str) -> None
+ def prepare_settings(self, docname: str) -> None:
"""Prepare to set up environment for reading."""
self.temp_data['docname'] = docname
# defaults to the global default, but can be re-set in a document
@@ -475,13 +458,11 @@ class BuildEnvironment:
# utilities to use while reading a document
@property
- def docname(self):
- # type: () -> str
+ def docname(self) -> str:
"""Returns the docname of the document currently being parsed."""
return self.temp_data['docname']
- def new_serialno(self, category=''):
- # type: (str) -> int
+ def new_serialno(self, category: str = '') -> int:
"""Return a serial number, e.g. for index entry targets.
The number is guaranteed to be unique in the current document.
@@ -491,8 +472,7 @@ class BuildEnvironment:
self.temp_data[key] = cur + 1
return cur
- def note_dependency(self, filename):
- # type: (str) -> None
+ def note_dependency(self, filename: str) -> None:
"""Add *filename* as a dependency of the current document.
This means that the document will be rebuilt if this file changes.
@@ -501,8 +481,7 @@ class BuildEnvironment:
"""
self.dependencies[self.docname].add(filename)
- def note_included(self, filename):
- # type: (str) -> None
+ def note_included(self, filename: str) -> None:
"""Add *filename* as a included from other document.
This means the document is not orphaned.
@@ -511,15 +490,13 @@ class BuildEnvironment:
"""
self.included[self.docname].add(self.path2doc(filename))
- def note_reread(self):
- # type: () -> None
+ def note_reread(self) -> None:
"""Add the current document to the list of documents that will
automatically be re-read at the next build.
"""
self.reread_always.add(self.docname)
- def get_domain(self, domainname):
- # type: (str) -> Domain
+ def get_domain(self, domainname: str) -> Domain:
"""Return the domain instance with the specified name.
Raises an ExtensionError if the domain is not registered.
@@ -531,8 +508,7 @@ class BuildEnvironment:
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
- def get_doctree(self, docname):
- # type: (str) -> nodes.document
+ def get_doctree(self, docname: str) -> nodes.document:
"""Read the doctree for a file from the pickle and return it."""
filename = path.join(self.doctreedir, docname + '.doctree')
with open(filename, 'rb') as f:
@@ -541,9 +517,9 @@ class BuildEnvironment:
doctree.reporter = LoggingReporter(self.doc2path(docname))
return doctree
- def get_and_resolve_doctree(self, docname, builder, doctree=None,
- prune_toctrees=True, includehidden=False):
- # type: (str, Builder, nodes.document, bool, bool) -> nodes.document
+ def get_and_resolve_doctree(self, docname: str, builder: "Builder",
+ doctree: nodes.document = None, prune_toctrees: bool = True,
+ includehidden: bool = False) -> nodes.document:
"""Read the doctree from the pickle, resolve cross-references and
toctrees and return it.
"""
@@ -565,9 +541,9 @@ class BuildEnvironment:
return doctree
- def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0,
- titles_only=False, collapse=False, includehidden=False):
- # type: (str, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Node
+ def resolve_toctree(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
+ prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
+ collapse: bool = False, includehidden: bool = False) -> Node:
"""Resolve a *toctree* node into individual bullet lists with titles
as items, returning None (if no containing titles are found) or
a new node.
@@ -583,12 +559,11 @@ class BuildEnvironment:
maxdepth, titles_only, collapse,
includehidden)
- def resolve_references(self, doctree, fromdocname, builder):
- # type: (nodes.document, str, Builder) -> None
+ def resolve_references(self, doctree: nodes.document, fromdocname: str,
+ builder: "Builder") -> None:
self.apply_post_transforms(doctree, fromdocname)
- def apply_post_transforms(self, doctree, docname):
- # type: (nodes.document, str) -> None
+ def apply_post_transforms(self, doctree: nodes.document, docname: str) -> None:
"""Apply all post-transforms."""
try:
# set env.docname during applying post-transforms
@@ -605,12 +580,10 @@ class BuildEnvironment:
# allow custom references to be resolved
self.events.emit('doctree-resolved', doctree, docname)
- def collect_relations(self):
- # type: () -> Dict[str, List[str]]
+ def collect_relations(self) -> Dict[str, List[str]]:
traversed = set()
- def traverse_toctree(parent, docname):
- # type: (str, str) -> Iterator[Tuple[str, str]]
+ def traverse_toctree(parent: str, docname: str) -> Iterator[Tuple[str, str]]:
if parent == docname:
logger.warning(__('self referenced toctree found. Ignored.'), location=docname)
return
@@ -639,8 +612,7 @@ class BuildEnvironment:
return relations
- def check_consistency(self):
- # type: () -> None
+ def check_consistency(self) -> None:
"""Do consistency checks."""
included = set().union(*self.included.values()) # type: ignore
for docname in sorted(self.all_docs):
@@ -663,48 +635,41 @@ class BuildEnvironment:
# --------- METHODS FOR COMPATIBILITY --------------------------------------
- def update(self, config, srcdir, doctreedir):
- # type: (Config, str, str) -> List[str]
+ 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, app):
- # type: (List[str], Sphinx) -> None
+ 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, app, nproc):
- # type: (List[str], Sphinx, int) -> None
+ 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, app=None):
- # type: (str, Sphinx) -> None
+ 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, doctree):
- # type: (str, nodes.document) -> None
+ 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):
- # type: () -> List[str]
+ 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, app=None):
- # type: (IO, Sphinx) -> BuildEnvironment
+ def load(f: IO, app: "Sphinx" = None) -> "BuildEnvironment":
warnings.warn('BuildEnvironment.load() is deprecated. '
'Please use pickle.load() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -720,8 +685,7 @@ class BuildEnvironment:
return env
@classmethod
- def loads(cls, string, app=None):
- # type: (bytes, Sphinx) -> BuildEnvironment
+ def loads(cls, string: bytes, app: "Sphinx" = None) -> "BuildEnvironment":
warnings.warn('BuildEnvironment.loads() is deprecated. '
'Please use pickle.loads() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -729,8 +693,7 @@ class BuildEnvironment:
return cls.load(io, app)
@classmethod
- def frompickle(cls, filename, app):
- # type: (str, Sphinx) -> BuildEnvironment
+ def frompickle(cls, filename: str, app: "Sphinx") -> "BuildEnvironment":
warnings.warn('BuildEnvironment.frompickle() is deprecated. '
'Please use pickle.load() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -738,16 +701,14 @@ class BuildEnvironment:
return cls.load(f, app)
@staticmethod
- def dump(env, f):
- # type: (BuildEnvironment, IO) -> None
+ 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):
- # type: (BuildEnvironment) -> bytes
+ def dumps(cls, env: "BuildEnvironment") -> bytes:
warnings.warn('BuildEnvironment.dumps() is deprecated. '
'Please use pickle.dumps() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -755,8 +716,7 @@ class BuildEnvironment:
cls.dump(env, io)
return io.getvalue()
- def topickle(self, filename):
- # type: (str) -> None
+ def topickle(self, filename: str) -> None:
warnings.warn('env.topickle() is deprecated. '
'Please use pickle.dump() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -764,15 +724,14 @@ class BuildEnvironment:
self.dump(self, f)
@property
- def versionchanges(self):
- # type: () -> Dict[str, List[Tuple[str, str, int, str, str, str]]]
+ 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, version, node, lineno):
- # type: (str, str, addnodes.versionmodified, int) -> None
+ 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)
diff --git a/sphinx/environment/adapters/asset.py b/sphinx/environment/adapters/asset.py
index b57943967..bc282de0c 100644
--- a/sphinx/environment/adapters/asset.py
+++ b/sphinx/environment/adapters/asset.py
@@ -8,18 +8,14 @@
:license: BSD, see LICENSE for details.
"""
-if False:
- # For type annotation
- from sphinx.environment import BuildEnvironment # NOQA
+from sphinx.environment import BuildEnvironment
class ImageAdapter:
- def __init__(self, env):
- # type: (BuildEnvironment) -> None
+ def __init__(self, env: BuildEnvironment) -> None:
self.env = env
- def get_original_image_uri(self, name):
- # type: (str) -> str
+ def get_original_image_uri(self, name: str) -> str:
"""Get the original image URI."""
while name in self.env.original_image_uri:
name = self.env.original_image_uri[name]
diff --git a/sphinx/environment/adapters/indexentries.py b/sphinx/environment/adapters/indexentries.py
index 38d28f0b0..68198040d 100644
--- a/sphinx/environment/adapters/indexentries.py
+++ b/sphinx/environment/adapters/indexentries.py
@@ -11,33 +11,30 @@ import bisect
import re
import unicodedata
from itertools import groupby
+from typing import Any, Dict, Pattern, List, Tuple
+from sphinx.builders import Builder
+from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import split_into, logging
-if False:
- # For type annotation
- from typing import Any, Dict, Pattern, List, Tuple # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__)
class IndexEntries:
- def __init__(self, env):
- # type: (BuildEnvironment) -> None
+ def __init__(self, env: BuildEnvironment) -> None:
self.env = env
- def create_index(self, builder, group_entries=True,
- _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
- # type: (Builder, bool, Pattern) -> List[Tuple[str, List[Tuple[str, Any]]]]
+ def create_index(self, builder: Builder, group_entries: bool = True,
+ _fixre: Pattern = re.compile(r'(.*) ([(][^()]*[)])')
+ ) -> List[Tuple[str, List[Tuple[str, Any]]]]:
"""Create the real index from the collected index entries."""
new = {} # type: Dict[str, List]
- def add_entry(word, subword, main, link=True, dic=new, key=None):
- # type: (str, str, str, bool, Dict, str) -> None
+ def add_entry(word: str, subword: str, main: str, link: bool = True,
+ dic: Dict = new, key: str = None) -> None:
# Force the word to be unicode if it's a ASCII bytestring.
# This will solve problems with unicode normalization later.
# For instance the RFC role will add bytestrings at the moment
@@ -91,8 +88,7 @@ class IndexEntries:
# sort the index entries; put all symbols at the front, even those
# following the letters in ASCII, this is where the chr(127) comes from
- def keyfunc(entry):
- # type: (Tuple[str, List]) -> Tuple[str, str]
+ def keyfunc(entry: Tuple[str, List]) -> Tuple[str, str]:
key, (void, void, category_key) = entry
if category_key:
# using specified category key to sort
@@ -137,12 +133,21 @@ class IndexEntries:
oldsubitems = subitems
i += 1
+ # sort the sub-index entries
+ def keyfunc2(entry: Tuple[str, List]) -> str:
+ key = unicodedata.normalize('NFD', entry[0].lower())
+ if key.startswith('\N{RIGHT-TO-LEFT MARK}'):
+ key = key[1:]
+ if key[0:1].isalpha() or key.startswith('_'):
+ key = chr(127) + key
+ return key
+
# group the entries by letter
- def keyfunc2(item):
- # type: (Tuple[str, List]) -> str
+ def keyfunc3(item: Tuple[str, List]) -> str:
# hack: mutating the subitems dicts to a list in the keyfunc
k, v = item
- v[1] = sorted((si, se) for (si, (se, void, void)) in v[1].items())
+ v[1] = sorted(((si, se) for (si, (se, void, void)) in v[1].items()),
+ key=keyfunc2)
if v[2] is None:
# now calculate the key
if k.startswith('\N{RIGHT-TO-LEFT MARK}'):
@@ -156,4 +161,4 @@ class IndexEntries:
else:
return v[2]
return [(key_, list(group))
- for (key_, group) in groupby(newlist, keyfunc2)]
+ for (key_, group) in groupby(newlist, keyfunc3)]
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index fe7bd3d0d..d5f827dde 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -8,9 +8,11 @@
:license: BSD, see LICENSE for details.
"""
-from typing import Iterable, cast
+from typing import cast
+from typing import Iterable, List
from docutils import nodes
+from docutils.nodes import Element, Node
from sphinx import addnodes
from sphinx.locale import __
@@ -20,20 +22,18 @@ from sphinx.util.nodes import clean_astext, process_only_nodes
if False:
# For type annotation
- from typing import Any, Dict, List # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.builders import Builder
+ from sphinx.environment import BuildEnvironment
+
logger = logging.getLogger(__name__)
class TocTree:
- def __init__(self, env):
- # type: (BuildEnvironment) -> None
+ def __init__(self, env: "BuildEnvironment") -> None:
self.env = env
- def note(self, docname, toctreenode):
- # type: (str, addnodes.toctree) -> None
+ def note(self, docname: str, toctreenode: addnodes.toctree) -> None:
"""Note a TOC tree directive in a document and gather information about
file relations from it.
"""
@@ -48,9 +48,9 @@ class TocTree:
self.env.files_to_rebuild.setdefault(includefile, set()).add(docname)
self.env.toctree_includes.setdefault(docname, []).extend(includefiles)
- def resolve(self, docname, builder, toctree, prune=True, maxdepth=0,
- titles_only=False, collapse=False, includehidden=False):
- # type: (str, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Element
+ def resolve(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
+ prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
+ collapse: bool = False, includehidden: bool = False) -> Element:
"""Resolve a *toctree* node into individual bullet lists with titles
as items, returning None (if no containing titles are found) or
a new node.
@@ -86,8 +86,7 @@ class TocTree:
toctree_ancestors = self.get_toctree_ancestors(docname)
excluded = Matcher(self.env.config.exclude_patterns)
- def _toctree_add_classes(node, depth):
- # type: (nodes.Element, int) -> None
+ def _toctree_add_classes(node: Element, depth: int) -> None:
"""Add 'toctree-l%d' and 'current' classes to the toctree."""
for subnode in node.children:
if isinstance(subnode, (addnodes.compact_paragraph,
@@ -105,7 +104,7 @@ class TocTree:
if not subnode['anchorname']:
# give the whole branch a 'current' class
# (useful for styling it differently)
- branchnode = subnode # type: nodes.Element
+ branchnode = subnode # type: Element
while branchnode:
branchnode['classes'].append('current')
branchnode = branchnode.parent
@@ -117,11 +116,12 @@ class TocTree:
subnode['iscurrent'] = True
subnode = subnode.parent
- def _entries_from_toctree(toctreenode, parents, separate=False, subtree=False):
- # type: (addnodes.toctree, List[str], bool, bool) -> List[nodes.Element]
+ def _entries_from_toctree(toctreenode: addnodes.toctree, parents: List[str],
+ separate: bool = False, subtree: bool = False
+ ) -> List[Element]:
"""Return TOC entries for a toctree node."""
refs = [(e[0], e[1]) for e in toctreenode['entries']]
- entries = [] # type: List[nodes.Element]
+ entries = [] # type: List[Element]
for (title, ref) in refs:
try:
refdoc = None
@@ -265,8 +265,7 @@ class TocTree:
docname, refnode['refuri']) + refnode['anchorname']
return newnode
- def get_toctree_ancestors(self, docname):
- # type: (str) -> List[str]
+ def get_toctree_ancestors(self, docname: str) -> List[str]:
parent = {}
for p, children in self.env.toctree_includes.items():
for child in children:
@@ -278,8 +277,8 @@ class TocTree:
d = parent[d]
return ancestors
- def _toctree_prune(self, node, depth, maxdepth, collapse=False):
- # type: (nodes.Element, int, int, bool) -> None
+ def _toctree_prune(self, node: Element, depth: int, maxdepth: int, collapse: bool = False
+ ) -> None:
"""Utility: Cut a TOC at a specified depth."""
for subnode in node.children[:]:
if isinstance(subnode, (addnodes.compact_paragraph,
@@ -300,8 +299,7 @@ class TocTree:
# recurse on visible children
self._toctree_prune(subnode, depth + 1, maxdepth, collapse)
- def get_toc_for(self, docname, builder):
- # type: (str, Builder) -> nodes.Node
+ def get_toc_for(self, docname: str, builder: "Builder") -> Node:
"""Return a TOC nodetree -- for use on the same page only!"""
tocdepth = self.env.metadata[docname].get('tocdepth', 0)
try:
@@ -316,11 +314,11 @@ class TocTree:
node['refuri'] = node['anchorname'] or '#'
return toc
- def get_toctree_for(self, docname, builder, collapse, **kwds):
- # type: (str, Builder, bool, Any) -> nodes.Element
+ def get_toctree_for(self, docname: str, builder: "Builder", collapse: bool, **kwds
+ ) -> Element:
"""Return the global TOC nodetree."""
doctree = self.env.get_doctree(self.env.config.master_doc)
- toctrees = [] # type: List[nodes.Element]
+ toctrees = [] # type: List[Element]
if 'includehidden' not in kwds:
kwds['includehidden'] = True
if 'maxdepth' not in kwds:
diff --git a/sphinx/environment/collectors/__init__.py b/sphinx/environment/collectors/__init__.py
index 3f7b9344b..eb16a9f25 100644
--- a/sphinx/environment/collectors/__init__.py
+++ b/sphinx/environment/collectors/__init__.py
@@ -8,12 +8,12 @@
:license: BSD, see LICENSE for details.
"""
-if False:
- # For type annotation
- from typing import Dict, List, Set # NOQA
- from docutils import nodes # NOQA
- from sphinx.sphinx import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+from typing import Dict, List, Set
+
+from docutils import nodes
+
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
class EnvironmentCollector:
@@ -27,8 +27,7 @@ class EnvironmentCollector:
listener_ids = None # type: Dict[str, int]
- def enable(self, app):
- # type: (Sphinx) -> None
+ def enable(self, app: Sphinx) -> None:
assert self.listener_ids is None
self.listener_ids = {
'doctree-read': app.connect('doctree-read', self.process_doc),
@@ -38,43 +37,39 @@ class EnvironmentCollector:
'env-get-outdated': app.connect('env-get-outdated', self.get_outdated_docs),
}
- def disable(self, app):
- # type: (Sphinx) -> None
+ def disable(self, app: Sphinx) -> None:
assert self.listener_ids is not None
for listener_id in self.listener_ids.values():
app.disconnect(listener_id)
self.listener_ids = None
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
"""Remove specified data of a document.
This method is called on the removal of the document."""
raise NotImplementedError
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
"""Merge in specified data regarding docnames from a different `BuildEnvironment`
object which coming from a subprocess in parallel builds."""
raise NotImplementedError
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process a document and gather specific data from it.
This method is called after the document is read."""
raise NotImplementedError
- def get_updated_docs(self, app, env):
- # type: (Sphinx, BuildEnvironment) -> List[str]
+ def get_updated_docs(self, app: Sphinx, env: BuildEnvironment) -> List[str]:
"""Return a list of docnames to re-read.
This methods is called after reading the whole of documents (experimental).
"""
return []
- def get_outdated_docs(self, app, env, added, changed, removed):
- # type: (Sphinx, BuildEnvironment, str, Set[str], Set[str], Set[str]) -> List[str]
+ def get_outdated_docs(self, app: Sphinx, env: BuildEnvironment,
+ added: Set[str], changed: Set[str], removed: Set[str]) -> List[str]:
"""Return a list of docnames to re-read.
This methods is called before reading the documents.
diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py
index fed8280c1..e1d3abef1 100644
--- a/sphinx/environment/collectors/asset.py
+++ b/sphinx/environment/collectors/asset.py
@@ -11,22 +11,21 @@
import os
from glob import glob
from os import path
+from typing import Any, Dict, List, Set
from docutils import nodes
+from docutils.nodes import Node
from docutils.utils import relative_path
from sphinx import addnodes
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.i18n import get_image_filename_for_language, search_image_for_language
from sphinx.util.images import guess_mimetype
-if False:
- # For type annotation
- from typing import Dict, List, Set # NOQA
- from sphinx.sphinx import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__)
@@ -34,16 +33,14 @@ logger = logging.getLogger(__name__)
class ImageCollector(EnvironmentCollector):
"""Image files collector for sphinx.environment."""
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.images.purge_doc(docname)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
env.images.merge_other(docnames, other.images)
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process and rewrite image URIs."""
docname = app.env.docname
@@ -92,8 +89,8 @@ class ImageCollector(EnvironmentCollector):
continue
app.env.images.add_file(docname, imgpath)
- def collect_candidates(self, env, imgpath, candidates, node):
- # type: (BuildEnvironment, str, Dict[str, str], nodes.Node) -> None
+ def collect_candidates(self, env: BuildEnvironment, imgpath: str,
+ candidates: Dict[str, str], node: Node) -> None:
globbed = {} # type: Dict[str, List[str]]
for filename in glob(imgpath):
new_imgpath = relative_path(path.join(env.srcdir, 'dummy'),
@@ -115,16 +112,14 @@ class ImageCollector(EnvironmentCollector):
class DownloadFileCollector(EnvironmentCollector):
"""Download files collector for sphinx.environment."""
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.dlfiles.purge_doc(docname)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
env.dlfiles.merge_other(docnames, other.dlfiles)
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process downloadable file paths. """
for node in doctree.traverse(addnodes.download_reference):
targetname = node['reftarget']
@@ -140,8 +135,7 @@ class DownloadFileCollector(EnvironmentCollector):
node['filename'] = app.env.dlfiles.add_file(app.env.docname, rel_filename)
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(ImageCollector)
app.add_env_collector(DownloadFileCollector)
diff --git a/sphinx/environment/collectors/dependencies.py b/sphinx/environment/collectors/dependencies.py
index b091b371d..63ae63e84 100644
--- a/sphinx/environment/collectors/dependencies.py
+++ b/sphinx/environment/collectors/dependencies.py
@@ -10,35 +10,30 @@
import os
from os import path
+from typing import Any, Dict, Set
+from docutils import nodes
from docutils.utils import relative_path
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util.osutil import fs_encoding
-if False:
- # For type annotation
- from typing import Dict, Set # NOQA
- from docutils import nodes # NOQA
- from sphinx.sphinx import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
-
class DependenciesCollector(EnvironmentCollector):
"""dependencies collector for sphinx.environment."""
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.dependencies.pop(docname, None)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
if docname in other.dependencies:
env.dependencies[docname] = other.dependencies[docname]
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process docutils-generated dependency info."""
cwd = os.getcwd()
frompath = path.join(path.normpath(app.srcdir), 'dummy')
@@ -55,8 +50,7 @@ class DependenciesCollector(EnvironmentCollector):
app.env.dependencies[app.env.docname].add(relpath)
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(DependenciesCollector)
return {
diff --git a/sphinx/environment/collectors/indexentries.py b/sphinx/environment/collectors/indexentries.py
index 2d8af7887..9c86779fc 100644
--- a/sphinx/environment/collectors/indexentries.py
+++ b/sphinx/environment/collectors/indexentries.py
@@ -8,34 +8,31 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, Set
+
+from docutils import nodes
+
from sphinx import addnodes
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util import split_index_msg, logging
-if False:
- # For type annotation
- from typing import Dict, Set # NOQA
- from docutils import nodes # NOQA
- from sphinx.applicatin import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
-
logger = logging.getLogger(__name__)
class IndexEntriesCollector(EnvironmentCollector):
name = 'indices'
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.indexentries.pop(docname, None)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
env.indexentries[docname] = other.indexentries[docname]
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
docname = app.env.docname
entries = app.env.indexentries[docname] = []
for node in doctree.traverse(addnodes.index):
@@ -50,8 +47,7 @@ class IndexEntriesCollector(EnvironmentCollector):
entries.append(entry)
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(IndexEntriesCollector)
return {
diff --git a/sphinx/environment/collectors/metadata.py b/sphinx/environment/collectors/metadata.py
index 9168e7840..a547d2551 100644
--- a/sphinx/environment/collectors/metadata.py
+++ b/sphinx/environment/collectors/metadata.py
@@ -8,33 +8,28 @@
:license: BSD, see LICENSE for details.
"""
-from typing import List, cast
+from typing import Any, Dict, List, Set
+from typing import cast
from docutils import nodes
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
-if False:
- # For type annotation
- from typing import Dict, Set # NOQA
- from sphinx.sphinx import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
-
class MetadataCollector(EnvironmentCollector):
"""metadata collector for sphinx.environment."""
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.metadata.pop(docname, None)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
env.metadata[docname] = other.metadata[docname]
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process the docinfo part of the doctree as metadata.
Keep processing minimal -- just return what docutils says.
@@ -67,8 +62,7 @@ class MetadataCollector(EnvironmentCollector):
doctree.pop(0)
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(MetadataCollector)
return {
diff --git a/sphinx/environment/collectors/title.py b/sphinx/environment/collectors/title.py
index 0f43eb0af..7d464f874 100644
--- a/sphinx/environment/collectors/title.py
+++ b/sphinx/environment/collectors/title.py
@@ -8,34 +8,30 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, Set
+
from docutils import nodes
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.transforms import SphinxContentsFilter
-if False:
- # For type annotation
- from typing import Dict, Set # NOQA
- from sphinx.sphinx import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
-
class TitleCollector(EnvironmentCollector):
"""title collector for sphinx.environment."""
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.titles.pop(docname, None)
env.longtitles.pop(docname, None)
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
env.titles[docname] = other.titles[docname]
env.longtitles[docname] = other.longtitles[docname]
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Add a title node to the document (just copy the first section title),
and store that title in the environment.
"""
@@ -59,8 +55,7 @@ class TitleCollector(EnvironmentCollector):
app.env.longtitles[app.env.docname] = longtitlenode
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(TitleCollector)
return {
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index 53009c753..d1e211d2c 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -8,31 +8,29 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, List, Set, Tuple, Type, TypeVar
from typing import cast
from docutils import nodes
+from docutils.nodes import Element, Node
from sphinx import addnodes
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
from sphinx.environment.adapters.toctree import TocTree
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.locale import __
from sphinx.transforms import SphinxContentsFilter
from sphinx.util import url_re, logging
-if False:
- # For type annotation
- from typing import Dict, List, Set, Tuple, Type, TypeVar # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- N = TypeVar('N')
+N = TypeVar('N')
logger = logging.getLogger(__name__)
class TocTreeCollector(EnvironmentCollector):
- def clear_doc(self, app, env, docname):
- # type: (Sphinx, BuildEnvironment, str) -> None
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.tocs.pop(docname, None)
env.toc_secnumbers.pop(docname, None)
env.toc_fignumbers.pop(docname, None)
@@ -46,8 +44,8 @@ class TocTreeCollector(EnvironmentCollector):
if not fnset:
del env.files_to_rebuild[subfn]
- def merge_other(self, app, env, docnames, other):
- # type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
+ def merge_other(self, app: Sphinx, env: BuildEnvironment, docnames: Set[str],
+ other: BuildEnvironment) -> None:
for docname in docnames:
env.tocs[docname] = other.tocs[docname]
env.toc_num_entries[docname] = other.toc_num_entries[docname]
@@ -61,14 +59,12 @@ class TocTreeCollector(EnvironmentCollector):
for subfn, fnset in other.files_to_rebuild.items():
env.files_to_rebuild.setdefault(subfn, set()).update(fnset & set(docnames))
- def process_doc(self, app, doctree):
- # type: (Sphinx, nodes.document) -> None
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Build a TOC from the doctree and store it in the inventory."""
docname = app.env.docname
numentries = [0] # nonlocal again...
- def traverse_in_section(node, cls):
- # type: (nodes.Element, Type[N]) -> List[N]
+ def traverse_in_section(node: Element, cls: Type[N]) -> List[N]:
"""Like traverse(), but stay within the same section."""
result = [] # type: List[N]
if isinstance(node, cls):
@@ -80,9 +76,8 @@ class TocTreeCollector(EnvironmentCollector):
result.extend(traverse_in_section(child, cls))
return result
- def build_toc(node, depth=1):
- # type: (nodes.Element, int) -> nodes.bullet_list
- entries = [] # type: List[nodes.Element]
+ def build_toc(node: Element, depth: int = 1) -> nodes.bullet_list:
+ entries = [] # type: List[Element]
for sectionnode in node:
# find all toctree nodes in this section and add them
# to the toc (just copying the toctree node which is then
@@ -107,7 +102,7 @@ class TocTreeCollector(EnvironmentCollector):
'', '', internal=True, refuri=docname,
anchorname=anchorname, *nodetext)
para = addnodes.compact_paragraph('', '', reference)
- item = nodes.list_item('', para) # type: nodes.Element
+ item = nodes.list_item('', para) # type: Element
sub_item = build_toc(sectionnode, depth + 1)
if sub_item:
item += sub_item
@@ -135,12 +130,10 @@ class TocTreeCollector(EnvironmentCollector):
app.env.tocs[docname] = nodes.bullet_list('')
app.env.toc_num_entries[docname] = numentries[0]
- def get_updated_docs(self, app, env):
- # type: (Sphinx, BuildEnvironment) -> List[str]
+ def get_updated_docs(self, app: Sphinx, env: BuildEnvironment) -> List[str]:
return self.assign_section_numbers(env) + self.assign_figure_numbers(env)
- def assign_section_numbers(self, env):
- # type: (BuildEnvironment) -> List[str]
+ def assign_section_numbers(self, env: BuildEnvironment) -> List[str]:
"""Assign a section number to each heading under a numbered toctree."""
# a list of all docnames whose section numbers changed
rewrite_needed = []
@@ -149,8 +142,7 @@ class TocTreeCollector(EnvironmentCollector):
old_secnumbers = env.toc_secnumbers
env.toc_secnumbers = {}
- def _walk_toc(node, secnums, depth, titlenode=None):
- # type: (nodes.Element, Dict, int, nodes.title) -> None
+ def _walk_toc(node: Element, secnums: Dict, depth: int, titlenode: nodes.title = None) -> None: # NOQA
# titlenode is the title of the document, it will get assigned a
# secnumber too, so that it shows up in next/prev/parent rellinks
for subnode in node.children:
@@ -184,8 +176,7 @@ class TocTreeCollector(EnvironmentCollector):
elif isinstance(subnode, addnodes.toctree):
_walk_toctree(subnode, depth)
- def _walk_toctree(toctreenode, depth):
- # type: (addnodes.toctree, int) -> None
+ def _walk_toctree(toctreenode: addnodes.toctree, depth: int) -> None:
if depth == 0:
return
for (title, ref) in toctreenode['entries']:
@@ -216,8 +207,7 @@ class TocTreeCollector(EnvironmentCollector):
return rewrite_needed
- def assign_figure_numbers(self, env):
- # type: (BuildEnvironment) -> List[str]
+ def assign_figure_numbers(self, env: BuildEnvironment) -> List[str]:
"""Assign a figure number to each figure under a numbered toctree."""
rewrite_needed = []
@@ -227,8 +217,7 @@ class TocTreeCollector(EnvironmentCollector):
env.toc_fignumbers = {}
fignum_counter = {} # type: Dict[str, Dict[Tuple[int, ...], int]]
- def get_figtype(node):
- # type: (nodes.Node) -> str
+ def get_figtype(node: Node) -> str:
for domain in env.domains.values():
figtype = domain.get_enumerable_node_type(node)
if figtype:
@@ -236,8 +225,7 @@ class TocTreeCollector(EnvironmentCollector):
return None
- def get_section_number(docname, section):
- # type: (str, nodes.section) -> Tuple[int, ...]
+ def get_section_number(docname: str, section: nodes.section) -> Tuple[int, ...]:
anchorname = '#' + section['ids'][0]
secnumbers = env.toc_secnumbers.get(docname, {})
if anchorname in secnumbers:
@@ -247,24 +235,22 @@ class TocTreeCollector(EnvironmentCollector):
return secnum or tuple()
- def get_next_fignumber(figtype, secnum):
- # type: (str, Tuple[int, ...]) -> Tuple[int, ...]
+ def get_next_fignumber(figtype: str, secnum: Tuple[int, ...]) -> Tuple[int, ...]:
counter = fignum_counter.setdefault(figtype, {})
secnum = secnum[:env.config.numfig_secnum_depth]
counter[secnum] = counter.get(secnum, 0) + 1
return secnum + (counter[secnum],)
- def register_fignumber(docname, secnum, figtype, fignode):
- # type: (str, Tuple[int, ...], str, nodes.Element) -> None
+ def register_fignumber(docname: str, secnum: Tuple[int, ...],
+ figtype: str, fignode: Element) -> None:
env.toc_fignumbers.setdefault(docname, {})
fignumbers = env.toc_fignumbers[docname].setdefault(figtype, {})
figure_id = fignode['ids'][0]
fignumbers[figure_id] = get_next_fignumber(figtype, secnum)
- def _walk_doctree(docname, doctree, secnum):
- # type: (str, nodes.Element, Tuple[int, ...]) -> None
+ def _walk_doctree(docname: str, doctree: Element, secnum: Tuple[int, ...]) -> None:
for subnode in doctree.children:
if isinstance(subnode, nodes.section):
next_secnum = get_section_number(docname, subnode)
@@ -286,8 +272,7 @@ class TocTreeCollector(EnvironmentCollector):
_walk_doctree(docname, subnode, secnum)
- def _walk_doc(docname, secnum):
- # type: (str, Tuple[int, ...]) -> None
+ def _walk_doc(docname: str, secnum: Tuple[int, ...]) -> None:
if docname not in assigned:
assigned.add(docname)
doctree = env.get_doctree(docname)
@@ -302,8 +287,7 @@ class TocTreeCollector(EnvironmentCollector):
return rewrite_needed
-def setup(app):
- # type: (Sphinx) -> Dict
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(TocTreeCollector)
return {
diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py
index d38b749f0..b1d19e5cb 100644
--- a/sphinx/ext/apidoc.py
+++ b/sphinx/ext/apidoc.py
@@ -22,6 +22,7 @@ import sys
import warnings
from fnmatch import fnmatch
from os import path
+from typing import Any, List, Tuple
import sphinx.locale
from sphinx import __display_version__, package_dir
@@ -32,10 +33,6 @@ from sphinx.util import rst
from sphinx.util.osutil import FileAvoidWrite, ensuredir
from sphinx.util.template import ReSTRenderer
-if False:
- # For type annotation
- from typing import Any, List, Tuple # NOQA
-
# automodule options
if 'SPHINX_APIDOC_OPTIONS' in os.environ:
OPTIONS = os.environ['SPHINX_APIDOC_OPTIONS'].split(',')
@@ -53,8 +50,7 @@ PY_SUFFIXES = {'.py', '.pyx'}
template_dir = path.join(package_dir, 'templates', 'apidoc')
-def makename(package, module):
- # type: (str, str) -> str
+def makename(package: str, module: str) -> str:
"""Join package and module with a dot."""
warnings.warn('makename() is deprecated.',
RemovedInSphinx40Warning)
@@ -68,14 +64,12 @@ def makename(package, module):
return name
-def module_join(*modnames):
- # type: (*str) -> str
+def module_join(*modnames: str) -> str:
"""Join module names with dots."""
return '.'.join(filter(None, modnames))
-def write_file(name, text, opts):
- # type: (str, str, Any) -> None
+def write_file(name: str, text: str, opts: Any) -> None:
"""Write the output file for module/package ."""
fname = path.join(opts.destdir, '%s.%s' % (name, opts.suffix))
if opts.dryrun:
@@ -89,8 +83,7 @@ def write_file(name, text, opts):
f.write(text)
-def format_heading(level, text, escape=True):
- # type: (int, str, bool) -> str
+def format_heading(level: int, text: str, escape: bool = True) -> str:
"""Create a heading of [1, 2 or 3 supported]."""
warnings.warn('format_warning() is deprecated.',
RemovedInSphinx40Warning)
@@ -100,8 +93,7 @@ def format_heading(level, text, escape=True):
return '%s\n%s\n\n' % (text, underlining)
-def format_directive(module, package=None):
- # type: (str, str) -> str
+def format_directive(module: str, package: str = None) -> str:
"""Create the automodule directive and add the options."""
warnings.warn('format_directive() is deprecated.',
RemovedInSphinx40Warning)
@@ -111,8 +103,8 @@ def format_directive(module, package=None):
return directive
-def create_module_file(package, basename, opts):
- # type: (str, str, Any) -> None
+def create_module_file(package: str, basename: str, opts: Any,
+ user_template_dir: str = None) -> None:
"""Build the text of the file and write the file."""
qualname = module_join(package, basename)
context = {
@@ -121,12 +113,13 @@ def create_module_file(package, basename, opts):
'qualname': qualname,
'automodule_options': OPTIONS,
}
- text = ReSTRenderer(template_dir).render('module.rst', context)
+ text = ReSTRenderer([user_template_dir, template_dir]).render('module.rst_t', context)
write_file(qualname, text, opts)
-def create_package_file(root, master_package, subroot, py_files, opts, subs, is_namespace, excludes=[]): # NOQA
- # type: (str, str, str, List[str], Any, List[str], bool, List[str]) -> None
+def create_package_file(root: str, master_package: str, subroot: str, py_files: List[str],
+ opts: Any, subs: List[str], is_namespace: bool,
+ excludes: List[str] = [], user_template_dir: str = None) -> None:
"""Build the text of the file and write the file."""
# build a list of sub packages (directories containing an INITPY file)
subpackages = [sub for sub in subs if not
@@ -151,16 +144,16 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
'automodule_options': OPTIONS,
'show_headings': not opts.noheadings,
}
- text = ReSTRenderer(template_dir).render('package.rst', context)
+ text = ReSTRenderer([user_template_dir, template_dir]).render('package.rst_t', context)
write_file(pkgname, text, opts)
if submodules and opts.separatemodules:
for submodule in submodules:
- create_module_file(None, submodule, opts)
+ create_module_file(None, submodule, opts, user_template_dir)
-def create_modules_toc_file(modules, opts, name='modules'):
- # type: (List[str], Any, str) -> None
+def create_modules_toc_file(modules: List[str], opts: Any, name: str = 'modules',
+ user_template_dir: str = None) -> None:
"""Create the module's index."""
modules.sort()
prev_module = ''
@@ -176,12 +169,11 @@ def create_modules_toc_file(modules, opts, name='modules'):
'maxdepth': opts.maxdepth,
'docnames': modules,
}
- text = ReSTRenderer(template_dir).render('toc.rst', context)
+ text = ReSTRenderer([user_template_dir, template_dir]).render('toc.rst_t', context)
write_file(name, text, opts)
-def shall_skip(module, opts, excludes=[]):
- # type: (str, Any, List[str]) -> bool
+def shall_skip(module: str, opts: Any, excludes: List[str] = []) -> bool:
"""Check if we want to skip this module."""
# skip if the file doesn't exist and not using implicit namespaces
if not opts.implicit_namespaces and not path.exists(module):
@@ -207,8 +199,7 @@ def shall_skip(module, opts, excludes=[]):
return False
-def is_skipped_module(filename, opts, excludes):
- # type: (str, Any, List[str]) -> bool
+def is_skipped_module(filename: str, opts: Any, excludes: List[str]) -> bool:
"""Check if we want to skip this module."""
if not path.exists(filename):
# skip if the file doesn't exist
@@ -220,8 +211,8 @@ def is_skipped_module(filename, opts, excludes):
return False
-def recurse_tree(rootpath, excludes, opts):
- # type: (str, List[str], Any) -> List[str]
+def recurse_tree(rootpath: str, excludes: List[str], opts: Any,
+ user_template_dir: str = None) -> List[str]:
"""
Look for every file in the directory tree and create the corresponding
ReST files.
@@ -271,7 +262,8 @@ def recurse_tree(rootpath, excludes, opts):
# a namespace and there is something there to document
if not is_namespace or len(py_files) > 0:
create_package_file(root, root_package, subpackage,
- py_files, opts, subs, is_namespace, excludes)
+ py_files, opts, subs, is_namespace, excludes,
+ user_template_dir)
toplevels.append(module_join(root_package, subpackage))
else:
# if we are at the root level, we don't require it to be a package
@@ -279,14 +271,13 @@ def recurse_tree(rootpath, excludes, opts):
for py_file in py_files:
if not is_skipped_module(path.join(rootpath, py_file), opts, excludes):
module = path.splitext(py_file)[0]
- create_module_file(root_package, module, opts)
+ create_module_file(root_package, module, opts, user_template_dir)
toplevels.append(module)
return toplevels
-def is_excluded(root, excludes):
- # type: (str, List[str]) -> bool
+def is_excluded(root: str, excludes: List[str]) -> bool:
"""Check if the directory is in the exclude list.
Note: by having trailing slashes, we avoid common prefix issues, like
@@ -298,8 +289,7 @@ def is_excluded(root, excludes):
return False
-def get_parser():
- # type: () -> argparse.ArgumentParser
+def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] -o '
'[EXCLUDE_PATTERN, ...]',
@@ -386,11 +376,15 @@ Note: By default this script will not overwrite already created files."""))
const='sphinx.ext.%s' % ext, dest='extensions',
help=__('enable %s extension') % ext)
+ group = parser.add_argument_group(__('Project templating'))
+ group.add_argument('-t', '--templatedir', metavar='TEMPLATEDIR',
+ dest='templatedir',
+ help=__('template directory for template files'))
+
return parser
-def main(argv=sys.argv[1:]):
- # type: (List[str]) -> int
+def main(argv: List[str] = sys.argv[1:]) -> int:
"""Parse and check the command line arguments."""
sphinx.locale.setlocale(locale.LC_ALL, '')
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
@@ -412,7 +406,7 @@ def main(argv=sys.argv[1:]):
if not args.dryrun:
ensuredir(args.destdir)
excludes = [path.abspath(exclude) for exclude in args.exclude_pattern]
- modules = recurse_tree(rootpath, excludes, args)
+ modules = recurse_tree(rootpath, excludes, args, args.templatedir)
if args.full:
from sphinx.cmd import quickstart as qs
@@ -455,9 +449,10 @@ def main(argv=sys.argv[1:]):
d['extensions'].extend(ext.split(','))
if not args.dryrun:
- qs.generate(d, silent=True, overwrite=args.force)
+ qs.generate(d, silent=True, overwrite=args.force,
+ templatedir=args.templatedir)
elif args.tocfile:
- create_modules_toc_file(modules, args, args.tocfile)
+ create_modules_toc_file(modules, args, args.tocfile, args.templatedir)
return 0
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 7172553ac..4273351bf 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -12,15 +12,18 @@
import re
import warnings
-from typing import Any
+from types import ModuleType
+from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
from docutils.statemachine import StringList
import sphinx
-from sphinx.config import ENUM
+from sphinx.application import Sphinx
+from sphinx.config import Config, ENUM
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
+from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import import_object, get_object_members
from sphinx.ext.autodoc.mock import mock
from sphinx.locale import _, __
@@ -35,12 +38,8 @@ from sphinx.util.inspect import (
if False:
# For type annotation
- from types import ModuleType # NOQA
- from typing import Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.config import Config # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
+ from sphinx.ext.autodoc.directive import DocumenterBridge
+
logger = logging.getLogger(__name__)
@@ -61,8 +60,7 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE)
-def identity(x):
- # type: (Any) -> Any
+def identity(x: Any) -> Any:
return x
@@ -71,16 +69,14 @@ INSTANCEATTR = object()
SLOTSATTR = object()
-def members_option(arg):
- # type: (Any) -> Union[object, List[str]]
+def members_option(arg: Any) -> Union[object, List[str]]:
"""Used to convert the :members: option to auto directives."""
if arg is None or arg is True:
return ALL
return [x.strip() for x in arg.split(',')]
-def members_set_option(arg):
- # type: (Any) -> Union[object, Set[str]]
+def members_set_option(arg: Any) -> Union[object, Set[str]]:
"""Used to convert the :members: option to auto directives."""
if arg is None:
return ALL
@@ -90,8 +86,7 @@ def members_set_option(arg):
SUPPRESS = object()
-def annotation_option(arg):
- # type: (Any) -> Any
+def annotation_option(arg: Any) -> Any:
if arg is None:
# suppress showing the representation of the object
return SUPPRESS
@@ -99,16 +94,14 @@ def annotation_option(arg):
return arg
-def bool_option(arg):
- # type: (Any) -> bool
+def bool_option(arg: Any) -> bool:
"""Used to convert flag options to auto directives. (Instead of
directives.flag(), which returns None).
"""
return True
-def merge_special_members_option(options):
- # type: (Dict) -> None
+def merge_special_members_option(options: Dict) -> None:
"""Merge :special-members: option to :members: option."""
if 'special-members' in options and options['special-members'] is not ALL:
if options.get('members') is ALL:
@@ -123,8 +116,7 @@ def merge_special_members_option(options):
# Some useful event listener factories for autodoc-process-docstring.
-def cut_lines(pre, post=0, what=None):
- # type: (int, int, str) -> Callable
+def cut_lines(pre: int, post: int = 0, what: str = None) -> Callable:
"""Return a listener that removes the first *pre* and last *post*
lines of every docstring. If *what* is a sequence of strings,
only docstrings of a type in *what* will be processed.
@@ -136,8 +128,8 @@ def cut_lines(pre, post=0, what=None):
This can (and should) be used in place of :confval:`automodule_skip_lines`.
"""
- def process(app, what_, name, obj, options, lines):
- # type: (Sphinx, str, str, Any, Any, List[str]) -> None
+ def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str]
+ ) -> None:
if what and what_ not in what:
return
del lines[:pre]
@@ -152,8 +144,8 @@ def cut_lines(pre, post=0, what=None):
return process
-def between(marker, what=None, keepempty=False, exclude=False):
- # type: (str, Sequence[str], bool, bool) -> Callable
+def between(marker: str, what: Sequence[str] = None, keepempty: bool = False,
+ exclude: bool = False) -> Callable:
"""Return a listener that either keeps, or if *exclude* is True excludes,
lines between lines that match the *marker* regular expression. If no line
matches, the resulting docstring would be empty, so no change will be made
@@ -164,8 +156,8 @@ def between(marker, what=None, keepempty=False, exclude=False):
"""
marker_re = re.compile(marker)
- def process(app, what_, name, obj, options, lines):
- # type: (Sphinx, str, str, Any, Any, List[str]) -> None
+ def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str]
+ ) -> None:
if what and what_ not in what:
return
deleted = 0
@@ -192,8 +184,7 @@ def between(marker, what=None, keepempty=False, exclude=False):
# But we define this class here to keep compatibility (see #4538)
class Options(dict):
"""A dict/attribute hybrid that returns None on nonexisting keys."""
- def __getattr__(self, name):
- # type: (str) -> Any
+ def __getattr__(self, name: str) -> Any:
try:
return self[name.replace('_', '-')]
except KeyError:
@@ -229,19 +220,17 @@ class Documenter:
option_spec = {'noindex': bool_option} # type: Dict[str, Callable]
- def get_attr(self, obj, name, *defargs):
- # type: (Any, str, Any) -> Any
+ def get_attr(self, obj: Any, name: str, *defargs) -> Any:
"""getattr() override for types such as Zope interfaces."""
return autodoc_attrgetter(self.env.app, obj, name, *defargs)
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
"""Called to see if a member can be documented by this documenter."""
raise NotImplementedError('must be implemented in subclasses')
- def __init__(self, directive, name, indent=''):
- # type: (DocumenterBridge, str, str) -> None
+ def __init__(self, directive: "DocumenterBridge", name: str, indent: str = '') -> None:
self.directive = directive
self.env = directive.env # type: BuildEnvironment
self.options = directive.genopt
@@ -266,18 +255,16 @@ class Documenter:
self.analyzer = None # type: ModuleAnalyzer
@property
- def documenters(self):
- # type: () -> Dict[str, Type[Documenter]]
+ def documenters(self) -> Dict[str, Type["Documenter"]]:
"""Returns registered Documenter classes"""
return get_documenters(self.env.app)
- def add_line(self, line, source, *lineno):
- # type: (str, str, int) -> None
+ def add_line(self, line: str, source: str, *lineno: int) -> None:
"""Append one line of generated reST to the output."""
self.directive.result.append(self.indent + line, source, *lineno)
- def resolve_name(self, modname, parents, path, base):
- # type: (str, Any, str, Any) -> Tuple[str, List[str]]
+ def resolve_name(self, modname: str, parents: Any, path: str, base: Any
+ ) -> Tuple[str, List[str]]:
"""Resolve the module and name of the object to document given by the
arguments and the current module/class.
@@ -287,8 +274,7 @@ class Documenter:
"""
raise NotImplementedError('must be implemented in subclasses')
- def parse_name(self):
- # type: () -> bool
+ def parse_name(self) -> bool:
"""Determine what module to import and what attribute to document.
Returns True and sets *self.modname*, *self.objpath*, *self.fullname*,
@@ -324,8 +310,7 @@ class Documenter:
(self.objpath and '.' + '.'.join(self.objpath) or '')
return True
- def import_object(self):
- # type: () -> bool
+ def import_object(self) -> bool:
"""Import the object given by *self.modname* and *self.objpath* and set
it as *self.object*.
@@ -343,8 +328,7 @@ class Documenter:
self.env.note_reread()
return False
- def get_real_modname(self):
- # type: () -> str
+ def get_real_modname(self) -> str:
"""Get the real module name of an object to document.
It can differ from the name of the module through which the object was
@@ -352,8 +336,7 @@ class Documenter:
"""
return self.get_attr(self.object, '__module__', None) or self.modname
- def check_module(self):
- # type: () -> bool
+ def check_module(self) -> bool:
"""Check if *self.object* is really defined in the module given by
*self.modname*.
"""
@@ -367,16 +350,14 @@ class Documenter:
return False
return True
- def format_args(self, **kwargs):
- # type: (Any) -> str
+ def format_args(self, **kwargs) -> str:
"""Format the argument signature of *self.object*.
Should return None if the object does not have a signature.
"""
return None
- def format_name(self):
- # type: () -> str
+ def format_name(self) -> str:
"""Format the name of *self.object*.
This normally should be something that can be parsed by the generated
@@ -387,8 +368,7 @@ class Documenter:
# directives of course)
return '.'.join(self.objpath) or self.modname
- def format_signature(self, **kwargs):
- # type: (Any) -> str
+ def format_signature(self, **kwargs) -> str:
"""Format the signature (arguments and return annotation) of the object.
Let the user process it via the ``autodoc-process-signature`` event.
@@ -422,8 +402,7 @@ class Documenter:
else:
return ''
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
"""Add the directive header and options to the generated content."""
domain = getattr(self, 'domain', 'py')
directive = getattr(self, 'directivetype', self.objtype)
@@ -438,8 +417,7 @@ class Documenter:
# etc. don't support a prepended module name
self.add_line(' :module: %s' % self.modname, sourcename)
- def get_doc(self, encoding=None, ignore=1):
- # type: (str, int) -> List[List[str]]
+ def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
"""Decode and return lines of the docstring(s) for the object."""
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
@@ -452,8 +430,7 @@ class Documenter:
return [prepare_docstring(docstring, ignore, tab_width)]
return []
- def process_doc(self, docstrings):
- # type: (List[List[str]]) -> Iterator[str]
+ def process_doc(self, docstrings: List[List[str]]) -> Iterator[str]:
"""Let the user process the docstrings before adding them."""
for docstringlines in docstrings:
if self.env.app:
@@ -463,14 +440,12 @@ class Documenter:
self.options, docstringlines)
yield from docstringlines
- def get_sourcename(self):
- # type: () -> str
+ def get_sourcename(self) -> str:
if self.analyzer:
return '%s:docstring of %s' % (self.analyzer.srcname, self.fullname)
return 'docstring of %s' % self.fullname
- def add_content(self, more_content, no_docstring=False):
- # type: (Any, bool) -> None
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
"""Add content from docstrings, attribute documentation and user."""
# set sourcename and add content from attribute documentation
sourcename = self.get_sourcename()
@@ -500,8 +475,7 @@ class Documenter:
for line, src in zip(more_content.data, more_content.items):
self.add_line(line, src[0], src[1])
- def get_object_members(self, want_all):
- # type: (bool) -> Tuple[bool, List[Tuple[str, Any]]]
+ def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]:
"""Return `(members_check_module, members)` where `members` is a
list of `(membername, member)` pairs of the members of *self.object*.
@@ -527,8 +501,8 @@ class Documenter:
return False, sorted((m.name, m.value) for m in members.values()
if m.directly_defined)
- def filter_members(self, members, want_all):
- # type: (List[Tuple[str, Any]], bool) -> List[Tuple[str, Any, bool]]
+ def filter_members(self, members: List[Tuple[str, Any]], want_all: bool
+ ) -> List[Tuple[str, Any, bool]]:
"""Filter the given member list.
Members are skipped if
@@ -616,8 +590,7 @@ class Documenter:
return ret
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
"""Generate reST for member documentation.
If *all_members* is True, do all members, else those given by
@@ -669,8 +642,7 @@ class Documenter:
# sort by source order, by virtue of the module analyzer
tagorder = self.analyzer.tagorder
- def keyfunc(entry):
- # type: (Tuple[Documenter, bool]) -> int
+ def keyfunc(entry: Tuple[Documenter, bool]) -> int:
fullname = entry[0].name.split('::')[1]
return tagorder.get(fullname, len(tagorder))
memberdocumenters.sort(key=keyfunc)
@@ -684,9 +656,8 @@ class Documenter:
self.env.temp_data['autodoc:module'] = None
self.env.temp_data['autodoc:class'] = None
- def generate(self, more_content=None, real_modname=None,
- check_module=False, all_members=False):
- # type: (Any, str, bool, bool) -> None
+ def generate(self, more_content: Any = None, real_modname: str = None,
+ check_module: bool = False, all_members: bool = False) -> None:
"""Generate reST for the object given by *self.name*, and possibly for
its members.
@@ -778,26 +749,24 @@ class ModuleDocumenter(Documenter):
'imported-members': bool_option, 'ignore-module-all': bool_option
} # type: Dict[str, Callable]
- def __init__(self, *args):
- # type: (Any) -> None
+ def __init__(self, *args) -> None:
super().__init__(*args)
merge_special_members_option(self.options)
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
# don't document submodules automatically
return False
- def resolve_name(self, modname, parents, path, base):
- # type: (str, Any, str, Any) -> Tuple[str, List[str]]
+ def resolve_name(self, modname: str, parents: Any, path: str, base: Any
+ ) -> Tuple[str, List[str]]:
if modname is not None:
logger.warning(__('"::" in automodule name doesn\'t make sense'),
type='autodoc')
return (path or '') + base, []
- def parse_name(self):
- # type: () -> bool
+ def parse_name(self) -> bool:
ret = super().parse_name()
if self.args or self.retann:
logger.warning(__('signature arguments or return annotation '
@@ -805,8 +774,7 @@ class ModuleDocumenter(Documenter):
type='autodoc')
return ret
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
Documenter.add_directive_header(self, sig)
sourcename = self.get_sourcename()
@@ -819,8 +787,7 @@ class ModuleDocumenter(Documenter):
if self.options.deprecated:
self.add_line(' :deprecated:', sourcename)
- def get_object_members(self, want_all):
- # type: (bool) -> Tuple[bool, List[Tuple[str, object]]]
+ def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, object]]]:
if want_all:
if (self.options.ignore_module_all or not
hasattr(self.object, '__all__')):
@@ -861,8 +828,8 @@ class ModuleLevelDocumenter(Documenter):
Specialized Documenter subclass for objects on module level (functions,
classes, data/constants).
"""
- def resolve_name(self, modname, parents, path, base):
- # type: (str, Any, str, Any) -> Tuple[str, List[str]]
+ def resolve_name(self, modname: str, parents: Any, path: str, base: Any
+ ) -> Tuple[str, List[str]]:
if modname is None:
if path:
modname = path.rstrip('.')
@@ -882,8 +849,8 @@ class ClassLevelDocumenter(Documenter):
Specialized Documenter subclass for objects on class level (methods,
attributes).
"""
- def resolve_name(self, modname, parents, path, base):
- # type: (str, Any, str, Any) -> Tuple[str, List[str]]
+ def resolve_name(self, modname: str, parents: Any, path: str, base: Any
+ ) -> Tuple[str, List[str]]:
if modname is None:
if path:
mod_cls = path.rstrip('.')
@@ -916,8 +883,7 @@ class DocstringSignatureMixin:
feature of reading the signature from the docstring.
"""
- def _find_signature(self, encoding=None):
- # type: (str) -> Tuple[str, str]
+ def _find_signature(self, encoding: str = None) -> Tuple[str, str]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is "
"deprecated." % self.__class__.__name__,
@@ -951,8 +917,7 @@ class DocstringSignatureMixin:
break
return result
- def get_doc(self, encoding=None, ignore=1):
- # type: (str, int) -> List[List[str]]
+ def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
@@ -962,8 +927,7 @@ class DocstringSignatureMixin:
return lines
return super().get_doc(None, ignore) # type: ignore
- def format_signature(self, **kwargs):
- # type: (Any) -> str
+ def format_signature(self, **kwargs) -> str:
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
# only act if a signature is not explicitly given already, and if
# the feature is enabled
@@ -978,8 +942,7 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin):
Mixin for AttributeDocumenter to provide the
feature of stripping any function signature from the docstring.
"""
- def format_signature(self, **kwargs):
- # type: (Any) -> str
+ def format_signature(self, **kwargs) -> str:
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
# only act if a signature is not explicitly given already, and if
# the feature is enabled
@@ -1000,14 +963,13 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
member_order = 30
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
# supports functions, builtins and bound methods exported at the module level
return (inspect.isfunction(member) or inspect.isbuiltin(member) or
(inspect.isroutine(member) and isinstance(parent, ModuleDocumenter)))
- def format_args(self, **kwargs):
- # type: (Any) -> str
+ def format_args(self, **kwargs) -> str:
if self.env.config.autodoc_typehints == 'none':
kwargs.setdefault('show_annotation', False)
@@ -1042,12 +1004,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
args = args.replace('\\', '\\\\')
return args
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
pass
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
super().add_directive_header(sig)
@@ -1086,18 +1046,16 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
'private-members': bool_option, 'special-members': members_option,
} # type: Dict[str, Callable]
- def __init__(self, *args):
- # type: (Any) -> None
+ def __init__(self, *args) -> None:
super().__init__(*args)
merge_special_members_option(self.options)
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
return isinstance(member, type)
- def import_object(self):
- # type: () -> Any
+ def import_object(self) -> Any:
ret = super().import_object()
# if the class is documented under another name, document it
# as data/attribute
@@ -1108,8 +1066,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
self.doc_as_attr = True
return ret
- def format_args(self, **kwargs):
- # type: (Any) -> str
+ def format_args(self, **kwargs) -> str:
if self.env.config.autodoc_typehints == 'none':
kwargs.setdefault('show_annotation', False)
@@ -1129,15 +1086,13 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
# with __init__ in C
return None
- def format_signature(self, **kwargs):
- # type: (Any) -> str
+ def format_signature(self, **kwargs) -> str:
if self.doc_as_attr:
return ''
return super().format_signature(**kwargs)
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
if self.doc_as_attr:
self.directivetype = 'attribute'
super().add_directive_header(sig)
@@ -1154,8 +1109,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
sourcename)
- def get_doc(self, encoding=None, ignore=1):
- # type: (str, int) -> List[List[str]]
+ def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
@@ -1199,8 +1153,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
tab_width = self.directive.state.document.settings.tab_width
return [prepare_docstring(docstring, ignore, tab_width) for docstring in docstrings]
- def add_content(self, more_content, no_docstring=False):
- # type: (Any, bool) -> None
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
if self.doc_as_attr:
classname = safe_getattr(self.object, '__qualname__', None)
if not classname:
@@ -1215,15 +1168,13 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
else:
super().add_content(more_content)
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
if self.doc_as_attr:
return
super().document_members(all_members)
- def generate(self, more_content=None, real_modname=None,
- check_module=False, all_members=False):
- # type: (Any, str, bool, bool) -> None
+ def generate(self, more_content: Any = None, real_modname: str = None,
+ check_module: bool = False, all_members: bool = False) -> None:
# Do not pass real_modname and use the name from the __module__
# attribute of the class.
# If a class gets imported into the module real_modname
@@ -1245,8 +1196,8 @@ class ExceptionDocumenter(ClassDocumenter):
priority = 10
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
return isinstance(member, type) and issubclass(member, BaseException)
@@ -1261,12 +1212,11 @@ class DataDocumenter(ModuleLevelDocumenter):
option_spec["annotation"] = annotation_option
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
return isinstance(parent, ModuleDocumenter) and isattr
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
if not self.options.annotation:
@@ -1282,12 +1232,10 @@ class DataDocumenter(ModuleLevelDocumenter):
self.add_line(' :annotation: %s' % self.options.annotation,
sourcename)
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
pass
- def get_real_modname(self):
- # type: () -> str
+ def get_real_modname(self) -> str:
return self.get_attr(self.parent or self.object, '__module__', None) \
or self.modname
@@ -1302,13 +1250,12 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
priority = 1 # must be more than FunctionDocumenter
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
return inspect.isroutine(member) and \
not isinstance(parent, ModuleDocumenter)
- def import_object(self):
- # type: () -> Any
+ def import_object(self) -> Any:
ret = super().import_object()
if not ret:
return ret
@@ -1325,8 +1272,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
return ret
- def format_args(self, **kwargs):
- # type: (Any) -> str
+ def format_args(self, **kwargs) -> str:
if self.env.config.autodoc_typehints == 'none':
kwargs.setdefault('show_annotation', False)
@@ -1341,8 +1287,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
args = args.replace('\\', '\\\\')
return args
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig) -> None:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
@@ -1356,8 +1301,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
self.add_line(' :staticmethod:', sourcename)
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
pass
@@ -1375,13 +1319,12 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
priority = 10
@staticmethod
- def is_function_or_method(obj):
- # type: (Any) -> bool
+ def is_function_or_method(obj: Any) -> bool:
return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj)
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
if inspect.isattributedescriptor(member):
return True
elif (not isinstance(parent, ModuleDocumenter) and
@@ -1391,12 +1334,10 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
else:
return False
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
pass
- def import_object(self):
- # type: () -> Any
+ def import_object(self) -> Any:
ret = super().import_object()
if inspect.isenumattribute(self.object):
self.object = self.object.value
@@ -1407,13 +1348,11 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
self._datadescriptor = False
return ret
- def get_real_modname(self):
- # type: () -> str
+ def get_real_modname(self) -> str:
return self.get_attr(self.parent or self.object, '__module__', None) \
or self.modname
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
if not self.options.annotation:
@@ -1429,8 +1368,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
else:
self.add_line(' :annotation: %s' % self.options.annotation, sourcename)
- def add_content(self, more_content, no_docstring=False):
- # type: (Any, bool) -> None
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
if not self._datadescriptor:
# if it's not a data descriptor, its docstring is very probably the
# wrong thing to display
@@ -1450,21 +1388,18 @@ class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): #
priority = AttributeDocumenter.priority + 1
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
return inspect.isproperty(member) and isinstance(parent, ClassDocumenter)
- def document_members(self, all_members=False):
- # type: (bool) -> None
+ def document_members(self, all_members: bool = False) -> None:
pass
- def get_real_modname(self):
- # type: () -> str
+ def get_real_modname(self) -> str:
return self.get_attr(self.parent or self.object, '__module__', None) \
or self.modname
- def add_directive_header(self, sig):
- # type: (str) -> None
+ def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
if inspect.isabstractmethod(self.object):
@@ -1485,21 +1420,19 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
priority = 11
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
"""This documents only INSTANCEATTR members."""
return isattr and (member is INSTANCEATTR)
- def import_object(self):
- # type: () -> bool
+ def import_object(self) -> bool:
"""Never import anything."""
# disguise as an attribute
self.objtype = 'attribute'
self._datadescriptor = False
return True
- def add_content(self, more_content, no_docstring=False):
- # type: (Any, bool) -> None
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
"""Never try to get a docstring from the object."""
super().add_content(more_content, no_docstring=True)
@@ -1517,13 +1450,12 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
priority = 11
@classmethod
- def can_document_member(cls, member, membername, isattr, parent):
- # type: (Any, str, bool, Any) -> bool
+ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
"""This documents only SLOTSATTR members."""
return member is SLOTSATTR
- def import_object(self):
- # type: () -> bool
+ def import_object(self) -> Any:
"""Never import anything."""
# disguise as an attribute
self.objtype = 'attribute'
@@ -1541,8 +1473,7 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
self.env.note_reread()
return False
- def get_doc(self, encoding=None, ignore=1):
- # type: (str, int) -> List[List[str]]
+ def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
"""Decode and return lines of the docstring(s) for the object."""
name = self.objpath[-1]
__slots__ = safe_getattr(self.parent, '__slots__', [])
@@ -1553,14 +1484,12 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
return []
-def get_documenters(app):
- # type: (Sphinx) -> Dict[str, Type[Documenter]]
+def get_documenters(app: Sphinx) -> Dict[str, Type[Documenter]]:
"""Returns registered Documenter classes"""
return app.registry.documenters
-def autodoc_attrgetter(app, obj, name, *defargs):
- # type: (Sphinx, Any, str, Any) -> Any
+def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs) -> Any:
"""Alternative getattr() for types"""
for typ, func in app.registry.autodoc_attrgettrs.items():
if isinstance(obj, typ):
@@ -1569,8 +1498,7 @@ def autodoc_attrgetter(app, obj, name, *defargs):
return safe_getattr(obj, name, *defargs)
-def merge_autodoc_default_flags(app, config):
- # type: (Sphinx, Config) -> None
+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
@@ -1602,8 +1530,7 @@ deprecated_alias('sphinx.ext.autodoc',
RemovedInSphinx40Warning)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ModuleDocumenter)
app.add_autodocumenter(ClassDocumenter)
app.add_autodocumenter(ExceptionDocumenter)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 6b002b101..953e3c44c 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -7,27 +7,22 @@
"""
import warnings
+from typing import Any, Callable, Dict, List, Set, Type
from docutils import nodes
-from docutils.parsers.rst.states import Struct
+from docutils.nodes import Element, Node
+from docutils.parsers.rst.states import RSTState, Struct
from docutils.statemachine import StringList
-from docutils.utils import assemble_option_dict
+from docutils.utils import Reporter, assemble_option_dict
+from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
-from sphinx.ext.autodoc import Options, get_documenters
+from sphinx.environment import BuildEnvironment
+from sphinx.ext.autodoc import Documenter, Options, get_documenters
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
-if False:
- # For type annotation
- from typing import Any, Callable, Dict, List, Set, Type # NOQA
- from docutils.parsers.rst.state import RSTState # NOQA
- from docutils.utils import Reporter # NOQA
- from sphinx.config import Config # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.ext.autodoc import Documenter # NOQA
-
logger = logging.getLogger(__name__)
@@ -41,21 +36,19 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
class DummyOptionSpec(dict):
"""An option_spec allows any options."""
- def __bool__(self):
- # type: () -> bool
+ def __bool__(self) -> bool:
"""Behaves like some options are defined."""
return True
- def __getitem__(self, key):
- # type: (str) -> Callable[[str], str]
+ def __getitem__(self, key: str) -> Callable[[str], str]:
return lambda x: x
class DocumenterBridge:
"""A parameters container for Documenters."""
- def __init__(self, env, reporter, options, lineno, state=None):
- # type: (BuildEnvironment, Reporter, Options, int, Any) -> None
+ def __init__(self, env: BuildEnvironment, reporter: Reporter, options: Options,
+ lineno: int, state: Any = None) -> None:
self.env = env
self.reporter = reporter
self.genopt = options
@@ -73,13 +66,12 @@ class DocumenterBridge:
document = Struct(settings=settings)
self.state = Struct(document=document)
- def warn(self, msg):
- # type: (str) -> None
+ def warn(self, msg: str) -> None:
logger.warning(msg, location=(self.env.docname, self.lineno))
-def process_documenter_options(documenter, config, options):
- # type: (Type[Documenter], Config, Dict) -> Options
+def process_documenter_options(documenter: Type[Documenter], config: Config, options: Dict
+ ) -> Options:
"""Recognize options of Documenter from user input."""
for name in AUTODOC_DEFAULT_OPTIONS:
if name not in documenter.option_spec:
@@ -92,12 +84,12 @@ def process_documenter_options(documenter, config, options):
return Options(assemble_option_dict(options.items(), documenter.option_spec))
-def parse_generated_content(state, content, documenter):
- # type: (RSTState, StringList, Documenter) -> List[nodes.Node]
+def parse_generated_content(state: RSTState, content: StringList, documenter: Documenter
+ ) -> List[Node]:
"""Parse a generated content by Documenter."""
with switch_source_input(state, content):
if documenter.titles_allowed:
- node = nodes.section() # type: nodes.Element
+ node = nodes.section() # type: Element
# necessary so that the child nodes get the right source/line set
node.document = state.document
nested_parse_with_titles(state, content, node)
@@ -121,8 +113,7 @@ class AutodocDirective(SphinxDirective):
optional_arguments = 0
final_argument_whitespace = True
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
reporter = self.state.document.reporter
try:
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 0c6f47039..fc934958d 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -12,20 +12,16 @@ import sys
import traceback
import warnings
from collections import namedtuple
+from typing import Any, Callable, Dict, List
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
-if False:
- # For type annotation
- from typing import Any, Callable, Dict, List # NOQA
-
logger = logging.getLogger(__name__)
-def import_module(modname, warningiserror=False):
- # type: (str, bool) -> Any
+def import_module(modname: str, warningiserror: bool = False) -> Any:
"""
Call __import__(modname), convert exceptions to ImportError
"""
@@ -41,8 +37,9 @@ def import_module(modname, warningiserror=False):
raise ImportError(exc, traceback.format_exc())
-def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
- # type: (str, List[str], str, Callable[[Any, str], Any], bool) -> Any
+def import_object(modname: str, objpath: List[str], objtype: str = '',
+ attrgetter: Callable[[Any, str], Any] = safe_getattr,
+ warningiserror: bool = False) -> Any:
if objpath:
logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
else:
@@ -108,8 +105,8 @@ def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warning
Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
-def get_object_members(subject, objpath, attrgetter, analyzer=None):
- # type: (Any, List[str], Callable, Any) -> Dict[str, Attribute] # NOQA
+def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
+ analyzer: Any = None) -> Dict[str, Attribute]:
"""Get members and attributes of target object."""
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py
index 6ae389258..4f534a452 100644
--- a/sphinx/ext/autodoc/mock.py
+++ b/sphinx/ext/autodoc/mock.py
@@ -15,14 +15,11 @@ 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
-if False:
- # For type annotation
- from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union # NOQA
-
logger = logging.getLogger(__name__)
@@ -31,8 +28,7 @@ class _MockObject:
__display_name__ = '_MockObject'
- def __new__(cls, *args, **kwargs):
- # type: (Any, Any) -> Any
+ def __new__(cls, *args, **kwargs) -> Any:
if len(args) == 3 and isinstance(args[1], tuple):
superclass = args[1][-1].__class__
if superclass is cls:
@@ -42,48 +38,39 @@ class _MockObject:
return super().__new__(cls)
- def __init__(self, *args, **kwargs):
- # type: (Any, Any) -> None
+ def __init__(self, *args, **kwargs) -> None:
self.__qualname__ = ''
- def __len__(self):
- # type: () -> int
+ def __len__(self) -> int:
return 0
- def __contains__(self, key):
- # type: (str) -> bool
+ def __contains__(self, key: str) -> bool:
return False
- def __iter__(self):
- # type: () -> Iterator
+ def __iter__(self) -> Iterator:
return iter([])
- def __mro_entries__(self, bases):
- # type: (Tuple) -> Tuple
+ def __mro_entries__(self, bases: Tuple) -> Tuple:
return (self.__class__,)
- def __getitem__(self, key):
- # type: (str) -> _MockObject
+ def __getitem__(self, key: str) -> "_MockObject":
return _make_subclass(key, self.__display_name__, self.__class__)()
- def __getattr__(self, key):
- # type: (str) -> _MockObject
+ def __getattr__(self, key: str) -> "_MockObject":
return _make_subclass(key, self.__display_name__, self.__class__)()
- def __call__(self, *args, **kw):
- # type: (Any, Any) -> Any
+ def __call__(self, *args, **kw) -> Any:
if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
return self
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return self.__display_name__
-def _make_subclass(name, module, superclass=_MockObject, attributes=None):
- # type: (str, str, Any, dict) -> Any
+def _make_subclass(name: str, module: str, superclass: Any = _MockObject,
+ attributes: Any = None) -> Any:
attrs = {'__module__': module, '__display_name__': module + '.' + name}
attrs.update(attributes or {})
@@ -94,8 +81,7 @@ class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = os.devnull
- def __init__(self, name, loader=None):
- # type: (str, _MockImporter) -> None
+ def __init__(self, name: str, loader: "_MockImporter" = None) -> None:
super().__init__(name)
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
@@ -104,18 +90,15 @@ class _MockModule(ModuleType):
warnings.warn('The loader argument for _MockModule is deprecated.',
RemovedInSphinx30Warning)
- def __getattr__(self, name):
- # type: (str) -> _MockObject
+ def __getattr__(self, name: str) -> _MockObject:
return _make_subclass(name, self.__name__)()
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return self.__name__
class _MockImporter(MetaPathFinder):
- def __init__(self, names):
- # type: (List[str]) -> None
+ def __init__(self, names: List[str]) -> None:
self.names = names
self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path
@@ -124,8 +107,7 @@ class _MockImporter(MetaPathFinder):
warnings.warn('_MockImporter is now deprecated.',
RemovedInSphinx30Warning)
- def disable(self):
- # type: () -> None
+ 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
@@ -134,16 +116,14 @@ class _MockImporter(MetaPathFinder):
if m in sys.modules:
del sys.modules[m]
- def find_module(self, name, path=None):
- # type: (str, Sequence[Union[bytes, str]]) -> Any
+ 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):
- # type: (str) -> ModuleType
+ def load_module(self, name: str) -> ModuleType:
if name in sys.modules:
# module has already been imported, return it
return sys.modules[name]
@@ -157,34 +137,30 @@ class _MockImporter(MetaPathFinder):
class MockLoader(Loader):
"""A loader for mocking."""
- def __init__(self, finder):
- # type: (MockFinder) -> None
+ def __init__(self, finder: "MockFinder") -> None:
super().__init__()
self.finder = finder
- def create_module(self, spec):
- # type: (ModuleSpec) -> ModuleType
+ def create_module(self, spec: ModuleSpec) -> ModuleType:
logger.debug('[autodoc] adding a mock module as %s!', spec.name)
self.finder.mocked_modules.append(spec.name)
return _MockModule(spec.name)
- def exec_module(self, module):
- # type: (ModuleType) -> None
+ def exec_module(self, module: ModuleType) -> None:
pass # nothing to do
class MockFinder(MetaPathFinder):
"""A finder for mocking."""
- def __init__(self, modnames):
- # type: (List[str]) -> None
+ def __init__(self, modnames: List[str]) -> None:
super().__init__()
self.modnames = modnames
self.loader = MockLoader(self)
self.mocked_modules = [] # type: List[str]
- def find_spec(self, fullname, path, target=None):
- # type: (str, Sequence[Union[bytes, str]], ModuleType) -> ModuleSpec
+ def find_spec(self, fullname: str, path: Sequence[Union[bytes, str]],
+ target: ModuleType = None) -> ModuleSpec:
for modname in self.modnames:
# check if fullname is (or is a descendant of) one of our targets
if modname == fullname or fullname.startswith(modname + '.'):
@@ -192,16 +168,14 @@ class MockFinder(MetaPathFinder):
return None
- def invalidate_caches(self):
- # type: () -> None
+ def invalidate_caches(self) -> None:
"""Invalidate mocked modules on sys.modules."""
for modname in self.mocked_modules:
sys.modules.pop(modname, None)
@contextlib.contextmanager
-def mock(modnames):
- # type: (List[str]) -> Generator[None, None, None]
+def mock(modnames: List[str]) -> Generator[None, None, None]:
"""Insert mock modules during context::
with mock(['target.module.name']):
diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py
index 3d55ad749..9173fecf0 100644
--- a/sphinx/ext/autosectionlabel.py
+++ b/sphinx/ext/autosectionlabel.py
@@ -8,24 +8,22 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict
from typing import cast
from docutils import nodes
+from docutils.nodes import Node
+from sphinx.application import Sphinx
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.nodes import clean_astext
-if False:
- # For type annotation
- from typing import Any, Dict # NOQA
- from sphinx.application import Sphinx # NOQA
-
logger = logging.getLogger(__name__)
-def get_node_depth(node):
+def get_node_depth(node: Node) -> int:
i = 0
cur_node = node
while cur_node.parent != node.document:
@@ -34,8 +32,7 @@ def get_node_depth(node):
return i
-def register_sections_as_label(app, document):
- # type: (Sphinx, nodes.Node) -> None
+def register_sections_as_label(app: Sphinx, document: Node) -> None:
labels = app.env.domaindata['std']['labels']
anonlabels = app.env.domaindata['std']['anonlabels']
for node in document.traverse(nodes.section):
@@ -61,8 +58,7 @@ def register_sections_as_label(app, document):
labels[name] = docname, labelid, sectname
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autosectionlabel_prefix_document', False, 'env')
app.add_config_value('autosectionlabel_maxdepth', None, 'env')
app.connect('doctree-read', register_sections_as_label)
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 6eb0fea9b..7c92bb8b5 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -58,19 +58,24 @@ import posixpath
import re
import sys
import warnings
+from os import path
from types import ModuleType
-from typing import List, cast
+from typing import Any, Dict, List, Tuple, Type
+from typing import cast
from docutils import nodes
+from docutils.nodes import Element, Node, system_message
from docutils.parsers.rst import directives
-from docutils.parsers.rst.states import RSTStateMachine, Struct, state_classes
+from docutils.parsers.rst.states import Inliner, RSTStateMachine, Struct, state_classes
from docutils.statemachine import StringList
import sphinx
from sphinx import addnodes
+from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.environment import BuildEnvironment
from sphinx.environment.adapters.toctree import TocTree
-from sphinx.ext.autodoc import get_documenters
+from sphinx.ext.autodoc import Documenter, get_documenters
from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
from sphinx.ext.autodoc.mock import mock
@@ -81,15 +86,8 @@ from sphinx.util.docutils import (
NullReporter, SphinxDirective, SphinxRole, new_document, switch_source_input
)
from sphinx.util.matching import Matcher
+from sphinx.writers.html import HTMLTranslator
-if False:
- # For type annotation
- from typing import Any, Dict, Tuple, Type # NOQA
- from docutils.parsers.rst.states import Inliner # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.ext.autodoc import Documenter # NOQA
- from sphinx.writers.html import HTMLTranslator # NOQA
logger = logging.getLogger(__name__)
@@ -104,16 +102,14 @@ class autosummary_toc(nodes.comment):
pass
-def process_autosummary_toc(app, doctree):
- # type: (Sphinx, nodes.document) -> None
+def process_autosummary_toc(app: Sphinx, doctree: nodes.document) -> None:
"""Insert items described in autosummary:: to the TOC tree, but do
not generate the toctree:: list.
"""
env = app.builder.env
crawled = {}
- def crawl_toc(node, depth=1):
- # type: (nodes.Element, int) -> None
+ def crawl_toc(node: Element, depth: int = 1) -> None:
crawled[node] = True
for j, subnode in enumerate(node):
try:
@@ -130,14 +126,12 @@ def process_autosummary_toc(app, doctree):
crawl_toc(doctree)
-def autosummary_toc_visit_html(self, node):
- # type: (nodes.NodeVisitor, autosummary_toc) -> None
+def autosummary_toc_visit_html(self: nodes.NodeVisitor, node: autosummary_toc) -> None:
"""Hide autosummary toctree list in HTML output."""
raise nodes.SkipNode
-def autosummary_noop(self, node):
- # type: (nodes.NodeVisitor, nodes.Node) -> None
+def autosummary_noop(self: nodes.NodeVisitor, node: Node) -> None:
pass
@@ -147,8 +141,7 @@ class autosummary_table(nodes.comment):
pass
-def autosummary_table_visit_html(self, node):
- # type: (HTMLTranslator, autosummary_table) -> None
+def autosummary_table_visit_html(self: HTMLTranslator, node: autosummary_table) -> None:
"""Make the first column of the table non-breaking."""
try:
table = cast(nodes.table, node[0])
@@ -173,16 +166,14 @@ _app = None # type: Sphinx
class FakeDirective(DocumenterBridge):
- def __init__(self):
- # type: () -> None
+ def __init__(self) -> None:
settings = Struct(tab_width=8)
document = Struct(settings=settings)
state = Struct(document=document)
super().__init__({}, None, Options(), 0, state) # type: ignore
-def get_documenter(app, obj, parent):
- # type: (Sphinx, Any, Any) -> Type[Documenter]
+def get_documenter(app: Sphinx, obj: Any, parent: Any) -> Type[Documenter]:
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@@ -236,8 +227,7 @@ class Autosummary(SphinxDirective):
'template': directives.unchanged,
}
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
self.bridge = DocumenterBridge(self.env, self.state.document.reporter,
Options(), self.lineno, self.state)
@@ -256,24 +246,30 @@ class Autosummary(SphinxDirective):
docname = posixpath.join(tree_prefix, real_name)
docname = posixpath.normpath(posixpath.join(dirname, docname))
if docname not in self.env.found_docs:
+ location = self.state_machine.get_source_and_line(self.lineno)
if excluded(self.env.doc2path(docname, None)):
- logger.warning(__('toctree references excluded document %r'), docname)
+ msg = __('autosummary references excluded document %r. Ignored.')
else:
- logger.warning(__('toctree references unknown document %r'), docname)
+ msg = __('autosummary: stub file not found %r. '
+ 'Check your autosummary_generate setting.')
+
+ logger.warning(msg, real_name, location=location)
+ continue
+
docnames.append(docname)
- tocnode = addnodes.toctree()
- tocnode['includefiles'] = docnames
- tocnode['entries'] = [(None, docn) for docn in docnames]
- tocnode['maxdepth'] = -1
- tocnode['glob'] = None
+ if docnames:
+ tocnode = addnodes.toctree()
+ tocnode['includefiles'] = docnames
+ tocnode['entries'] = [(None, docn) for docn in docnames]
+ tocnode['maxdepth'] = -1
+ tocnode['glob'] = None
- nodes.append(autosummary_toc('', '', tocnode))
+ nodes.append(autosummary_toc('', '', tocnode))
return nodes
- def get_items(self, names):
- # type: (List[str]) -> List[Tuple[str, str, str, str]]
+ def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]:
"""Try to import the given names, and return a list of
``[(name, signature, summary_string, real_name), ...]``.
"""
@@ -353,8 +349,7 @@ class Autosummary(SphinxDirective):
return items
- def get_table(self, items):
- # type: (List[Tuple[str, str, str, str]]) -> List[nodes.Node]
+ def get_table(self, items: List[Tuple[str, str, str, str]]) -> List[Node]:
"""Generate a proper list of table nodes for autosummary:: directive.
*items* is a list produced by :meth:`get_items`.
@@ -372,8 +367,7 @@ class Autosummary(SphinxDirective):
body = nodes.tbody('')
group.append(body)
- def append_row(*column_texts):
- # type: (str) -> None
+ def append_row(*column_texts: str) -> None:
row = nodes.row('')
source, line = self.state_machine.get_source_and_line()
for text in column_texts:
@@ -401,42 +395,36 @@ class Autosummary(SphinxDirective):
return [table_spec, table]
- def warn(self, msg):
- # type: (str) -> None
+ def warn(self, msg: str) -> None:
warnings.warn('Autosummary.warn() is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
logger.warning(msg)
@property
- def genopt(self):
- # type: () -> Options
+ def genopt(self) -> Options:
warnings.warn('Autosummary.genopt is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self.bridge.genopt
@property
- def warnings(self):
- # type: () -> List[nodes.Node]
+ def warnings(self) -> List[Node]:
warnings.warn('Autosummary.warnings is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return []
@property
- def result(self):
- # type: () -> StringList
+ def result(self) -> StringList:
warnings.warn('Autosummary.result is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self.bridge.result
-def strip_arg_typehint(s):
- # type: (str) -> str
+def strip_arg_typehint(s: str) -> str:
"""Strip a type hint from argument definition."""
return s.split(':')[0].strip()
-def mangle_signature(sig, max_chars=30):
- # type: (str, int) -> str
+def mangle_signature(sig: str, max_chars: int = 30) -> str:
"""Reformat a function signature to a more compact form."""
# Strip return type annotation
s = re.sub(r"\)\s*->\s.*$", ")", sig)
@@ -493,8 +481,7 @@ def mangle_signature(sig, max_chars=30):
return "(%s)" % sig
-def extract_summary(doc, document):
- # type: (List[str], Any) -> str
+def extract_summary(doc: List[str], document: Any) -> str:
"""Extract summary from docstring."""
# Skip a blank lines at the top
@@ -542,8 +529,8 @@ def extract_summary(doc, document):
return summary
-def limited_join(sep, items, max_chars=30, overflow_marker="..."):
- # type: (str, List[str], int, str) -> str
+def limited_join(sep: str, items: List[str], max_chars: int = 30,
+ overflow_marker: str = "...") -> str:
"""Join a number of strings to one, limiting the length to *max_chars*.
If the string overflows this limit, replace the last fitting item by
@@ -569,8 +556,7 @@ def limited_join(sep, items, max_chars=30, overflow_marker="..."):
# -- Importing items -----------------------------------------------------------
-def get_import_prefixes_from_env(env):
- # type: (BuildEnvironment) -> List[str]
+def get_import_prefixes_from_env(env: BuildEnvironment) -> List[str]:
"""
Obtain current Python import prefixes (for `import_by_name`)
from ``document.env``
@@ -591,8 +577,7 @@ def get_import_prefixes_from_env(env):
return prefixes
-def import_by_name(name, prefixes=[None]):
- # type: (str, List[str]) -> Tuple[str, Any, Any, str]
+def import_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, Any, Any, str]:
"""Import a Python object that has the given *name*, under one of the
*prefixes*. The first name that succeeds is used.
"""
@@ -610,8 +595,7 @@ def import_by_name(name, prefixes=[None]):
raise ImportError('no module named %s' % ' or '.join(tried))
-def _import_by_name(name):
- # type: (str) -> Tuple[Any, Any, str]
+def _import_by_name(name: str) -> Tuple[Any, Any, str]:
"""Import a Python object given its full name."""
try:
name_parts = name.split('.')
@@ -654,8 +638,9 @@ def _import_by_name(name):
# -- :autolink: (smart default role) -------------------------------------------
-def autolink_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]):
- # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
+def autolink_role(typ: str, rawtext: str, etext: str, lineno: int, inliner: Inliner,
+ options: Dict = {}, content: List[str] = []
+ ) -> Tuple[List[Node], List[system_message]]:
"""Smart linking role.
Expands to ':obj:`text`' if `text` is an object that can be imported;
@@ -686,8 +671,7 @@ class AutoLink(SphinxRole):
Expands to ':obj:`text`' if `text` is an object that can be imported;
otherwise expands to '*text*'.
"""
- def run(self):
- # type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
+ def run(self) -> Tuple[List[Node], List[system_message]]:
pyobj_role = self.env.get_domain('py').role('obj')
objects, errors = pyobj_role('obj', self.rawtext, self.text, self.lineno,
self.inliner, self.options, self.content)
@@ -708,10 +692,8 @@ class AutoLink(SphinxRole):
return objects, errors
-def get_rst_suffix(app):
- # type: (Sphinx) -> str
- def get_supported_format(suffix):
- # type: (str) -> Tuple[str, ...]
+def get_rst_suffix(app: Sphinx) -> str:
+ def get_supported_format(suffix: str) -> Tuple[str, ...]:
parser_class = app.registry.get_source_parsers().get(suffix)
if parser_class is None:
return ('restructuredtext',)
@@ -727,30 +709,34 @@ def get_rst_suffix(app):
return None
-def process_generate_options(app):
- # type: (Sphinx) -> None
+def process_generate_options(app: Sphinx) -> None:
genfiles = app.config.autosummary_generate
- if genfiles and not hasattr(genfiles, '__len__'):
+ if genfiles is True:
env = app.builder.env
genfiles = [env.doc2path(x, base=None) for x in env.found_docs
if os.path.isfile(env.doc2path(x))]
+ else:
+ ext = list(app.config.source_suffix)
+ genfiles = [genfile + (not genfile.endswith(tuple(ext)) and ext[0] or '')
+ for genfile in genfiles]
+
+ for entry in genfiles[:]:
+ if not path.isfile(path.join(app.srcdir, entry)):
+ logger.warning(__('autosummary_generate: file not found: %s'), entry)
+ genfiles.remove(entry)
if not genfiles:
return
- from sphinx.ext.autosummary.generate import generate_autosummary_docs
-
- ext = list(app.config.source_suffix)
- genfiles = [genfile + (not genfile.endswith(tuple(ext)) and ext[0] or '')
- for genfile in genfiles]
-
suffix = get_rst_suffix(app)
if suffix is None:
logger.warning(__('autosummary generats .rst files internally. '
'But your source_suffix does not contain .rst. Skipped.'))
return
+ from sphinx.ext.autosummary.generate import generate_autosummary_docs
+
imported_members = app.config.autosummary_imported_members
with mock(app.config.autosummary_mock_imports):
generate_autosummary_docs(genfiles, builder=app.builder,
@@ -758,8 +744,7 @@ def process_generate_options(app):
app=app, imported_members=imported_members)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
# I need autodoc
app.setup_extension('sphinx.ext.autodoc')
app.add_node(autosummary_toc,
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 4a699dd9f..2a23f1289 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -24,6 +24,7 @@ import pydoc
import re
import sys
import warnings
+from typing import Any, Callable, Dict, List, Set, Tuple, Type
from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
@@ -31,7 +32,9 @@ from jinja2.sandbox import SandboxedEnvironment
import sphinx.locale
from sphinx import __display_version__
from sphinx import package_dir
+from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.ext.autodoc import Documenter
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.locale import __
@@ -41,12 +44,6 @@ from sphinx.util import rst
from sphinx.util.inspect import safe_getattr
from sphinx.util.osutil import ensuredir
-if False:
- # For type annotation
- from typing import Any, Callable, Dict, List, Set, Tuple, Type, Union # NOQA
- from sphinx.builders import Builder # NOQA
- from sphinx.ext.autodoc import Documenter # NOQA
-
logger = logging.getLogger(__name__)
@@ -54,15 +51,13 @@ logger = logging.getLogger(__name__)
class DummyApplication:
"""Dummy Application class for sphinx-autogen command."""
- def __init__(self):
- # type: () -> None
+ def __init__(self) -> None:
self.registry = SphinxComponentRegistry()
self.messagelog = [] # type: List[str]
self.verbosity = 0
-def setup_documenters(app):
- # type: (Any) -> None
+def setup_documenters(app: Any) -> None:
from sphinx.ext.autodoc import (
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
@@ -79,18 +74,15 @@ def setup_documenters(app):
app.registry.add_documenter(documenter.objtype, documenter)
-def _simple_info(msg):
- # type: (str) -> None
+def _simple_info(msg: str) -> None:
print(msg)
-def _simple_warn(msg):
- # type: (str) -> None
+def _simple_warn(msg: str) -> None:
print('WARNING: ' + msg, file=sys.stderr)
-def _underline(title, line='='):
- # type: (str, str) -> str
+def _underline(title: str, line: str = '=') -> str:
if '\n' in title:
raise ValueError('Can only underline single lines')
return title + '\n' + line * len(title)
@@ -99,8 +91,7 @@ def _underline(title, line='='):
class AutosummaryRenderer:
"""A helper class for rendering."""
- def __init__(self, builder, template_dir):
- # type: (Builder, str) -> None
+ def __init__(self, builder: Builder, template_dir: str) -> None:
loader = None # type: BaseLoader
template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
if builder is None:
@@ -117,8 +108,7 @@ class AutosummaryRenderer:
self.env.filters['e'] = rst.escape
self.env.filters['underline'] = _underline
- def exists(self, template_name):
- # type: (str) -> bool
+ def exists(self, template_name: str) -> bool:
"""Check if template file exists."""
try:
self.env.get_template(template_name)
@@ -126,38 +116,107 @@ class AutosummaryRenderer:
except TemplateNotFound:
return False
- def render(self, template_name, context):
- # type: (str, Dict) -> str
+ def render(self, template_name: str, context: Dict) -> str:
"""Render a template file."""
return self.env.get_template(template_name).render(context)
# -- Generating output ---------------------------------------------------------
-def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
- warn=None, info=None, base_path=None, builder=None,
- template_dir=None, imported_members=False, app=None):
- # type: (List[str], str, str, Callable, Callable, str, Builder, str, bool, Any) -> None
+
+def generate_autosummary_content(name: str, obj: Any, parent: Any,
+ template: AutosummaryRenderer, template_name: str,
+ imported_members: bool, app: Any) -> str:
+ doc = get_documenter(app, obj, parent)
+
+ if template_name is None:
+ template_name = 'autosummary/%s.rst' % doc.objtype
+ if not template.exists(template_name):
+ template_name = 'autosummary/base.rst'
+
+ def get_members(obj: Any, types: Set[str], include_public: List[str] = [],
+ imported: bool = True) -> Tuple[List[str], List[str]]:
+ items = [] # type: List[str]
+ for name in dir(obj):
+ try:
+ value = safe_getattr(obj, name)
+ except AttributeError:
+ continue
+ documenter = get_documenter(app, value, obj)
+ if documenter.objtype in types:
+ if imported or getattr(value, '__module__', None) == obj.__name__:
+ # skip imported members if expected
+ items.append(name)
+ public = [x for x in items
+ if x in include_public or not x.startswith('_')]
+ return public, items
+
+ ns = {} # type: Dict[str, Any]
+
+ if doc.objtype == 'module':
+ ns['members'] = dir(obj)
+ ns['functions'], ns['all_functions'] = \
+ get_members(obj, {'function'}, imported=imported_members)
+ ns['classes'], ns['all_classes'] = \
+ get_members(obj, {'class'}, imported=imported_members)
+ ns['exceptions'], ns['all_exceptions'] = \
+ get_members(obj, {'exception'}, imported=imported_members)
+ elif doc.objtype == 'class':
+ ns['members'] = dir(obj)
+ ns['inherited_members'] = \
+ set(dir(obj)) - set(obj.__dict__.keys())
+ ns['methods'], ns['all_methods'] = \
+ get_members(obj, {'method'}, ['__init__'])
+ ns['attributes'], ns['all_attributes'] = \
+ get_members(obj, {'attribute', 'property'})
+
+ parts = name.split('.')
+ if doc.objtype in ('method', 'attribute', 'property'):
+ mod_name = '.'.join(parts[:-2])
+ cls_name = parts[-2]
+ obj_name = '.'.join(parts[-2:])
+ ns['class'] = cls_name
+ else:
+ mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
+
+ ns['fullname'] = name
+ ns['module'] = mod_name
+ ns['objname'] = obj_name
+ ns['name'] = parts[-1]
+
+ ns['objtype'] = doc.objtype
+ ns['underline'] = len(name) * '='
+
+ return template.render(template_name, ns)
+
+
+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:
if info:
warnings.warn('info argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx40Warning)
+ _info = info
else:
- info = logger.info
+ _info = logger.info
if warn:
warnings.warn('warn argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx40Warning)
+ _warn = warn
else:
- warn = logger.warning
+ _warn = logger.warning
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
- info(__('[autosummary] generating autosummary for: %s') %
- ', '.join(showed_sources))
+ _info(__('[autosummary] generating autosummary for: %s') %
+ ', '.join(showed_sources))
if output_dir:
- info(__('[autosummary] writing to %s') % output_dir)
+ _info(__('[autosummary] writing to %s') % output_dir)
if base_path is not None:
sources = [os.path.join(base_path, filename) for filename in sources]
@@ -183,7 +242,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
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)
@@ -195,67 +254,9 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn)
with open(fn, 'w') as f:
- doc = get_documenter(app, obj, parent)
-
- if template_name is None:
- template_name = 'autosummary/%s.rst' % doc.objtype
- if not template.exists(template_name):
- template_name = 'autosummary/base.rst'
-
- def get_members(obj, types, include_public=[], imported=True):
- # type: (Any, Set[str], List[str], bool) -> Tuple[List[str], List[str]] # NOQA
- items = [] # type: List[str]
- for name in dir(obj):
- try:
- value = safe_getattr(obj, name)
- except AttributeError:
- continue
- documenter = get_documenter(app, value, obj)
- if documenter.objtype in types:
- if imported or getattr(value, '__module__', None) == obj.__name__:
- # skip imported members if expected
- items.append(name)
- public = [x for x in items
- if x in include_public or not x.startswith('_')]
- return public, items
-
- ns = {} # type: Dict[str, Any]
-
- if doc.objtype == 'module':
- ns['members'] = dir(obj)
- ns['functions'], ns['all_functions'] = \
- get_members(obj, {'function'}, imported=imported_members)
- ns['classes'], ns['all_classes'] = \
- get_members(obj, {'class'}, imported=imported_members)
- ns['exceptions'], ns['all_exceptions'] = \
- get_members(obj, {'exception'}, imported=imported_members)
- elif doc.objtype == 'class':
- ns['members'] = dir(obj)
- ns['inherited_members'] = \
- set(dir(obj)) - set(obj.__dict__.keys())
- ns['methods'], ns['all_methods'] = \
- get_members(obj, {'method'}, ['__init__'])
- ns['attributes'], ns['all_attributes'] = \
- get_members(obj, {'attribute', 'property'})
-
- parts = name.split('.')
- if doc.objtype in ('method', 'attribute', 'property'):
- mod_name = '.'.join(parts[:-2])
- cls_name = parts[-2]
- obj_name = '.'.join(parts[-2:])
- ns['class'] = cls_name
- else:
- mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
-
- ns['fullname'] = name
- ns['module'] = mod_name
- ns['objname'] = obj_name
- ns['name'] = parts[-1]
-
- ns['objtype'] = doc.objtype
- ns['underline'] = len(name) * '='
-
- rendered = template.render(template_name, ns)
+ rendered = generate_autosummary_content(name, obj, parent,
+ template, template_name,
+ imported_members, app)
f.write(rendered)
# descend recursively to new files
@@ -268,8 +269,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
# -- Finding documented entries in files ---------------------------------------
-def find_autosummary_in_files(filenames):
- # type: (List[str]) -> List[Tuple[str, str, str]]
+def find_autosummary_in_files(filenames: List[str]) -> List[Tuple[str, str, str]]:
"""Find out what items are documented in source/*.rst.
See `find_autosummary_in_lines`.
@@ -282,8 +282,8 @@ def find_autosummary_in_files(filenames):
return documented
-def find_autosummary_in_docstring(name, module=None, filename=None):
- # type: (str, Any, str) -> List[Tuple[str, str, str]]
+def find_autosummary_in_docstring(name: str, module: Any = None, filename: str = None
+ ) -> List[Tuple[str, str, str]]:
"""Find out what items are documented in the given object's docstring.
See `find_autosummary_in_lines`.
@@ -302,8 +302,8 @@ def find_autosummary_in_docstring(name, module=None, filename=None):
return []
-def find_autosummary_in_lines(lines, module=None, filename=None):
- # type: (List[str], Any, str) -> List[Tuple[str, str, str]]
+def find_autosummary_in_lines(lines: List[str], module: Any = None, filename: str = None
+ ) -> List[Tuple[str, str, str]]:
"""Find out what items appear in autosummary:: directives in the
given lines.
@@ -389,8 +389,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
return documented
-def get_parser():
- # type: () -> argparse.ArgumentParser
+def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] ...',
epilog=__('For more information, visit .'),
@@ -432,8 +431,7 @@ The format of the autosummary directive is documented in the
return parser
-def main(argv=sys.argv[1:]):
- # type: (List[str]) -> None
+def main(argv: List[str] = sys.argv[1:]) -> None:
sphinx.locale.setlocale(locale.LC_ALL, '')
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 6c9489046..24f51b856 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -14,30 +14,25 @@ import inspect
import pickle
import re
from os import path
+from typing import Any, Dict, IO, List, Pattern, Set, Tuple
import sphinx
+from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.inspect import safe_getattr
-if False:
- # For type annotation
- from typing import Any, Dict, IO, List, Pattern, Set, Tuple # NOQA
- from sphinx.application import Sphinx # NOQA
-
logger = logging.getLogger(__name__)
# utility
-def write_header(f, text, char='-'):
- # type:(IO, str, str) -> None
+def write_header(f: IO, text: str, char: str = '-') -> None:
f.write(text + '\n')
f.write(char * len(text) + '\n')
-def compile_regex_list(name, exps):
- # type: (str, str) -> List[Pattern]
+def compile_regex_list(name: str, exps: str) -> List[Pattern]:
lst = []
for exp in exps:
try:
@@ -55,8 +50,7 @@ class CoverageBuilder(Builder):
epilog = __('Testing of coverage in the sources finished, look at the '
'results in %(outdir)s' + path.sep + 'python.txt.')
- def init(self):
- # type: () -> None
+ def init(self) -> None:
self.c_sourcefiles = [] # type: List[str]
for pattern in self.config.coverage_c_path:
pattern = path.join(self.srcdir, pattern)
@@ -82,12 +76,10 @@ class CoverageBuilder(Builder):
self.py_ignorexps = compile_regex_list('coverage_ignore_pyobjects',
self.config.coverage_ignore_pyobjects)
- def get_outdated_docs(self):
- # type: () -> str
+ def get_outdated_docs(self) -> str:
return 'coverage overview'
- def write(self, *ignored):
- # type: (Any) -> None
+ def write(self, *ignored) -> None:
self.py_undoc = {} # type: Dict[str, Dict[str, Any]]
self.build_py_coverage()
self.write_py_coverage()
@@ -96,8 +88,7 @@ class CoverageBuilder(Builder):
self.build_c_coverage()
self.write_c_coverage()
- def build_c_coverage(self):
- # type: () -> None
+ def build_c_coverage(self) -> None:
# Fetch all the info from the header files
c_objects = self.env.domaindata['c']['objects']
for filename in self.c_sourcefiles:
@@ -118,8 +109,7 @@ class CoverageBuilder(Builder):
if undoc:
self.c_undoc[filename] = undoc
- def write_c_coverage(self):
- # type: () -> None
+ def write_c_coverage(self) -> None:
output_file = path.join(self.outdir, 'c.txt')
with open(output_file, 'w') as op:
if self.config.coverage_write_headline:
@@ -138,8 +128,7 @@ class CoverageBuilder(Builder):
return True
return False
- def build_py_coverage(self):
- # type: () -> None
+ def build_py_coverage(self) -> None:
objects = self.env.domaindata['py']['objects']
modules = self.env.domaindata['py']['modules']
@@ -230,8 +219,7 @@ class CoverageBuilder(Builder):
self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
- def write_py_coverage(self):
- # type: () -> None
+ def write_py_coverage(self) -> None:
output_file = path.join(self.outdir, 'python.txt')
failed = []
with open(output_file, 'w') as op:
@@ -266,16 +254,14 @@ class CoverageBuilder(Builder):
write_header(op, 'Modules that failed to import')
op.writelines(' * %s -- %s\n' % x for x in failed)
- def finish(self):
- # type: () -> None
+ def finish(self) -> None:
# dump the coverage data to a pickle file too
picklepath = path.join(self.outdir, 'undoc.pickle')
with open(picklepath, 'wb') as dumpfile:
pickle.dump((self.py_undoc, self.c_undoc), dumpfile)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(CoverageBuilder)
app.add_config_value('coverage_ignore_modules', [], False)
app.add_config_value('coverage_ignore_functions', [], False)
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 01ab38efe..68df253d5 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -16,8 +16,10 @@ import time
import warnings
from io import StringIO
from os import path
+from typing import Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type
from docutils import nodes
+from docutils.nodes import Element, Node, TextElement
from docutils.parsers.rst import directives
from packaging.specifiers import SpecifierSet, InvalidSpecifier
from packaging.version import Version
@@ -33,8 +35,8 @@ from sphinx.util.osutil import relpath
if False:
# For type annotation
- from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Type # NOQA
- from sphinx.application import Sphinx # NOQA
+ from sphinx.application import Sphinx
+
logger = logging.getLogger(__name__)
@@ -42,15 +44,13 @@ blankline_re = re.compile(r'^\s*', re.MULTILINE)
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
-def doctest_encode(text, encoding):
- # type: (str, str) -> str
+def doctest_encode(text: str, encoding: str) -> str:
warnings.warn('doctest_encode() is deprecated.',
RemovedInSphinx40Warning)
return text
-def is_allowed_version(spec, version):
- # type: (str, str) -> bool
+def is_allowed_version(spec: str, version: str) -> bool:
"""Check `spec` satisfies `version` or not.
This obeys PEP-440 specifiers:
@@ -80,8 +80,7 @@ class TestDirective(SphinxDirective):
optional_arguments = 1
final_argument_whitespace = True
- def run(self):
- # type: () -> List[nodes.Node]
+ def run(self) -> List[Node]:
# use ordinary docutils nodes for test code: they get special attributes
# so that our builder recognizes them, and the other builders are happy.
code = '\n'.join(self.content)
@@ -95,7 +94,7 @@ class TestDirective(SphinxDirective):
if not test:
test = code
code = doctestopt_re.sub('', code)
- nodetype = nodes.literal_block # type: Type[nodes.TextElement]
+ nodetype = nodes.literal_block # type: Type[TextElement]
if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options:
nodetype = nodes.comment
if self.arguments:
@@ -194,15 +193,13 @@ parser = doctest.DocTestParser()
# helper classes
class TestGroup:
- def __init__(self, name):
- # type: (str) -> None
+ def __init__(self, name: str) -> None:
self.name = name
self.setup = [] # type: List[TestCode]
self.tests = [] # type: List[List[TestCode]]
self.cleanup = [] # type: List[TestCode]
- def add_code(self, code, prepend=False):
- # type: (TestCode, bool) -> None
+ def add_code(self, code: "TestCode", prepend: bool = False) -> None:
if code.type == 'testsetup':
if prepend:
self.setup.insert(0, code)
@@ -220,30 +217,28 @@ class TestGroup:
else:
raise RuntimeError(__('invalid TestCode type'))
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return 'TestGroup(name=%r, setup=%r, cleanup=%r, tests=%r)' % (
self.name, self.setup, self.cleanup, self.tests)
class TestCode:
- def __init__(self, code, type, filename, lineno, options=None):
- # type: (str, str, Optional[str], int, Optional[Dict]) -> None
+ def __init__(self, code: str, type: str, filename: str,
+ lineno: int, options: Dict = None) -> None:
self.code = code
self.type = type
self.filename = filename
self.lineno = lineno
self.options = options or {}
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return 'TestCode(%r, %r, filename=%r, lineno=%r, options=%r)' % (
self.code, self.type, self.filename, self.lineno, self.options)
class SphinxDocTestRunner(doctest.DocTestRunner):
- def summarize(self, out, verbose=None): # type: ignore
- # type: (Callable, bool) -> Tuple[int, int]
+ def summarize(self, out: Callable, verbose: bool = None # type: ignore
+ ) -> Tuple[int, int]:
string_io = StringIO()
old_stdout = sys.stdout
sys.stdout = string_io
@@ -254,9 +249,8 @@ class SphinxDocTestRunner(doctest.DocTestRunner):
out(string_io.getvalue())
return res
- def _DocTestRunner__patched_linecache_getlines(self, filename,
- module_globals=None):
- # type: (str, Any) -> Any
+ def _DocTestRunner__patched_linecache_getlines(self, filename: str,
+ module_globals: Any = None) -> Any:
# this is overridden from DocTestRunner adding the try-except below
m = self._DocTestRunner__LINECACHE_FILENAME_RE.match(filename) # type: ignore
if m and m.group('name') == self.test.name:
@@ -282,8 +276,7 @@ class DocTestBuilder(Builder):
epilog = __('Testing of doctests in the sources finished, look at the '
'results in %(outdir)s/output.txt.')
- def init(self):
- # type: () -> None
+ def init(self) -> None:
# default options
self.opt = self.config.doctest_default_flags
@@ -312,32 +305,26 @@ class DocTestBuilder(Builder):
'==================================%s\n') %
(date, '=' * len(date)))
- def _out(self, text):
- # type: (str) -> None
+ def _out(self, text: str) -> None:
logger.info(text, nonl=True)
self.outfile.write(text)
- def _warn_out(self, text):
- # type: (str) -> None
+ def _warn_out(self, text: str) -> None:
if self.app.quiet or self.app.warningiserror:
logger.warning(text)
else:
logger.info(text, nonl=True)
self.outfile.write(text)
- def get_target_uri(self, docname, typ=None):
- # type: (str, str) -> str
+ def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''
- def get_outdated_docs(self):
- # type: () -> Set[str]
+ def get_outdated_docs(self) -> Set[str]:
return self.env.found_docs
- def finish(self):
- # type: () -> None
+ def finish(self) -> None:
# write executive summary
- def s(v):
- # type: (int) -> str
+ def s(v: int) -> str:
return v != 1 and 's' or ''
repl = (self.total_tries, s(self.total_tries),
self.total_failures, s(self.total_failures),
@@ -356,8 +343,8 @@ Doctest summary
if self.total_failures or self.setup_failures or self.cleanup_failures:
self.app.statuscode = 1
- def write(self, build_docnames, updated_docnames, method='update'):
- # type: (Iterable[str], Sequence[str], str) -> None
+ def write(self, build_docnames: Iterable[str], updated_docnames: Sequence[str],
+ method: str = 'update') -> None:
if build_docnames is None:
build_docnames = sorted(self.env.all_docs)
@@ -367,8 +354,7 @@ Doctest summary
doctree = self.env.get_doctree(docname)
self.test_doc(docname, doctree)
- def get_filename_for_node(self, node, docname):
- # type: (nodes.Node, str) -> str
+ def get_filename_for_node(self, node: Node, docname: str) -> str:
"""Try to get the file which actually contains the doctest, not the
filename of the document it's included in."""
try:
@@ -379,8 +365,7 @@ Doctest summary
return filename
@staticmethod
- def get_line_number(node):
- # type: (nodes.Node) -> Optional[int]
+ def get_line_number(node: Node) -> int:
"""Get the real line number or admit we don't know."""
# TODO: Work out how to store or calculate real (file-relative)
# line numbers for doctest blocks in docstrings.
@@ -395,8 +380,7 @@ Doctest summary
return node.line - 1
return None
- def skipped(self, node):
- # type: (nodes.Element) -> bool
+ def skipped(self, node: Element) -> bool:
if 'skipif' not in node:
return False
else:
@@ -409,8 +393,7 @@ Doctest summary
exec(self.config.doctest_global_cleanup, context)
return should_skip
- def test_doc(self, docname, doctree):
- # type: (str, nodes.Node) -> None
+ def test_doc(self, docname: str, doctree: Node) -> None:
groups = {} # type: Dict[str, TestGroup]
add_to_all_groups = []
self.setup_runner = SphinxDocTestRunner(verbose=False,
@@ -424,17 +407,15 @@ Doctest summary
self.cleanup_runner._fakeout = self.setup_runner._fakeout # type: ignore
if self.config.doctest_test_doctest_blocks:
- def condition(node):
- # type: (nodes.Node) -> bool
+ def condition(node: Node) -> bool:
return (isinstance(node, (nodes.literal_block, nodes.comment)) and
'testnodetype' in node) or \
isinstance(node, nodes.doctest_block)
else:
- def condition(node):
- # type: (nodes.Node) -> bool
+ def condition(node: Node) -> bool:
return isinstance(node, (nodes.literal_block, nodes.comment)) \
and 'testnodetype' in node
- for node in doctree.traverse(condition): # type: nodes.Element
+ for node in doctree.traverse(condition): # type: Element
if self.skipped(node):
continue
@@ -490,16 +471,13 @@ Doctest summary
self.cleanup_failures += res_f
self.cleanup_tries += res_t
- def compile(self, code, name, type, flags, dont_inherit):
- # type: (str, str, str, Any, bool) -> Any
+ def compile(self, code: str, name: str, type: str, flags: Any, dont_inherit: bool) -> Any:
return compile(code, name, self.type, flags, dont_inherit)
- def test_group(self, group):
- # type: (TestGroup) -> None
+ def test_group(self, group: TestGroup) -> None:
ns = {} # type: Dict
- def run_setup_cleanup(runner, testcodes, what):
- # type: (Any, List[TestCode], Any) -> bool
+ def run_setup_cleanup(runner: Any, testcodes: List[TestCode], what: Any) -> bool:
examples = []
for testcode in testcodes:
example = doctest.Example(testcode.code, '', lineno=testcode.lineno)
@@ -568,8 +546,7 @@ Doctest summary
run_setup_cleanup(self.cleanup_runner, group.cleanup, 'cleanup')
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_directive('testsetup', TestsetupDirective)
app.add_directive('testcleanup', TestcleanupDirective)
app.add_directive('doctest', DoctestDirective)
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py
index 7ad721b8f..39a989f23 100644
--- a/sphinx/ext/extlinks.py
+++ b/sphinx/ext/extlinks.py
@@ -23,23 +23,22 @@
:license: BSD, see LICENSE for details.
"""
+from typing import Any, Dict, List, Tuple
+
from docutils import nodes, utils
+from docutils.nodes import Node, system_message
+from docutils.parsers.rst.states import Inliner
import sphinx
+from sphinx.application import Sphinx
from sphinx.util.nodes import split_explicit_title
-
-if False:
- # For type annotation
- from typing import Any, Dict, List, Tuple # NOQA
- from docutils.parsers.rst.states import Inliner # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.util.typing import RoleFunction # NOQA
+from sphinx.util.typing import RoleFunction
-def make_link_role(base_url, prefix):
- # type: (str, str) -> RoleFunction
- def role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- # type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
+def make_link_role(base_url: str, prefix: str) -> RoleFunction:
+ def role(typ: str, rawtext: str, text: str, lineno: int,
+ inliner: Inliner, options: Dict = {}, content: List[str] = []
+ ) -> Tuple[List[Node], List[system_message]]:
text = utils.unescape(text)
has_explicit_title, title, part = split_explicit_title(text)
try:
@@ -60,14 +59,12 @@ def make_link_role(base_url, prefix):
return role
-def setup_link_roles(app):
- # type: (Sphinx) -> None
+def setup_link_roles(app: Sphinx) -> None:
for name, (base_url, prefix) in app.config.extlinks.items():
app.add_role(name, make_link_role(base_url, prefix))
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('extlinks', {}, 'env')
app.connect('builder-inited', setup_link_roles)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
diff --git a/sphinx/ext/githubpages.py b/sphinx/ext/githubpages.py
index 5cecddd09..6a07c651a 100644
--- a/sphinx/ext/githubpages.py
+++ b/sphinx/ext/githubpages.py
@@ -10,18 +10,14 @@
import os
import urllib
+from typing import Any, Dict
import sphinx
-
-if False:
- # For type annotation
- from typing import Any, Dict # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
+from sphinx.application import Sphinx
+from sphinx.environment import BuildEnvironment
-def create_nojekyll_and_cname(app, env):
- # type: (Sphinx, BuildEnvironment) -> None
+def create_nojekyll_and_cname(app: Sphinx, env: BuildEnvironment) -> None:
if app.builder.format == 'html':
open(os.path.join(app.builder.outdir, '.nojekyll'), 'wt').close()
@@ -35,7 +31,6 @@ def create_nojekyll_and_cname(app, env):
f.write(domain)
-def setup(app):
- # type: (Sphinx) -> Dict[str, Any]
+def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('env-updated', create_nojekyll_and_cname)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index ab45b55fc..c16952bc0 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -15,31 +15,27 @@ import subprocess
from hashlib import sha1
from os import path
from subprocess import CalledProcessError, PIPE
+from typing import Any, Dict, List, Tuple
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.nodes import Node
+from docutils.parsers.rst import Directive, directives
import sphinx
+from sphinx.application import Sphinx
from sphinx.errors import SphinxError
from sphinx.locale import _, __
from sphinx.util import logging
-from sphinx.util.docutils import SphinxDirective
+from sphinx.util.docutils import SphinxDirective, SphinxTranslator
from sphinx.util.fileutil import copy_asset
from sphinx.util.i18n import search_image_for_language
from sphinx.util.nodes import set_source_info
from sphinx.util.osutil import ensuredir
-
-if False:
- # For type annotation
- from docutils.parsers.rst import Directive # NOQA
- from typing import Any, Dict, List, Tuple # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.util.docutils import SphinxTranslator # NOQA
- from sphinx.writers.html import HTMLTranslator # NOQA
- from sphinx.writers.latex import LaTeXTranslator # NOQA
- from sphinx.writers.manpage import ManualPageTranslator # NOQA
- from sphinx.writers.texinfo import TexinfoTranslator # NOQA
- from sphinx.writers.text import TextTranslator # NOQA
+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
logger = logging.getLogger(__name__)
@@ -53,8 +49,7 @@ class ClickableMapDefinition:
maptag_re = re.compile('
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 1fb7bcde6..a36c256d3 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -169,7 +169,7 @@ class DownloadFiles(dict):
Hence don't hack this directly.
"""
- def add_file(self, docname: str, filename: str) -> None:
+ def add_file(self, docname: str, filename: str) -> str:
if filename not in self:
digest = md5(filename.encode()).hexdigest()
dest = '%s/%s' % (digest, os.path.basename(filename))
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 14b190406..bfd43e19e 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -12,7 +12,7 @@ import os
import re
import warnings
from collections import namedtuple
-from datetime import datetime
+from datetime import datetime, timezone
from os import path
from typing import Callable, Generator, List, Set, Tuple
@@ -274,7 +274,7 @@ def format_date(format: str, date: datetime = None, language: str = None) -> str
if source_date_epoch is not None:
date = datetime.utcfromtimestamp(float(source_date_epoch))
else:
- date = datetime.now()
+ date = datetime.now(timezone.utc).astimezone()
result = []
tokens = date_format_re.split(format)
diff --git a/sphinx/util/stemmer/__init__.py b/sphinx/util/stemmer/__init__.py
index bda5d2bc2..8d8d36c68 100644
--- a/sphinx/util/stemmer/__init__.py
+++ b/sphinx/util/stemmer/__init__.py
@@ -30,7 +30,7 @@ class PyStemmer(BaseStemmer):
return self.stemmer.stemWord(word)
-class StandardStemmer(PorterStemmer, BaseStemmer): # type: ignore
+class StandardStemmer(PorterStemmer, BaseStemmer):
"""All those porter stemmer implementations look hideous;
make at least the stem method nicer.
"""
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 636767d41..fd8886944 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -9,7 +9,7 @@
"""
import os
-from typing import Dict
+from typing import Dict, List, Union
from jinja2.loaders import BaseLoader
from jinja2.sandbox import SandboxedEnvironment
@@ -34,7 +34,13 @@ class BaseRenderer:
class FileRenderer(BaseRenderer):
- def __init__(self, search_path: str) -> None:
+ def __init__(self, search_path: Union[str, List[str]]) -> None:
+ if isinstance(search_path, str):
+ search_path = [search_path]
+ else:
+ # filter "None" paths
+ search_path = list(filter(None, search_path))
+
loader = SphinxFileSystemLoader(search_path)
super().__init__(loader)
@@ -46,7 +52,7 @@ class FileRenderer(BaseRenderer):
class SphinxRenderer(FileRenderer):
- def __init__(self, template_path: str = None) -> None:
+ def __init__(self, template_path: Union[str, List[str]] = None) -> None:
if template_path is None:
template_path = os.path.join(package_dir, 'templates')
super().__init__(template_path)
@@ -76,7 +82,7 @@ class LaTeXRenderer(SphinxRenderer):
class ReSTRenderer(SphinxRenderer):
- def __init__(self, template_path: str = None, language: str = None) -> None:
+ def __init__(self, template_path: Union[str, List[str]] = None, language: str = None) -> None: # NOQA
super().__init__(template_path)
# add language to environment
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 5c7f52507..5fdcb5b93 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1373,11 +1373,8 @@ class LaTeXTranslator(SphinxTranslator):
suffix = node.get('suffix', '.')
self.body.append('\\begin{enumerate}\n')
- self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum))
- self.body.append('\\def\\label%s{%s\\the%s %s}\n' %
- (enum, prefix, enum, suffix))
- self.body.append('\\makeatletter\\def\\p@%s{\\p@%s %s\\the%s %s}\\makeatother\n' %
- (enumnext, enum, prefix, enum, suffix))
+ self.body.append('\\sphinxsetlistlabels{%s}{%s}{%s}{%s}{%s}%%\n' %
+ (style, enum, enumnext, prefix, suffix))
if 'start' in node:
self.body.append('\\setcounter{%s}{%d}\n' % (enum, node['start'] - 1))
if self.table:
@@ -2598,7 +2595,7 @@ class LaTeXTranslator(SphinxTranslator):
RemovedInSphinx30Warning)
def visit_admonition(self, node):
- # type: (nodes.Element) -> None
+ # type: (LaTeXTranslator, nodes.Element) -> None
self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' %
(name, admonitionlabels[name]))
return visit_admonition
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 4262ccd66..b952812f0 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -1752,6 +1752,6 @@ class TexinfoTranslator(SphinxTranslator):
RemovedInSphinx30Warning)
def visit(self, node):
- # type: (nodes.Element) -> None
+ # type: (TexinfoTranslator, nodes.Element) -> None
self.visit_admonition(node, admonitionlabels[name])
return visit
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 1447510c3..dc8a7963a 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -1375,6 +1375,6 @@ class TextTranslator(SphinxTranslator):
RemovedInSphinx30Warning)
def depart_admonition(self, node):
- # type: (nodes.Element) -> None
+ # type: (TextTranslator, nodes.Element) -> None
self.end_state(first=admonitionlabels[name] + ': ')
return depart_admonition
diff --git a/tests/roots/test-ext-autodoc/target/inheritance.py b/tests/roots/test-ext-autodoc/target/inheritance.py
index 3a5fc0711..ffac84bb6 100644
--- a/tests/roots/test-ext-autodoc/target/inheritance.py
+++ b/tests/roots/test-ext-autodoc/target/inheritance.py
@@ -15,5 +15,3 @@ class Derived(Base):
def inheritedmeth(self):
# no docstring here
pass
-
-
diff --git a/tests/roots/test-images/index.rst b/tests/roots/test-images/index.rst
index 67b742b27..14a2987a0 100644
--- a/tests/roots/test-images/index.rst
+++ b/tests/roots/test-images/index.rst
@@ -26,4 +26,4 @@ test-image
.. image:: https://www.python.org/static/img/python-logo.png
.. non-exist remote image
-.. image:: http://example.com/NOT_EXIST.PNG
+.. image:: https://www.google.com/NOT_EXIST.PNG
diff --git a/tests/roots/test-intl/external_links.txt b/tests/roots/test-intl/external_links.txt
index 96e3973de..1cecbeeb8 100644
--- a/tests/roots/test-intl/external_links.txt
+++ b/tests/roots/test-intl/external_links.txt
@@ -23,8 +23,8 @@ link to external1_ and external2_.
link to `Sphinx Site `_ and `Python Site `_.
-.. _external1: http://example.com/external1
-.. _external2: http://example.com/external2
+.. _external1: https://www.google.com/external1
+.. _external2: https://www.google.com/external2
Multiple references in the same line
diff --git a/tests/roots/test-linkcheck/links.txt b/tests/roots/test-linkcheck/links.txt
index ac5ed3246..fa8f11e4c 100644
--- a/tests/roots/test-linkcheck/links.txt
+++ b/tests/roots/test-linkcheck/links.txt
@@ -6,11 +6,11 @@ This is from CPython documentation.
Some additional anchors to exercise ignore code
-* `Example Bar invalid `_
-* `Example Bar invalid `_ tests that default ignore anchor of #! does not need to be prefixed with /
-* `Example Bar invalid `_
+* `Example Bar invalid `_
+* `Example Bar invalid `_ tests that default ignore anchor of #! does not need to be prefixed with /
+* `Example Bar invalid `_
* `Example anchor invalid `_
* `Complete nonsense `_
-.. image:: http://example.com/image.png
-.. figure:: http://example.com/image2.png
+.. image:: https://www.google.com/image.png
+.. figure:: https://www.google.com/image2.png
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index 26c63771a..b41ceb2d3 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -52,7 +52,8 @@ def test_msgfmt(app):
assert (app.outdir / 'en_US.po').isfile(), 'msginit failed'
try:
- args = ['msgfmt', 'en_US.po', '-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')]
+ args = ['msgfmt', 'en_US.po',
+ '-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')]
subprocess.run(args, stdout=PIPE, stderr=PIPE, check=True)
except OSError:
pytest.skip() # most likely msgfmt was not found
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 677ca9de0..3255bb71e 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -16,6 +16,7 @@ from itertools import cycle, chain
import pytest
from html5lib import HTMLParser
+from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.testing.util import strip_escseq
from sphinx.util import docutils
@@ -1239,25 +1240,25 @@ def test_html_entity(app):
def test_html_inventory(app):
app.builder.build_all()
with open(app.outdir / 'objects.inv', 'rb') as f:
- invdata = InventoryFile.load(f, 'http://example.com', os.path.join)
+ invdata = InventoryFile.load(f, 'https://www.google.com', os.path.join)
assert set(invdata.keys()) == {'std:label', 'std:doc'}
assert set(invdata['std:label'].keys()) == {'modindex', 'genindex', 'search'}
assert invdata['std:label']['modindex'] == ('Python',
'',
- 'http://example.com/py-modindex.html',
+ 'https://www.google.com/py-modindex.html',
'Module Index')
assert invdata['std:label']['genindex'] == ('Python',
'',
- 'http://example.com/genindex.html',
+ 'https://www.google.com/genindex.html',
'Index')
assert invdata['std:label']['search'] == ('Python',
'',
- 'http://example.com/search.html',
+ 'https://www.google.com/search.html',
'Search Page')
assert set(invdata['std:doc'].keys()) == {'index'}
assert invdata['std:doc']['index'] == ('Python',
'',
- 'http://example.com/index.html',
+ 'https://www.google.com/index.html',
'The basic Sphinx documentation for testing')
@@ -1496,3 +1497,29 @@ def test_html_pygments_style_manually(app):
def test_html_pygments_for_classic_theme(app):
style = app.builder.highlighter.formatter_args.get('style')
assert style.__name__ == 'SphinxStyle'
+
+
+@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_extra_path')
+def test_validate_html_extra_path(app):
+ (app.confdir / '_static').makedirs()
+ app.config.html_extra_path = [
+ '/path/to/not_found', # not found
+ '_static',
+ app.outdir, # outdir
+ app.outdir / '_static', # inside outdir
+ ]
+ validate_html_extra_path(app, app.config)
+ assert app.config.html_extra_path == ['_static']
+
+
+@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_static_path')
+def test_validate_html_static_path(app):
+ (app.confdir / '_static').makedirs()
+ app.config.html_static_path = [
+ '/path/to/not_found', # not found
+ '_static',
+ app.outdir, # outdir
+ app.outdir / '_static', # inside outdir
+ ]
+ validate_html_static_path(app, app.config)
+ assert app.config.html_static_path == ['_static']
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 13bb22e96..67491ddc9 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -1242,7 +1242,7 @@ def test_latex_images(app, status, warning):
# not found images
assert '\\sphinxincludegraphics{{NOT_EXIST}.PNG}' not in result
assert ('WARNING: Could not fetch remote image: '
- 'http://example.com/NOT_EXIST.PNG [404]' in warning.getvalue())
+ 'https://www.google.com/NOT_EXIST.PNG [404]' in warning.getvalue())
# an image having target
assert ('\\sphinxhref{https://www.sphinx-doc.org/}'
@@ -1292,25 +1292,15 @@ def test_latex_nested_enumerated_list(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'python.tex').text(encoding='utf8')
- assert ('\\def\\theenumi{\\arabic{enumi}}\n'
- '\\def\\labelenumi{\\theenumi .}\n'
- '\\makeatletter\\def\\p@enumii{\\p@enumi \\theenumi .}\\makeatother\n'
+ assert ('\\sphinxsetlistlabels{\\arabic}{enumi}{enumii}{}{.}%\n'
'\\setcounter{enumi}{4}\n' in result)
- assert ('\\def\\theenumii{\\alph{enumii}}\n'
- '\\def\\labelenumii{\\theenumii .}\n'
- '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n'
+ assert ('\\sphinxsetlistlabels{\\alph}{enumii}{enumiii}{}{.}%\n'
'\\setcounter{enumii}{3}\n' in result)
- assert ('\\def\\theenumiii{\\arabic{enumiii}}\n'
- '\\def\\labelenumiii{\\theenumiii )}\n'
- '\\makeatletter\\def\\p@enumiv{\\p@enumiii \\theenumiii )}\\makeatother\n'
+ assert ('\\sphinxsetlistlabels{\\arabic}{enumiii}{enumiv}{}{)}%\n'
'\\setcounter{enumiii}{9}\n' in result)
- assert ('\\def\\theenumiv{\\arabic{enumiv}}\n'
- '\\def\\labelenumiv{(\\theenumiv )}\n'
- '\\makeatletter\\def\\p@enumv{\\p@enumiv (\\theenumiv )}\\makeatother\n'
+ assert ('\\sphinxsetlistlabels{\\arabic}{enumiv}{enumv}{(}{)}%\n'
'\\setcounter{enumiv}{23}\n' in result)
- assert ('\\def\\theenumii{\\roman{enumii}}\n'
- '\\def\\labelenumii{\\theenumii .}\n'
- '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n'
+ assert ('\\sphinxsetlistlabels{\\roman}{enumii}{enumiii}{}{.}%\n'
'\\setcounter{enumii}{2}\n' in result)
@@ -1405,6 +1395,7 @@ def test_latex_figure_in_admonition(app, status, warning):
result = (app.outdir / 'python.tex').text(encoding='utf8')
assert(r'\begin{figure}[H]' in result)
+
def test_default_latex_documents():
from sphinx.util import texescape
texescape.init()
diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py
index 6d25058eb..4bf47a962 100644
--- a/tests/test_build_linkcheck.py
+++ b/tests/test_build_linkcheck.py
@@ -25,8 +25,8 @@ def test_defaults(app, status, warning):
# looking for non-existent URL should fail
assert " Max retries exceeded with url: /doesnotexist" in content
# images should fail
- assert "Not Found for url: http://example.com/image.png" in content
- assert "Not Found for url: http://example.com/image2.png" in content
+ assert "Not Found for url: https://www.google.com/image.png" in content
+ assert "Not Found for url: https://www.google.com/image2.png" in content
assert len(content.splitlines()) == 5
@@ -36,8 +36,8 @@ def test_defaults(app, status, warning):
'linkcheck_ignore': [
'https://localhost:7777/doesnotexist',
'http://www.sphinx-doc.org/en/1.7/intro.html#',
- 'http://example.com/image.png',
- 'http://example.com/image2.png']
+ 'https://www.google.com/image.png',
+ 'https://www.google.com/image2.png']
})
def test_anchors_ignored(app, status, warning):
app.builder.build_all()
diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py
index d1f7c03dc..13b451078 100644
--- a/tests/test_directive_code.py
+++ b/tests/test_directive_code.py
@@ -617,6 +617,6 @@ def test_linenothreshold(app, status, warning):
# literal include not using linenothreshold
html, matched, _ = html.partition(lineos_head +
'1\n'
- '2\n'
+ '2\n'
'3' + lineos_tail)
assert not matched
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index c81d66b17..bbb4cc55e 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -129,14 +129,14 @@ def test_expressions():
'5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
- '5.0', '5.0e42','5.0e+42', '5.0e-42']:
+ '5.0', '5.0e42', '5.0e+42', '5.0e-42']:
expr = e + suffix
exprCheck(expr, 'L' + expr + 'E')
for e in [
'ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
- 'A.B', 'A.BpF','A.Bp+F', 'A.Bp-F']:
+ 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']:
expr = "0x" + e + suffix
exprCheck(expr, 'L' + expr + 'E')
exprCheck('"abc\\"cba"', 'LA8_KcE') # string
@@ -676,7 +676,7 @@ def test_template_args():
def test_initializers():
- idsMember = {1: 'v__T', 2:'1v'}
+ idsMember = {1: 'v__T', 2: '1v'}
idsFunction = {1: 'f__T', 2: '1f1T'}
idsTemplate = {2: 'I_1TE1fv', 4: 'I_1TE1fvv'}
# no init
@@ -748,7 +748,7 @@ def test_attributes():
{1: 'f', 2: '1fv'},
output='[[attr1]] [[attr2]] void f()')
# position: declarator
- check('member', 'int *[[attr]] i', {1: 'i__iP', 2:'1i'})
+ check('member', 'int *[[attr]] i', {1: 'i__iP', 2: '1i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i__iPVC', 2: '1i'},
output='int *[[attr]] volatile const i')
check('member', 'int &[[attr]] i', {1: 'i__iR', 2: '1i'})
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 15562536f..1c6b49e64 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -11,9 +11,32 @@ import pytest
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder
+from sphinx.environment import CONFIG_OK, CONFIG_CHANGED, CONFIG_EXTENSIONS_CHANGED, CONFIG_NEW
from sphinx.testing.comparer import PathComparer
+@pytest.mark.sphinx('dummy', testroot='basic')
+def test_config_status(make_app, app_params):
+ args, kwargs = app_params
+
+ # clean build
+ app1 = make_app(*args, freshenv=True, **kwargs)
+ assert app1.env.config_status == CONFIG_NEW
+ app1.build()
+
+ # incremental build (no config changed)
+ app2 = make_app(*args, **kwargs)
+ assert app2.env.config_status == CONFIG_OK
+
+ # incremental build (config entry changed)
+ app3 = make_app(*args, confoverrides={'master_doc': 'content'}, **kwargs)
+ assert app3.env.config_status == CONFIG_CHANGED
+
+ # incremental build (extension changed)
+ app4 = make_app(*args, confoverrides={'extensions': ['sphinx.ext.autodoc']}, **kwargs)
+ assert app4.env.config_status == CONFIG_EXTENSIONS_CHANGED
+
+
@pytest.mark.sphinx('dummy')
def test_images(app):
app.build()
diff --git a/tests/test_environment_indexentries.py b/tests/test_environment_indexentries.py
index ec76acdc0..4083eae04 100644
--- a/tests/test_environment_indexentries.py
+++ b/tests/test_environment_indexentries.py
@@ -47,19 +47,30 @@ def test_create_pair_index(app):
app.env.indexentries.clear()
text = (".. index:: pair: docutils; reStructuredText\n"
".. index:: pair: Python; interpreter\n"
- ".. index:: pair: Sphinx; documentation tool\n")
+ ".. index:: pair: Sphinx; documentation tool\n"
+ ".. index:: pair: Sphinx; :+1:\n"
+ ".. index:: pair: Sphinx; Ель\n"
+ ".. index:: pair: Sphinx; ёлка\n")
restructuredtext.parse(app, text)
index = IndexEntries(app.env).create_index(app.builder)
- assert len(index) == 5
- assert index[0] == ('D',
+ assert len(index) == 7
+ assert index[0] == ('Symbols', [(':+1:', [[], [('Sphinx', [('', '#index-3')])], None])])
+ assert index[1] == ('D',
[('documentation tool', [[], [('Sphinx', [('', '#index-2')])], None]),
('docutils', [[], [('reStructuredText', [('', '#index-0')])], None])])
- assert index[1] == ('I', [('interpreter', [[], [('Python', [('', '#index-1')])], None])])
- assert index[2] == ('P', [('Python', [[], [('interpreter', [('', '#index-1')])], None])])
- assert index[3] == ('R',
+ assert index[2] == ('I', [('interpreter', [[], [('Python', [('', '#index-1')])], None])])
+ assert index[3] == ('P', [('Python', [[], [('interpreter', [('', '#index-1')])], None])])
+ assert index[4] == ('R',
[('reStructuredText', [[], [('docutils', [('', '#index-0')])], None])])
- assert index[4] == ('S',
- [('Sphinx', [[], [('documentation tool', [('', '#index-2')])], None])])
+ assert index[5] == ('S',
+ [('Sphinx', [[],
+ [(':+1:', [('', '#index-3')]),
+ ('documentation tool', [('', '#index-2')]),
+ ('ёлка', [('', '#index-5')]),
+ ('Ель', [('', '#index-4')])],
+ None])])
+ assert index[6] == ('Е', [('ёлка', [[], [('Sphinx', [('', '#index-5')])], None]),
+ ('Ель', [[], [('Sphinx', [('', '#index-4')])], None])])
@pytest.mark.sphinx('dummy')
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index ae97d3b57..21841cc74 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -302,3 +302,17 @@ def test_generate_autosummary_docs_property(app):
".. currentmodule:: target.methods\n"
"\n"
".. autoproperty:: Base.prop")
+
+
+@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
+ confoverrides={'autosummary_generate': []})
+def test_empty_autosummary_generate(app, status, warning):
+ app.build()
+ assert ("WARNING: autosummary: stub file not found 'autosummary_importfail'"
+ in warning.getvalue())
+
+
+@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
+ confoverrides={'autosummary_generate': ['unknown']})
+def test_invalid_autosummary_generate(app, status, warning):
+ assert 'WARNING: autosummary_generate: file not found: unknown.rst' in warning.getvalue()
diff --git a/tests/test_ext_githubpages.py b/tests/test_ext_githubpages.py
index 450a25498..132d95e03 100644
--- a/tests/test_ext_githubpages.py
+++ b/tests/test_ext_githubpages.py
@@ -18,14 +18,16 @@ def test_githubpages(app, status, warning):
assert not (app.outdir / 'CNAME').exists()
-@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.github.io'})
+@pytest.mark.sphinx('html', testroot='ext-githubpages',
+ confoverrides={'html_baseurl': 'https://sphinx-doc.github.io'})
def test_no_cname_for_github_io_domain(app, status, warning):
app.builder.build_all()
assert (app.outdir / '.nojekyll').exists()
assert not (app.outdir / 'CNAME').exists()
-@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.org'})
+@pytest.mark.sphinx('html', testroot='ext-githubpages',
+ confoverrides={'html_baseurl': 'https://sphinx-doc.org'})
def test_cname_for_custom_domain(app, status, warning):
app.builder.build_all()
assert (app.outdir / '.nojekyll').exists()
diff --git a/tests/test_intl.py b/tests/test_intl.py
index a052266b8..3815f8357 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -107,7 +107,7 @@ def test_text_emit_warnings(app, warning):
# test warnings in translation
warnings = getwarning(warning)
warning_expr = ('.*/warnings.txt:4::1: '
- 'WARNING: Inline literal start-string without end-string.\n')
+ 'WARNING: Inline literal start-string without end-string.\n')
assert_re_search(warning_expr, warnings)
@@ -885,8 +885,8 @@ def test_xml_keep_external_links(app):
assert_elem(
para1[0],
['LINK TO', 'external2', 'AND', 'external1', '.'],
- ['http://example.com/external2',
- 'http://example.com/external1'])
+ ['https://www.google.com/external2',
+ 'https://www.google.com/external1'])
assert_elem(
para1[1],
['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'],
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 006d19caa..b8c9b66d9 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -279,9 +279,9 @@ def get_verifier(verify, verify_re):
(
# in URIs
'verify_re',
- '`test `_',
+ '`test `_',
None,
- r'\\sphinxhref{http://example.com/~me/}{test}.*',
+ r'\\sphinxhref{https://www.google.com/~me/}{test}.*',
),
(
# description list: simple
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index 22f8ded6f..0681352e3 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -13,7 +13,6 @@ import datetime
import functools
import sys
import types
-from textwrap import dedent
import pytest
diff --git a/tests/test_util_pycompat.py b/tests/test_util_pycompat.py
index 6ffc83f84..a5d743972 100644
--- a/tests/test_util_pycompat.py
+++ b/tests/test_util_pycompat.py
@@ -8,8 +8,6 @@
:license: BSD, see LICENSE for details.
"""
-import tempfile
-
from sphinx.testing.util import strip_escseq
from sphinx.util import logging
from sphinx.util.pycompat import execfile_