mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into 5592_cmdoption_registers_multiple_indices
This commit is contained in:
commit
fde3d2a1ed
32
CHANGES
32
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)
|
||||
=====================================
|
||||
|
||||
|
@ -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
|
||||
-----------
|
||||
|
||||
|
@ -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
|
||||
|
2
setup.py
2
setup.py
@ -47,7 +47,7 @@ extras_require = {
|
||||
'html5lib',
|
||||
'flake8>=3.5.0',
|
||||
'flake8-import-order',
|
||||
'mypy>=0.590',
|
||||
'mypy>=0.711',
|
||||
'docutils-stubs',
|
||||
],
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 <http://sphinx-doc.org/>.'),
|
||||
@ -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')
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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] <PROJECT_DIR>',
|
||||
epilog=__("For more information, visit <http://sphinx-doc.org/>."),
|
||||
@ -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')
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
@ -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)]
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 <name>."""
|
||||
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 <level> [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 <OUTPUT_PATH> <MODULE_PATH> '
|
||||
'[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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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__', {})
|
||||
|
@ -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']):
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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] <SOURCE_FILE>...',
|
||||
epilog=__('For more information, visit <http://sphinx-doc.org/>.'),
|
||||
@ -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')
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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*<BLANKLINE>', 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)
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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('<map id="(.*?)"')
|
||||
href_re = re.compile('href=".*?"')
|
||||
|
||||
def __init__(self, filename, content, dot=''):
|
||||
# type: (str, str, str) -> None
|
||||
def __init__(self, filename: str, content: str, dot: str = '') -> None:
|
||||
self.id = None # type: str
|
||||
self.filename = filename
|
||||
self.content = content.splitlines()
|
||||
@ -62,8 +57,7 @@ class ClickableMapDefinition:
|
||||
|
||||
self.parse(dot=dot)
|
||||
|
||||
def parse(self, dot=None):
|
||||
# type: (str) -> None
|
||||
def parse(self, dot: str = None) -> None:
|
||||
matched = self.maptag_re.match(self.content[0])
|
||||
if not matched:
|
||||
raise GraphvizError('Invalid clickable map file found: %s' % self.filename)
|
||||
@ -80,8 +74,7 @@ class ClickableMapDefinition:
|
||||
if self.href_re.search(line):
|
||||
self.clickable.append(line)
|
||||
|
||||
def generate_clickable_map(self):
|
||||
# type: () -> str
|
||||
def generate_clickable_map(self) -> str:
|
||||
"""Generate clickable map tags if clickable item exists.
|
||||
|
||||
If not exists, this only returns empty string.
|
||||
@ -96,8 +89,7 @@ class graphviz(nodes.General, nodes.Inline, nodes.Element):
|
||||
pass
|
||||
|
||||
|
||||
def figure_wrapper(directive, node, caption):
|
||||
# type: (Directive, graphviz, str) -> nodes.figure
|
||||
def figure_wrapper(directive: Directive, node: graphviz, caption: str) -> nodes.figure:
|
||||
figure_node = nodes.figure('', node)
|
||||
if 'align' in node:
|
||||
figure_node['align'] = node.attributes.pop('align')
|
||||
@ -110,8 +102,7 @@ def figure_wrapper(directive, node, caption):
|
||||
return figure_node
|
||||
|
||||
|
||||
def align_spec(argument):
|
||||
# type: (Any) -> str
|
||||
def align_spec(argument: Any) -> str:
|
||||
return directives.choice(argument, ('left', 'center', 'right'))
|
||||
|
||||
|
||||
@ -132,8 +123,7 @@ class Graphviz(SphinxDirective):
|
||||
'name': directives.unchanged,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
if self.arguments:
|
||||
document = self.state.document
|
||||
if self.content:
|
||||
@ -194,8 +184,7 @@ class GraphvizSimple(SphinxDirective):
|
||||
'name': directives.unchanged,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
node = graphviz()
|
||||
node['code'] = '%s %s {\n%s\n}\n' % \
|
||||
(self.name, self.arguments[0], '\n'.join(self.content))
|
||||
@ -216,8 +205,8 @@ class GraphvizSimple(SphinxDirective):
|
||||
return [figure]
|
||||
|
||||
|
||||
def render_dot(self, code, options, format, prefix='graphviz'):
|
||||
# type: (SphinxTranslator, str, Dict, str, str) -> Tuple[str, str]
|
||||
def render_dot(self: SphinxTranslator, code: str, options: Dict,
|
||||
format: str, prefix: str = 'graphviz') -> Tuple[str, str]:
|
||||
"""Render graphviz code into a PNG or PDF output file."""
|
||||
graphviz_dot = options.get('graphviz_dot', self.builder.config.graphviz_dot)
|
||||
hashkey = (code + str(options) + str(graphviz_dot) +
|
||||
@ -265,9 +254,9 @@ def render_dot(self, code, options, format, prefix='graphviz'):
|
||||
'[stdout]\n%r') % (exc.stderr, exc.stdout))
|
||||
|
||||
|
||||
def render_dot_html(self, node, code, options, prefix='graphviz',
|
||||
imgcls=None, alt=None):
|
||||
# type: (HTMLTranslator, graphviz, str, Dict, str, str, str) -> Tuple[str, str]
|
||||
def render_dot_html(self: HTMLTranslator, node: graphviz, code: str, options: Dict,
|
||||
prefix: str = 'graphviz', imgcls: str = None, alt: str = None
|
||||
) -> Tuple[str, str]:
|
||||
format = self.builder.config.graphviz_output_format
|
||||
try:
|
||||
if format not in ('png', 'svg'):
|
||||
@ -319,13 +308,12 @@ def render_dot_html(self, node, code, options, prefix='graphviz',
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def html_visit_graphviz(self, node):
|
||||
# type: (HTMLTranslator, graphviz) -> None
|
||||
def html_visit_graphviz(self: HTMLTranslator, node: graphviz) -> None:
|
||||
render_dot_html(self, node, node['code'], node['options'])
|
||||
|
||||
|
||||
def render_dot_latex(self, node, code, options, prefix='graphviz'):
|
||||
# type: (LaTeXTranslator, graphviz, str, Dict, str) -> None
|
||||
def render_dot_latex(self: LaTeXTranslator, node: graphviz, code: str,
|
||||
options: Dict, prefix: str = 'graphviz') -> None:
|
||||
try:
|
||||
fname, outfn = render_dot(self, code, options, 'pdf', prefix)
|
||||
except GraphvizError as exc:
|
||||
@ -357,13 +345,12 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def latex_visit_graphviz(self, node):
|
||||
# type: (LaTeXTranslator, graphviz) -> None
|
||||
def latex_visit_graphviz(self: LaTeXTranslator, node: graphviz) -> None:
|
||||
render_dot_latex(self, node, node['code'], node['options'])
|
||||
|
||||
|
||||
def render_dot_texinfo(self, node, code, options, prefix='graphviz'):
|
||||
# type: (TexinfoTranslator, graphviz, str, Dict, str) -> None
|
||||
def render_dot_texinfo(self: TexinfoTranslator, node: graphviz, code: str,
|
||||
options: Dict, prefix: str = 'graphviz') -> None:
|
||||
try:
|
||||
fname, outfn = render_dot(self, code, options, 'png', prefix)
|
||||
except GraphvizError as exc:
|
||||
@ -374,13 +361,11 @@ def render_dot_texinfo(self, node, code, options, prefix='graphviz'):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def texinfo_visit_graphviz(self, node):
|
||||
# type: (TexinfoTranslator, graphviz) -> None
|
||||
def texinfo_visit_graphviz(self: TexinfoTranslator, node: graphviz) -> None:
|
||||
render_dot_texinfo(self, node, node['code'], node['options'])
|
||||
|
||||
|
||||
def text_visit_graphviz(self, node):
|
||||
# type: (TextTranslator, graphviz) -> None
|
||||
def text_visit_graphviz(self: TextTranslator, node: graphviz) -> None:
|
||||
if 'alt' in node.attributes:
|
||||
self.add_text(_('[graph: %s]') % node['alt'])
|
||||
else:
|
||||
@ -388,8 +373,7 @@ def text_visit_graphviz(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def man_visit_graphviz(self, node):
|
||||
# type: (ManualPageTranslator, graphviz) -> None
|
||||
def man_visit_graphviz(self: ManualPageTranslator, node: graphviz) -> None:
|
||||
if 'alt' in node.attributes:
|
||||
self.body.append(_('[graph: %s]') % node['alt'])
|
||||
else:
|
||||
@ -397,16 +381,14 @@ def man_visit_graphviz(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def on_build_finished(app, exc):
|
||||
# type: (Sphinx, Exception) -> None
|
||||
def on_build_finished(app: Sphinx, exc: Exception) -> None:
|
||||
if exc is None:
|
||||
src = path.join(sphinx.package_dir, 'templates', 'graphviz', 'graphviz.css')
|
||||
dst = path.join(app.outdir, '_static')
|
||||
copy_asset(src, dst)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_node(graphviz,
|
||||
html=(html_visit_graphviz, None),
|
||||
latex=(latex_visit_graphviz, None),
|
||||
|
@ -19,17 +19,16 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
class ifconfig(nodes.Element):
|
||||
pass
|
||||
@ -43,8 +42,7 @@ class IfConfig(SphinxDirective):
|
||||
final_argument_whitespace = True
|
||||
option_spec = {} # type: Dict
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
node = ifconfig()
|
||||
node.document = self.state.document
|
||||
self.set_source_info(node)
|
||||
@ -53,8 +51,7 @@ class IfConfig(SphinxDirective):
|
||||
return [node]
|
||||
|
||||
|
||||
def process_ifconfig_nodes(app, doctree, docname):
|
||||
# type: (Sphinx, nodes.document, str) -> None
|
||||
def process_ifconfig_nodes(app: Sphinx, doctree: nodes.document, docname: str) -> None:
|
||||
ns = {confval.name: confval.value for confval in app.config}
|
||||
ns.update(app.config.__dict__.copy())
|
||||
ns['builder'] = app.builder.name
|
||||
@ -76,8 +73,7 @@ def process_ifconfig_nodes(app, doctree, docname):
|
||||
node.replace_self(node.children)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_node(ifconfig)
|
||||
app.add_directive('ifconfig', IfConfig)
|
||||
app.connect('doctree-resolved', process_ifconfig_nodes)
|
||||
|
@ -7,19 +7,17 @@
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
from subprocess import CalledProcessError, PIPE
|
||||
from typing import Any, Dict
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.locale import __
|
||||
from sphinx.transforms.post_transforms.images import ImageConverter
|
||||
from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -31,8 +29,7 @@ class ImagemagickConverter(ImageConverter):
|
||||
('application/pdf', 'image/png'),
|
||||
]
|
||||
|
||||
def is_available(self):
|
||||
# type: () -> bool
|
||||
def is_available(self) -> bool:
|
||||
"""Confirms the converter is available or not."""
|
||||
try:
|
||||
args = [self.config.image_converter, '-version']
|
||||
@ -50,8 +47,7 @@ class ImagemagickConverter(ImageConverter):
|
||||
exc.stderr, exc.stdout)
|
||||
return False
|
||||
|
||||
def convert(self, _from, _to):
|
||||
# type: (str, str) -> bool
|
||||
def convert(self, _from: str, _to: str) -> bool:
|
||||
"""Converts the image to expected one."""
|
||||
try:
|
||||
# append an index 0 to source filename to pick up the first frame
|
||||
@ -75,8 +71,7 @@ class ImagemagickConverter(ImageConverter):
|
||||
(exc.stderr, exc.stdout))
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_post_transform(ImagemagickConverter)
|
||||
app.add_config_value('image_converter', 'convert', 'env')
|
||||
app.add_config_value('image_converter_args', [], 'env')
|
||||
|
@ -17,11 +17,16 @@ import tempfile
|
||||
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.nodes import Element
|
||||
|
||||
import sphinx
|
||||
from sphinx import package_dir
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.config import Config
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import _, __
|
||||
@ -30,14 +35,7 @@ from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
||||
from sphinx.util.osutil import ensuredir
|
||||
from sphinx.util.png import read_png_depth, write_png_depth
|
||||
from sphinx.util.template import LaTeXRenderer
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List, Tuple, Union # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.builders import Builder # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -47,8 +45,7 @@ templates_path = path.join(package_dir, 'templates', 'imgmath')
|
||||
class MathExtError(SphinxError):
|
||||
category = 'Math extension error'
|
||||
|
||||
def __init__(self, msg, stderr=None, stdout=None):
|
||||
# type: (str, bytes, bytes) -> None
|
||||
def __init__(self, msg: str, stderr: bytes = None, stdout: bytes = None) -> None:
|
||||
if stderr:
|
||||
msg += '\n[stderr]\n' + stderr.decode(sys.getdefaultencoding(), 'replace')
|
||||
if stdout:
|
||||
@ -92,8 +89,7 @@ DOC_BODY_PREVIEW = r'''
|
||||
depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]')
|
||||
|
||||
|
||||
def generate_latex_macro(math, config, confdir=''):
|
||||
# type: (str, Config, str) -> str
|
||||
def generate_latex_macro(math: str, config: Config, confdir: str = '') -> str:
|
||||
"""Generate LaTeX macro."""
|
||||
variables = {
|
||||
'fontsize': config.imgmath_font_size,
|
||||
@ -115,8 +111,7 @@ def generate_latex_macro(math, config, confdir=''):
|
||||
return LaTeXRenderer(templates_path).render(template_name, variables)
|
||||
|
||||
|
||||
def ensure_tempdir(builder):
|
||||
# type: (Builder) -> str
|
||||
def ensure_tempdir(builder: Builder) -> str:
|
||||
"""Create temporary directory.
|
||||
|
||||
use only one tempdir per build -- the use of a directory is cleaner
|
||||
@ -129,8 +124,7 @@ def ensure_tempdir(builder):
|
||||
return builder._imgmath_tempdir # type: ignore
|
||||
|
||||
|
||||
def compile_math(latex, builder):
|
||||
# type: (str, Builder) -> str
|
||||
def compile_math(latex: str, builder: Builder) -> str:
|
||||
"""Compile LaTeX macros for math to DVI."""
|
||||
tempdir = ensure_tempdir(builder)
|
||||
filename = path.join(tempdir, 'math.tex')
|
||||
@ -157,8 +151,7 @@ def compile_math(latex, builder):
|
||||
raise MathExtError('latex exited with error', exc.stderr, exc.stdout)
|
||||
|
||||
|
||||
def convert_dvi_to_image(command, name):
|
||||
# type: (List[str], str) -> Tuple[bytes, bytes]
|
||||
def convert_dvi_to_image(command: List[str], name: str) -> Tuple[bytes, bytes]:
|
||||
"""Convert DVI file to specific image format."""
|
||||
try:
|
||||
ret = subprocess.run(command, stdout=PIPE, stderr=PIPE, check=True)
|
||||
@ -172,8 +165,7 @@ def convert_dvi_to_image(command, name):
|
||||
raise MathExtError('%s exited with error' % name, exc.stderr, exc.stdout)
|
||||
|
||||
|
||||
def convert_dvi_to_png(dvipath, builder):
|
||||
# type: (str, Builder) -> Tuple[str, int]
|
||||
def convert_dvi_to_png(dvipath: str, builder: Builder) -> Tuple[str, int]:
|
||||
"""Convert DVI file to PNG image."""
|
||||
tempdir = ensure_tempdir(builder)
|
||||
filename = path.join(tempdir, 'math.png')
|
||||
@ -199,8 +191,7 @@ def convert_dvi_to_png(dvipath, builder):
|
||||
return filename, depth
|
||||
|
||||
|
||||
def convert_dvi_to_svg(dvipath, builder):
|
||||
# type: (str, Builder) -> Tuple[str, int]
|
||||
def convert_dvi_to_svg(dvipath: str, builder: Builder) -> Tuple[str, int]:
|
||||
"""Convert DVI file to SVG image."""
|
||||
tempdir = ensure_tempdir(builder)
|
||||
filename = path.join(tempdir, 'math.svg')
|
||||
@ -214,8 +205,7 @@ def convert_dvi_to_svg(dvipath, builder):
|
||||
return filename, None
|
||||
|
||||
|
||||
def render_math(self, math):
|
||||
# type: (HTMLTranslator, str) -> Tuple[str, int]
|
||||
def render_math(self: HTMLTranslator, math: str) -> Tuple[str, int]:
|
||||
"""Render the LaTeX math expression *math* using latex and dvipng or
|
||||
dvisvgm.
|
||||
|
||||
@ -271,8 +261,7 @@ def render_math(self, math):
|
||||
return relfn, depth
|
||||
|
||||
|
||||
def cleanup_tempdir(app, exc):
|
||||
# type: (Sphinx, Exception) -> None
|
||||
def cleanup_tempdir(app: Sphinx, exc: Exception) -> None:
|
||||
if exc:
|
||||
return
|
||||
if not hasattr(app.builder, '_imgmath_tempdir'):
|
||||
@ -283,15 +272,13 @@ def cleanup_tempdir(app, exc):
|
||||
pass
|
||||
|
||||
|
||||
def get_tooltip(self, node):
|
||||
# type: (HTMLTranslator, Union[nodes.math, nodes.math_block]) -> str
|
||||
def get_tooltip(self: HTMLTranslator, node: Element) -> str:
|
||||
if self.builder.config.imgmath_add_tooltips:
|
||||
return ' alt="%s"' % self.encode(node.astext()).strip()
|
||||
return ''
|
||||
|
||||
|
||||
def html_visit_math(self, node):
|
||||
# type: (HTMLTranslator, nodes.math) -> None
|
||||
def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
|
||||
try:
|
||||
fname, depth = render_math(self, '$' + node.astext() + '$')
|
||||
except MathExtError as exc:
|
||||
@ -313,8 +300,7 @@ def html_visit_math(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def html_visit_displaymath(self, node):
|
||||
# type: (HTMLTranslator, nodes.math_block) -> None
|
||||
def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None:
|
||||
if node['nowrap']:
|
||||
latex = node.astext()
|
||||
else:
|
||||
@ -354,8 +340,7 @@ deprecated_alias('sphinx.ext.imgmath',
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_html_math_renderer('imgmath',
|
||||
(html_visit_math, None),
|
||||
(html_visit_displaymath, None))
|
||||
|
@ -40,27 +40,25 @@ import inspect
|
||||
import re
|
||||
import sys
|
||||
from hashlib import md5
|
||||
from typing import Iterable, cast
|
||||
from typing import Any, Dict, Iterable, List, Tuple
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
import sphinx
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.ext.graphviz import (
|
||||
graphviz, figure_wrapper,
|
||||
render_dot_html, render_dot_latex, render_dot_texinfo
|
||||
)
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List, Optional, Tuple # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator # NOQA
|
||||
from sphinx.writers.latex import LaTeXTranslator # NOQA
|
||||
from sphinx.writers.texinfo import TexinfoTranslator # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
from sphinx.writers.texinfo import TexinfoTranslator
|
||||
|
||||
|
||||
module_sig_re = re.compile(r'''^(?:([\w.]*)\.)? # module names
|
||||
@ -68,8 +66,7 @@ module_sig_re = re.compile(r'''^(?:([\w.]*)\.)? # module names
|
||||
''', re.VERBOSE)
|
||||
|
||||
|
||||
def try_import(objname):
|
||||
# type: (str) -> Any
|
||||
def try_import(objname: str) -> Any:
|
||||
"""Import a object or module using *name* and *currentmodule*.
|
||||
*name* should be a relative name from *currentmodule* or
|
||||
a fully-qualified name.
|
||||
@ -96,8 +93,7 @@ def try_import(objname):
|
||||
return None
|
||||
|
||||
|
||||
def import_classes(name, currmodule):
|
||||
# type: (str, str) -> Any
|
||||
def import_classes(name: str, currmodule: str) -> Any:
|
||||
"""Import a class using its fully-qualified *name*."""
|
||||
target = None
|
||||
|
||||
@ -138,9 +134,9 @@ class InheritanceGraph:
|
||||
from all the way to the root "object", and then is able to generate a
|
||||
graphviz dot graph from them.
|
||||
"""
|
||||
def __init__(self, class_names, currmodule, show_builtins=False,
|
||||
private_bases=False, parts=0, aliases=None, top_classes=[]):
|
||||
# type: (List[str], str, bool, bool, int, Optional[Dict[str, str]], List[Any]) -> None
|
||||
def __init__(self, class_names: List[str], currmodule: str, show_builtins: bool = False,
|
||||
private_bases: bool = False, parts: int = 0, aliases: Dict[str, str] = None,
|
||||
top_classes: List[Any] = []) -> None:
|
||||
"""*class_names* is a list of child classes to show bases from.
|
||||
|
||||
If *show_builtins* is True, then Python builtins will be shown
|
||||
@ -154,16 +150,16 @@ class InheritanceGraph:
|
||||
raise InheritanceException('No classes found for '
|
||||
'inheritance diagram')
|
||||
|
||||
def _import_classes(self, class_names, currmodule):
|
||||
# type: (List[str], str) -> List[Any]
|
||||
def _import_classes(self, class_names: List[str], currmodule: str) -> List[Any]:
|
||||
"""Import a list of classes."""
|
||||
classes = [] # type: List[Any]
|
||||
for name in class_names:
|
||||
classes.extend(import_classes(name, currmodule))
|
||||
return classes
|
||||
|
||||
def _class_info(self, classes, show_builtins, private_bases, parts, aliases, top_classes):
|
||||
# type: (List[Any], bool, bool, int, Optional[Dict[str, str]], List[Any]) -> List[Tuple[str, str, List[str], str]] # NOQA
|
||||
def _class_info(self, classes: List[Any], show_builtins: bool, private_bases: bool,
|
||||
parts: int, aliases: Dict[str, str], top_classes: List[Any]
|
||||
) -> List[Tuple[str, str, List[str], str]]:
|
||||
"""Return name and bases for all classes that are ancestors of
|
||||
*classes*.
|
||||
|
||||
@ -182,8 +178,7 @@ class InheritanceGraph:
|
||||
all_classes = {}
|
||||
py_builtins = vars(builtins).values()
|
||||
|
||||
def recurse(cls):
|
||||
# type: (Any) -> None
|
||||
def recurse(cls: Any) -> None:
|
||||
if not show_builtins and cls in py_builtins:
|
||||
return
|
||||
if not private_bases and cls.__name__.startswith('_'):
|
||||
@ -222,8 +217,7 @@ class InheritanceGraph:
|
||||
|
||||
return list(all_classes.values())
|
||||
|
||||
def class_name(self, cls, parts=0, aliases=None):
|
||||
# type: (Any, int, Optional[Dict[str, str]]) -> str
|
||||
def class_name(self, cls: Any, parts: int = 0, aliases: Dict[str, str] = None) -> str:
|
||||
"""Given a class object, return a fully-qualified name.
|
||||
|
||||
This works for things I've tested in matplotlib so far, but may not be
|
||||
@ -243,8 +237,7 @@ class InheritanceGraph:
|
||||
return aliases[result]
|
||||
return result
|
||||
|
||||
def get_all_class_names(self):
|
||||
# type: () -> List[str]
|
||||
def get_all_class_names(self) -> List[str]:
|
||||
"""Get all of the class names involved in the graph."""
|
||||
return [fullname for (_, fullname, _, _) in self.class_info]
|
||||
|
||||
@ -266,17 +259,15 @@ class InheritanceGraph:
|
||||
'style': '"setlinewidth(0.5)"',
|
||||
}
|
||||
|
||||
def _format_node_attrs(self, attrs):
|
||||
# type: (Dict) -> str
|
||||
def _format_node_attrs(self, attrs: Dict) -> str:
|
||||
return ','.join(['%s=%s' % x for x in sorted(attrs.items())])
|
||||
|
||||
def _format_graph_attrs(self, attrs):
|
||||
# type: (Dict) -> str
|
||||
def _format_graph_attrs(self, attrs: Dict) -> str:
|
||||
return ''.join(['%s=%s;\n' % x for x in sorted(attrs.items())])
|
||||
|
||||
def generate_dot(self, name, urls={}, env=None,
|
||||
graph_attrs={}, node_attrs={}, edge_attrs={}):
|
||||
# type: (str, Dict, BuildEnvironment, Dict, Dict, Dict) -> str
|
||||
def generate_dot(self, name: str, urls: Dict = {}, env: BuildEnvironment = None,
|
||||
graph_attrs: Dict = {}, node_attrs: Dict = {}, edge_attrs: Dict = {}
|
||||
) -> str:
|
||||
"""Generate a graphviz dot graph from the classes that were passed in
|
||||
to __init__.
|
||||
|
||||
@ -344,8 +335,7 @@ class InheritanceDiagram(SphinxDirective):
|
||||
'top-classes': directives.unchanged_required,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
node = inheritance_diagram()
|
||||
node.document = self.state.document
|
||||
class_names = self.arguments[0].split()
|
||||
@ -391,14 +381,12 @@ class InheritanceDiagram(SphinxDirective):
|
||||
return [figure]
|
||||
|
||||
|
||||
def get_graph_hash(node):
|
||||
# type: (inheritance_diagram) -> str
|
||||
def get_graph_hash(node: inheritance_diagram) -> str:
|
||||
encoded = (node['content'] + str(node['parts'])).encode()
|
||||
return md5(encoded).hexdigest()[-10:]
|
||||
|
||||
|
||||
def html_visit_inheritance_diagram(self, node):
|
||||
# type: (HTMLTranslator, inheritance_diagram) -> None
|
||||
def html_visit_inheritance_diagram(self: HTMLTranslator, node: inheritance_diagram) -> None:
|
||||
"""
|
||||
Output the graph for HTML. This will insert a PNG with clickable
|
||||
image map.
|
||||
@ -431,8 +419,7 @@ def html_visit_inheritance_diagram(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def latex_visit_inheritance_diagram(self, node):
|
||||
# type: (LaTeXTranslator, inheritance_diagram) -> None
|
||||
def latex_visit_inheritance_diagram(self: LaTeXTranslator, node: inheritance_diagram) -> None:
|
||||
"""
|
||||
Output the graph for LaTeX. This will insert a PDF.
|
||||
"""
|
||||
@ -447,8 +434,8 @@ def latex_visit_inheritance_diagram(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def texinfo_visit_inheritance_diagram(self, node):
|
||||
# type: (TexinfoTranslator, inheritance_diagram) -> None
|
||||
def texinfo_visit_inheritance_diagram(self: TexinfoTranslator, node: inheritance_diagram
|
||||
) -> None:
|
||||
"""
|
||||
Output the graph for Texinfo. This will insert a PNG.
|
||||
"""
|
||||
@ -463,13 +450,11 @@ def texinfo_visit_inheritance_diagram(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def skip(self, node):
|
||||
# type: (nodes.NodeVisitor, inheritance_diagram) -> None
|
||||
def skip(self: nodes.NodeVisitor, node: inheritance_diagram) -> None:
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.setup_extension('sphinx.ext.graphviz')
|
||||
app.add_node(
|
||||
inheritance_diagram,
|
||||
|
@ -28,24 +28,23 @@ import posixpath
|
||||
import sys
|
||||
import time
|
||||
from os import path
|
||||
from typing import Any, Dict, IO, List, Tuple
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, TextElement
|
||||
from docutils.utils import relative_path
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders.html import INVENTORY_FILENAME
|
||||
from sphinx.config import Config
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.util import requests, logging
|
||||
from sphinx.util.inventory import InventoryFile
|
||||
from sphinx.util.typing import Inventory
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, IO, List, Tuple # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
from sphinx.util.typing import Inventory # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -53,8 +52,7 @@ logger = logging.getLogger(__name__)
|
||||
class InventoryAdapter:
|
||||
"""Inventory adapter for environment"""
|
||||
|
||||
def __init__(self, env):
|
||||
# type: (BuildEnvironment) -> None
|
||||
def __init__(self, env: BuildEnvironment) -> None:
|
||||
self.env = env
|
||||
|
||||
if not hasattr(env, 'intersphinx_cache'):
|
||||
@ -63,28 +61,23 @@ class InventoryAdapter:
|
||||
self.env.intersphinx_named_inventory = {} # type: ignore
|
||||
|
||||
@property
|
||||
def cache(self):
|
||||
# type: () -> Dict[str, Tuple[str, int, Inventory]]
|
||||
def cache(self) -> Dict[str, Tuple[str, int, Inventory]]:
|
||||
return self.env.intersphinx_cache # type: ignore
|
||||
|
||||
@property
|
||||
def main_inventory(self):
|
||||
# type: () -> Inventory
|
||||
def main_inventory(self) -> Inventory:
|
||||
return self.env.intersphinx_inventory # type: ignore
|
||||
|
||||
@property
|
||||
def named_inventory(self):
|
||||
# type: () -> Dict[str, Inventory]
|
||||
def named_inventory(self) -> Dict[str, Inventory]:
|
||||
return self.env.intersphinx_named_inventory # type: ignore
|
||||
|
||||
def clear(self):
|
||||
# type: () -> None
|
||||
def clear(self) -> None:
|
||||
self.env.intersphinx_inventory.clear() # type: ignore
|
||||
self.env.intersphinx_named_inventory.clear() # type: ignore
|
||||
|
||||
|
||||
def _strip_basic_auth(url):
|
||||
# type: (str) -> str
|
||||
def _strip_basic_auth(url: str) -> str:
|
||||
"""Returns *url* with basic auth credentials removed. Also returns the
|
||||
basic auth username and password if they're present in *url*.
|
||||
|
||||
@ -105,8 +98,7 @@ def _strip_basic_auth(url):
|
||||
return urlunsplit(frags)
|
||||
|
||||
|
||||
def _read_from_url(url, config=None):
|
||||
# type: (str, Config) -> IO
|
||||
def _read_from_url(url: str, config: Config = None) -> IO:
|
||||
"""Reads data from *url* with an HTTP *GET*.
|
||||
|
||||
This function supports fetching from resources which use basic HTTP auth as
|
||||
@ -131,8 +123,7 @@ def _read_from_url(url, config=None):
|
||||
return r.raw
|
||||
|
||||
|
||||
def _get_safe_url(url):
|
||||
# type: (str) -> str
|
||||
def _get_safe_url(url: str) -> str:
|
||||
"""Gets version of *url* with basic auth passwords obscured. This function
|
||||
returns results suitable for printing and logging.
|
||||
|
||||
@ -157,8 +148,7 @@ def _get_safe_url(url):
|
||||
return urlunsplit(frags)
|
||||
|
||||
|
||||
def fetch_inventory(app, uri, inv):
|
||||
# type: (Sphinx, str, Any) -> Any
|
||||
def fetch_inventory(app: Sphinx, uri: str, inv: Any) -> Any:
|
||||
"""Fetch, parse and return an intersphinx inventory file."""
|
||||
# both *uri* (base URI of the links to generate) and *inv* (actual
|
||||
# location of the inventory file) can be local or remote URIs
|
||||
@ -197,8 +187,7 @@ def fetch_inventory(app, uri, inv):
|
||||
return invdata
|
||||
|
||||
|
||||
def load_mappings(app):
|
||||
# type: (Sphinx) -> None
|
||||
def load_mappings(app: Sphinx) -> None:
|
||||
"""Load all intersphinx mappings into the environment."""
|
||||
now = int(time.time())
|
||||
cache_time = now - app.config.intersphinx_cache_limit * 86400
|
||||
@ -258,8 +247,8 @@ def load_mappings(app):
|
||||
inventories.main_inventory.setdefault(type, {}).update(objects)
|
||||
|
||||
|
||||
def missing_reference(app, env, node, contnode):
|
||||
# type: (Sphinx, BuildEnvironment, nodes.Element, nodes.TextElement) -> nodes.reference
|
||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: TextElement
|
||||
) -> nodes.reference:
|
||||
"""Attempt to resolve a missing reference via intersphinx references."""
|
||||
target = node['reftarget']
|
||||
inventories = InventoryAdapter(env)
|
||||
@ -336,8 +325,7 @@ def missing_reference(app, env, node, contnode):
|
||||
return None
|
||||
|
||||
|
||||
def normalize_intersphinx_mapping(app, config):
|
||||
# type: (Sphinx, Config) -> None
|
||||
def normalize_intersphinx_mapping(app: Sphinx, config: Config) -> None:
|
||||
for key, value in config.intersphinx_mapping.copy().items():
|
||||
try:
|
||||
if isinstance(value, (list, tuple)):
|
||||
@ -361,8 +349,7 @@ def normalize_intersphinx_mapping(app, config):
|
||||
config.intersphinx_mapping.pop(key)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_config_value('intersphinx_mapping', {}, True)
|
||||
app.add_config_value('intersphinx_cache_limit', 5, False)
|
||||
app.add_config_value('intersphinx_timeout', None, False)
|
||||
@ -376,8 +363,7 @@ def setup(app):
|
||||
}
|
||||
|
||||
|
||||
def inspect_main(argv):
|
||||
# type: (List[str]) -> None
|
||||
def inspect_main(argv: List[str]) -> None:
|
||||
"""Debug functionality to print out an inventory"""
|
||||
if len(argv) < 1:
|
||||
print("Print out an inventory file.\n"
|
||||
@ -393,8 +379,7 @@ def inspect_main(argv):
|
||||
srcdir = ''
|
||||
config = MockConfig()
|
||||
|
||||
def warn(self, msg):
|
||||
# type: (str) -> None
|
||||
def warn(self, msg: str) -> None:
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
try:
|
||||
|
@ -10,6 +10,7 @@
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict
|
||||
|
||||
from sphinxcontrib.jsmath import ( # NOQA
|
||||
html_visit_math,
|
||||
@ -18,16 +19,11 @@ from sphinxcontrib.jsmath import ( # NOQA
|
||||
)
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
warnings.warn('sphinx.ext.jsmath has been moved to sphinxcontrib-jsmath.',
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
|
@ -8,25 +8,23 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
|
||||
import sphinx
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import _
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, Set # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
class LinkcodeError(SphinxError):
|
||||
category = "linkcode error"
|
||||
|
||||
|
||||
def doctree_read(app, doctree):
|
||||
# type: (Sphinx, nodes.Node) -> None
|
||||
def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||||
env = app.builder.env
|
||||
|
||||
resolve_target = getattr(env.config, 'linkcode_resolve', None)
|
||||
@ -75,8 +73,7 @@ def doctree_read(app, doctree):
|
||||
signode += onlynode
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.connect('doctree-read', doctree_read)
|
||||
app.add_config_value('linkcode_resolve', None, '')
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
@ -9,26 +9,24 @@
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from typing import Callable, List, Tuple
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst.roles import math_role as math_role_base
|
||||
|
||||
from sphinx.addnodes import math, math_block as displaymath # NOQA # to keep compatibility
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders.latex.nodes import math_reference as eqref # NOQA # to keep compatibility
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.directives.patches import MathDirective as MathDirectiveBase
|
||||
from sphinx.domains.math import MathDomain # NOQA # to keep compatibility
|
||||
from sphinx.domains.math import MathReferenceRole as EqXRefRole # NOQA # to keep compatibility
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Callable, Tuple # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
|
||||
|
||||
class MathDirective(MathDirectiveBase):
|
||||
def run(self):
|
||||
def run(self) -> List[Node]:
|
||||
warnings.warn('sphinx.ext.mathbase.MathDirective is moved to '
|
||||
'sphinx.directives.patches package.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
@ -42,8 +40,7 @@ def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return math_role_base(role, rawtext, text, lineno, inliner, options, content)
|
||||
|
||||
|
||||
def get_node_equation_number(writer, node):
|
||||
# type: (HTMLTranslator, nodes.math_block) -> str
|
||||
def get_node_equation_number(writer: HTMLTranslator, node: nodes.math_block) -> str:
|
||||
warnings.warn('sphinx.ext.mathbase.get_node_equation_number() is moved to '
|
||||
'sphinx.util.math package.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
@ -51,8 +48,7 @@ def get_node_equation_number(writer, node):
|
||||
return get_node_equation_number(writer, node)
|
||||
|
||||
|
||||
def wrap_displaymath(text, label, numbering):
|
||||
# type: (str, str, bool) -> str
|
||||
def wrap_displaymath(text: str, label: str, numbering: bool) -> str:
|
||||
warnings.warn('sphinx.ext.mathbase.wrap_displaymath() is moved to '
|
||||
'sphinx.util.math package.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
@ -60,8 +56,7 @@ def wrap_displaymath(text, label, numbering):
|
||||
return wrap_displaymath(text, label, numbering)
|
||||
|
||||
|
||||
def is_in_section_title(node):
|
||||
# type: (nodes.Element) -> bool
|
||||
def is_in_section_title(node: Element) -> bool:
|
||||
"""Determine whether the node is in a section title"""
|
||||
from sphinx.util.nodes import traverse_parent
|
||||
|
||||
@ -75,8 +70,9 @@ def is_in_section_title(node):
|
||||
return False
|
||||
|
||||
|
||||
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
|
||||
# type: (Sphinx, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
|
||||
def setup_math(app: Sphinx,
|
||||
htmlinlinevisitors: Tuple[Callable, Callable],
|
||||
htmldisplayvisitors: Tuple[Callable, Callable]) -> None:
|
||||
warnings.warn('setup_math() is deprecated. '
|
||||
'Please use app.add_html_math_renderer() instead.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
|
@ -11,27 +11,23 @@
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any, Dict
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.domains.math import MathDomain
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.locale import _
|
||||
from sphinx.util.math import get_node_equation_number
|
||||
|
||||
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.writers.html import HTMLTranslator # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
|
||||
|
||||
def html_visit_math(self, node):
|
||||
# type: (HTMLTranslator, nodes.math) -> None
|
||||
def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight'))
|
||||
self.body.append(self.builder.config.mathjax_inline[0] +
|
||||
self.encode(node.astext()) +
|
||||
@ -39,8 +35,7 @@ def html_visit_math(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def html_visit_displaymath(self, node):
|
||||
# type: (HTMLTranslator, nodes.math_block) -> None
|
||||
def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None:
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight'))
|
||||
if node['nowrap']:
|
||||
self.body.append(self.encode(node.astext()))
|
||||
@ -72,8 +67,7 @@ def html_visit_displaymath(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def install_mathjax(app, env):
|
||||
# type: (Sphinx, BuildEnvironment) -> None
|
||||
def install_mathjax(app: Sphinx, env: BuildEnvironment) -> None:
|
||||
if app.builder.format != 'html' or app.builder.math_renderer_name != 'mathjax': # type: ignore # NOQA
|
||||
return
|
||||
if not app.config.mathjax_path:
|
||||
@ -94,8 +88,7 @@ def install_mathjax(app, env):
|
||||
builder.add_js_file(None, type="text/x-mathjax-config", body=body)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_html_math_renderer('mathjax',
|
||||
(html_visit_math, None),
|
||||
(html_visit_displaymath, None))
|
||||
|
@ -8,14 +8,12 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from sphinx import __display_version__ as __version__
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List # NOQA
|
||||
|
||||
|
||||
class Config:
|
||||
"""Sphinx napoleon extension settings in `conf.py`.
|
||||
@ -267,16 +265,14 @@ class Config:
|
||||
'napoleon_custom_sections': (None, 'env')
|
||||
}
|
||||
|
||||
def __init__(self, **settings):
|
||||
# type: (Any) -> None
|
||||
def __init__(self, **settings) -> None:
|
||||
for name, (default, rebuild) in self._config_values.items():
|
||||
setattr(self, name, default)
|
||||
for name, value in settings.items():
|
||||
setattr(self, name, value)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
"""Sphinx extension setup function.
|
||||
|
||||
When the extension is loaded, Sphinx imports this module and executes
|
||||
@ -313,8 +309,7 @@ def setup(app):
|
||||
return {'version': __version__, 'parallel_read_safe': True}
|
||||
|
||||
|
||||
def _patch_python_domain():
|
||||
# type: () -> None
|
||||
def _patch_python_domain() -> None:
|
||||
try:
|
||||
from sphinx.domains.python import PyTypedField
|
||||
except ImportError:
|
||||
@ -333,8 +328,8 @@ def _patch_python_domain():
|
||||
can_collapse=True))
|
||||
|
||||
|
||||
def _process_docstring(app, what, name, obj, options, lines):
|
||||
# type: (Sphinx, str, str, Any, Any, List[str]) -> None
|
||||
def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
|
||||
options: Any, lines: List[str]) -> None:
|
||||
"""Process the docstring for a given python object.
|
||||
|
||||
Called when autodoc has read and processed a docstring. `lines` is a list
|
||||
@ -383,8 +378,8 @@ def _process_docstring(app, what, name, obj, options, lines):
|
||||
lines[:] = result_lines[:]
|
||||
|
||||
|
||||
def _skip_member(app, what, name, obj, skip, options):
|
||||
# type: (Sphinx, str, str, Any, bool, Any) -> bool
|
||||
def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
|
||||
skip: bool, options: Any) -> bool:
|
||||
"""Determine if private and special class members are included in docs.
|
||||
|
||||
The following settings in conf.py determine if private and special class
|
||||
|
@ -13,16 +13,13 @@
|
||||
import inspect
|
||||
import re
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Dict, List, Tuple, Type, Union
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.config import Config as SphinxConfig
|
||||
from sphinx.ext.napoleon.iterators import modify_iter
|
||||
from sphinx.locale import _
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, List, Tuple, Type, Union # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.config import Config as SphinxConfig # NOQA
|
||||
|
||||
|
||||
_directive_regex = re.compile(r'\.\. \S+::')
|
||||
_google_section_regex = re.compile(r'^(\s|\w)+:\s*$')
|
||||
@ -103,9 +100,9 @@ class GoogleDocstring:
|
||||
_name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>[a-zA-Z0-9_.-]+)`|"
|
||||
r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
|
||||
|
||||
def __init__(self, docstring, config=None, app=None, what='', name='',
|
||||
obj=None, options=None):
|
||||
# type: (Union[str, List[str]], SphinxConfig, Sphinx, str, str, Any, Any) -> None
|
||||
def __init__(self, docstring: Union[str, List[str]], config: SphinxConfig = None,
|
||||
app: Sphinx = None, what: str = '', name: str = '',
|
||||
obj: Any = None, options: Any = None) -> None:
|
||||
self._config = config
|
||||
self._app = app
|
||||
|
||||
@ -175,8 +172,7 @@ class GoogleDocstring:
|
||||
|
||||
self._parse()
|
||||
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
def __str__(self) -> str:
|
||||
"""Return the parsed docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
@ -187,8 +183,7 @@ class GoogleDocstring:
|
||||
"""
|
||||
return '\n'.join(self.lines())
|
||||
|
||||
def lines(self):
|
||||
# type: () -> List[str]
|
||||
def lines(self) -> List[str]:
|
||||
"""Return the parsed lines of the docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
@ -199,8 +194,7 @@ class GoogleDocstring:
|
||||
"""
|
||||
return self._parsed_lines
|
||||
|
||||
def _consume_indented_block(self, indent=1):
|
||||
# type: (int) -> List[str]
|
||||
def _consume_indented_block(self, indent: int = 1) -> List[str]:
|
||||
lines = []
|
||||
line = self._line_iter.peek()
|
||||
while(not self._is_section_break() and
|
||||
@ -209,8 +203,7 @@ class GoogleDocstring:
|
||||
line = self._line_iter.peek()
|
||||
return lines
|
||||
|
||||
def _consume_contiguous(self):
|
||||
# type: () -> List[str]
|
||||
def _consume_contiguous(self) -> List[str]:
|
||||
lines = []
|
||||
while (self._line_iter.has_next() and
|
||||
self._line_iter.peek() and
|
||||
@ -218,8 +211,7 @@ class GoogleDocstring:
|
||||
lines.append(next(self._line_iter))
|
||||
return lines
|
||||
|
||||
def _consume_empty(self):
|
||||
# type: () -> List[str]
|
||||
def _consume_empty(self) -> List[str]:
|
||||
lines = []
|
||||
line = self._line_iter.peek()
|
||||
while self._line_iter.has_next() and not line:
|
||||
@ -227,8 +219,8 @@ class GoogleDocstring:
|
||||
line = self._line_iter.peek()
|
||||
return lines
|
||||
|
||||
def _consume_field(self, parse_type=True, prefer_type=False):
|
||||
# type: (bool, bool) -> Tuple[str, str, List[str]]
|
||||
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False
|
||||
) -> Tuple[str, str, List[str]]:
|
||||
line = next(self._line_iter)
|
||||
|
||||
before, colon, after = self._partition_field_on_colon(line)
|
||||
@ -249,8 +241,8 @@ class GoogleDocstring:
|
||||
_descs = self.__class__(_descs, self._config).lines()
|
||||
return _name, _type, _descs
|
||||
|
||||
def _consume_fields(self, parse_type=True, prefer_type=False):
|
||||
# type: (bool, bool) -> List[Tuple[str, str, List[str]]]
|
||||
def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False
|
||||
) -> List[Tuple[str, str, List[str]]]:
|
||||
self._consume_empty()
|
||||
fields = []
|
||||
while not self._is_section_break():
|
||||
@ -259,8 +251,7 @@ class GoogleDocstring:
|
||||
fields.append((_name, _type, _desc,))
|
||||
return fields
|
||||
|
||||
def _consume_inline_attribute(self):
|
||||
# type: () -> Tuple[str, List[str]]
|
||||
def _consume_inline_attribute(self) -> Tuple[str, List[str]]:
|
||||
line = next(self._line_iter)
|
||||
_type, colon, _desc = self._partition_field_on_colon(line)
|
||||
if not colon or not _desc:
|
||||
@ -270,8 +261,7 @@ class GoogleDocstring:
|
||||
_descs = self.__class__(_descs, self._config).lines()
|
||||
return _type, _descs
|
||||
|
||||
def _consume_returns_section(self):
|
||||
# type: () -> List[Tuple[str, str, List[str]]]
|
||||
def _consume_returns_section(self) -> List[Tuple[str, str, List[str]]]:
|
||||
lines = self._dedent(self._consume_to_next_section())
|
||||
if lines:
|
||||
before, colon, after = self._partition_field_on_colon(lines[0])
|
||||
@ -290,44 +280,38 @@ class GoogleDocstring:
|
||||
else:
|
||||
return []
|
||||
|
||||
def _consume_usage_section(self):
|
||||
# type: () -> List[str]
|
||||
def _consume_usage_section(self) -> List[str]:
|
||||
lines = self._dedent(self._consume_to_next_section())
|
||||
return lines
|
||||
|
||||
def _consume_section_header(self):
|
||||
# type: () -> str
|
||||
def _consume_section_header(self) -> str:
|
||||
section = next(self._line_iter)
|
||||
stripped_section = section.strip(':')
|
||||
if stripped_section.lower() in self._sections:
|
||||
section = stripped_section
|
||||
return section
|
||||
|
||||
def _consume_to_end(self):
|
||||
# type: () -> List[str]
|
||||
def _consume_to_end(self) -> List[str]:
|
||||
lines = []
|
||||
while self._line_iter.has_next():
|
||||
lines.append(next(self._line_iter))
|
||||
return lines
|
||||
|
||||
def _consume_to_next_section(self):
|
||||
# type: () -> List[str]
|
||||
def _consume_to_next_section(self) -> List[str]:
|
||||
self._consume_empty()
|
||||
lines = []
|
||||
while not self._is_section_break():
|
||||
lines.append(next(self._line_iter))
|
||||
return lines + self._consume_empty()
|
||||
|
||||
def _dedent(self, lines, full=False):
|
||||
# type: (List[str], bool) -> List[str]
|
||||
def _dedent(self, lines: List[str], full: bool = False) -> List[str]:
|
||||
if full:
|
||||
return [line.lstrip() for line in lines]
|
||||
else:
|
||||
min_indent = self._get_min_indent(lines)
|
||||
return [line[min_indent:] for line in lines]
|
||||
|
||||
def _escape_args_and_kwargs(self, name):
|
||||
# type: (str) -> str
|
||||
def _escape_args_and_kwargs(self, name: str) -> str:
|
||||
if name.endswith('_'):
|
||||
name = name[:-1] + r'\_'
|
||||
|
||||
@ -338,8 +322,7 @@ class GoogleDocstring:
|
||||
else:
|
||||
return name
|
||||
|
||||
def _fix_field_desc(self, desc):
|
||||
# type: (List[str]) -> List[str]
|
||||
def _fix_field_desc(self, desc: List[str]) -> List[str]:
|
||||
if self._is_list(desc):
|
||||
desc = [''] + desc
|
||||
elif desc[0].endswith('::'):
|
||||
@ -352,8 +335,7 @@ class GoogleDocstring:
|
||||
desc = ['', desc[0]] + self._indent(desc_block, 4)
|
||||
return desc
|
||||
|
||||
def _format_admonition(self, admonition, lines):
|
||||
# type: (str, List[str]) -> List[str]
|
||||
def _format_admonition(self, admonition: str, lines: List[str]) -> List[str]:
|
||||
lines = self._strip_empty(lines)
|
||||
if len(lines) == 1:
|
||||
return ['.. %s:: %s' % (admonition, lines[0].strip()), '']
|
||||
@ -363,8 +345,7 @@ class GoogleDocstring:
|
||||
else:
|
||||
return ['.. %s::' % admonition, '']
|
||||
|
||||
def _format_block(self, prefix, lines, padding=None):
|
||||
# type: (str, List[str], str) -> List[str]
|
||||
def _format_block(self, prefix: str, lines: List[str], padding: str = None) -> List[str]:
|
||||
if lines:
|
||||
if padding is None:
|
||||
padding = ' ' * len(prefix)
|
||||
@ -380,9 +361,9 @@ class GoogleDocstring:
|
||||
else:
|
||||
return [prefix]
|
||||
|
||||
def _format_docutils_params(self, fields, field_role='param',
|
||||
type_role='type'):
|
||||
# type: (List[Tuple[str, str, List[str]]], str, str) -> List[str]
|
||||
def _format_docutils_params(self, fields: List[Tuple[str, str, List[str]]],
|
||||
field_role: str = 'param', type_role: str = 'type'
|
||||
) -> List[str]:
|
||||
lines = []
|
||||
for _name, _type, _desc in fields:
|
||||
_desc = self._strip_empty(_desc)
|
||||
@ -397,8 +378,7 @@ class GoogleDocstring:
|
||||
lines.append(':%s %s: %s' % (type_role, _name, _type))
|
||||
return lines + ['']
|
||||
|
||||
def _format_field(self, _name, _type, _desc):
|
||||
# type: (str, str, List[str]) -> List[str]
|
||||
def _format_field(self, _name: str, _type: str, _desc: List[str]) -> List[str]:
|
||||
_desc = self._strip_empty(_desc)
|
||||
has_desc = any(_desc)
|
||||
separator = has_desc and ' -- ' or ''
|
||||
@ -427,8 +407,8 @@ class GoogleDocstring:
|
||||
else:
|
||||
return [field]
|
||||
|
||||
def _format_fields(self, field_type, fields):
|
||||
# type: (str, List[Tuple[str, str, List[str]]]) -> List[str]
|
||||
def _format_fields(self, field_type: str, fields: List[Tuple[str, str, List[str]]]
|
||||
) -> List[str]:
|
||||
field_type = ':%s:' % field_type.strip()
|
||||
padding = ' ' * len(field_type)
|
||||
multi = len(fields) > 1
|
||||
@ -446,8 +426,7 @@ class GoogleDocstring:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _get_current_indent(self, peek_ahead=0):
|
||||
# type: (int) -> int
|
||||
def _get_current_indent(self, peek_ahead: int = 0) -> int:
|
||||
line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
|
||||
while line != self._line_iter.sentinel:
|
||||
if line:
|
||||
@ -456,22 +435,19 @@ class GoogleDocstring:
|
||||
line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
|
||||
return 0
|
||||
|
||||
def _get_indent(self, line):
|
||||
# type: (str) -> int
|
||||
def _get_indent(self, line: str) -> int:
|
||||
for i, s in enumerate(line):
|
||||
if not s.isspace():
|
||||
return i
|
||||
return len(line)
|
||||
|
||||
def _get_initial_indent(self, lines):
|
||||
# type: (List[str]) -> int
|
||||
def _get_initial_indent(self, lines: List[str]) -> int:
|
||||
for line in lines:
|
||||
if line:
|
||||
return self._get_indent(line)
|
||||
return 0
|
||||
|
||||
def _get_min_indent(self, lines):
|
||||
# type: (List[str]) -> int
|
||||
def _get_min_indent(self, lines: List[str]) -> int:
|
||||
min_indent = None
|
||||
for line in lines:
|
||||
if line:
|
||||
@ -482,12 +458,10 @@ class GoogleDocstring:
|
||||
min_indent = indent
|
||||
return min_indent or 0
|
||||
|
||||
def _indent(self, lines, n=4):
|
||||
# type: (List[str], int) -> List[str]
|
||||
def _indent(self, lines: List[str], n: int = 4) -> List[str]:
|
||||
return [(' ' * n) + line for line in lines]
|
||||
|
||||
def _is_indented(self, line, indent=1):
|
||||
# type: (str, int) -> bool
|
||||
def _is_indented(self, line: str, indent: int = 1) -> bool:
|
||||
for i, s in enumerate(line):
|
||||
if i >= indent:
|
||||
return True
|
||||
@ -495,8 +469,7 @@ class GoogleDocstring:
|
||||
return False
|
||||
return False
|
||||
|
||||
def _is_list(self, lines):
|
||||
# type: (List[str]) -> bool
|
||||
def _is_list(self, lines: List[str]) -> bool:
|
||||
if not lines:
|
||||
return False
|
||||
if _bullet_list_regex.match(lines[0]):
|
||||
@ -513,8 +486,7 @@ class GoogleDocstring:
|
||||
break
|
||||
return next_indent > indent
|
||||
|
||||
def _is_section_header(self):
|
||||
# type: () -> bool
|
||||
def _is_section_header(self) -> bool:
|
||||
section = self._line_iter.peek().lower()
|
||||
match = _google_section_regex.match(section)
|
||||
if match and section.strip(':') in self._sections:
|
||||
@ -528,8 +500,7 @@ class GoogleDocstring:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_section_break(self):
|
||||
# type: () -> bool
|
||||
def _is_section_break(self) -> bool:
|
||||
line = self._line_iter.peek()
|
||||
return (not self._line_iter.has_next() or
|
||||
self._is_section_header() or
|
||||
@ -537,9 +508,7 @@ class GoogleDocstring:
|
||||
line and
|
||||
not self._is_indented(line, self._section_indent)))
|
||||
|
||||
def _load_custom_sections(self):
|
||||
# type: () -> None
|
||||
|
||||
def _load_custom_sections(self) -> None:
|
||||
if self._config.napoleon_custom_sections is not None:
|
||||
for entry in self._config.napoleon_custom_sections:
|
||||
if isinstance(entry, str):
|
||||
@ -554,8 +523,7 @@ class GoogleDocstring:
|
||||
self._sections.get(entry[1].lower(),
|
||||
self._parse_custom_generic_section)
|
||||
|
||||
def _parse(self):
|
||||
# type: () -> None
|
||||
def _parse(self) -> None:
|
||||
self._parsed_lines = self._consume_empty()
|
||||
|
||||
if self._name and self._what in ('attribute', 'data', 'property'):
|
||||
@ -594,16 +562,14 @@ class GoogleDocstring:
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition(admonition, lines)
|
||||
|
||||
def _parse_attribute_docstring(self):
|
||||
# type: () -> List[str]
|
||||
def _parse_attribute_docstring(self) -> List[str]:
|
||||
_type, _desc = self._consume_inline_attribute()
|
||||
lines = self._format_field('', '', _desc)
|
||||
if _type:
|
||||
lines.extend(['', ':type: %s' % _type])
|
||||
return lines
|
||||
|
||||
def _parse_attributes_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_attributes_section(self, section: str) -> List[str]:
|
||||
lines = []
|
||||
for _name, _type, _desc in self._consume_fields():
|
||||
if self._config.napoleon_use_ivar:
|
||||
@ -624,8 +590,7 @@ class GoogleDocstring:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_examples_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_examples_section(self, section: str) -> List[str]:
|
||||
labels = {
|
||||
'example': _('Example'),
|
||||
'examples': _('Examples'),
|
||||
@ -638,16 +603,14 @@ class GoogleDocstring:
|
||||
# for now, no admonition for simple custom sections
|
||||
return self._parse_generic_section(section, False)
|
||||
|
||||
def _parse_usage_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_usage_section(self, section: str) -> List[str]:
|
||||
header = ['.. rubric:: Usage:', '']
|
||||
block = ['.. code-block:: python', '']
|
||||
lines = self._consume_usage_section()
|
||||
lines = self._indent(lines, 3)
|
||||
return header + block + lines + ['']
|
||||
|
||||
def _parse_generic_section(self, section, use_admonition):
|
||||
# type: (str, bool) -> List[str]
|
||||
def _parse_generic_section(self, section: str, use_admonition: bool) -> List[str]:
|
||||
lines = self._strip_empty(self._consume_to_next_section())
|
||||
lines = self._dedent(lines)
|
||||
if use_admonition:
|
||||
@ -660,8 +623,7 @@ class GoogleDocstring:
|
||||
else:
|
||||
return [header, '']
|
||||
|
||||
def _parse_keyword_arguments_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_keyword_arguments_section(self, section: str) -> List[str]:
|
||||
fields = self._consume_fields()
|
||||
if self._config.napoleon_use_keyword:
|
||||
return self._format_docutils_params(
|
||||
@ -671,8 +633,7 @@ class GoogleDocstring:
|
||||
else:
|
||||
return self._format_fields(_('Keyword Arguments'), fields)
|
||||
|
||||
def _parse_methods_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_methods_section(self, section: str) -> List[str]:
|
||||
lines = [] # type: List[str]
|
||||
for _name, _type, _desc in self._consume_fields(parse_type=False):
|
||||
lines.append('.. method:: %s' % _name)
|
||||
@ -681,25 +642,21 @@ class GoogleDocstring:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_notes_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_notes_section(self, section: str) -> List[str]:
|
||||
use_admonition = self._config.napoleon_use_admonition_for_notes
|
||||
return self._parse_generic_section(_('Notes'), use_admonition)
|
||||
|
||||
def _parse_other_parameters_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_other_parameters_section(self, section: str) -> List[str]:
|
||||
return self._format_fields(_('Other Parameters'), self._consume_fields())
|
||||
|
||||
def _parse_parameters_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_parameters_section(self, section: str) -> List[str]:
|
||||
fields = self._consume_fields()
|
||||
if self._config.napoleon_use_param:
|
||||
return self._format_docutils_params(fields)
|
||||
else:
|
||||
return self._format_fields(_('Parameters'), fields)
|
||||
|
||||
def _parse_raises_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_raises_section(self, section: str) -> List[str]:
|
||||
fields = self._consume_fields(parse_type=False, prefer_type=True)
|
||||
lines = [] # type: List[str]
|
||||
for _name, _type, _desc in fields:
|
||||
@ -714,13 +671,11 @@ class GoogleDocstring:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_references_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_references_section(self, section: str) -> List[str]:
|
||||
use_admonition = self._config.napoleon_use_admonition_for_references
|
||||
return self._parse_generic_section(_('References'), use_admonition)
|
||||
|
||||
def _parse_returns_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_returns_section(self, section: str) -> List[str]:
|
||||
fields = self._consume_returns_section()
|
||||
multi = len(fields) > 1
|
||||
if multi:
|
||||
@ -748,21 +703,17 @@ class GoogleDocstring:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_see_also_section(self, section):
|
||||
# type (str) -> List[str]
|
||||
def _parse_see_also_section(self, section: str) -> List[str]:
|
||||
return self._parse_admonition('seealso', section)
|
||||
|
||||
def _parse_warns_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_warns_section(self, section: str) -> List[str]:
|
||||
return self._format_fields(_('Warns'), self._consume_fields())
|
||||
|
||||
def _parse_yields_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_yields_section(self, section: str) -> List[str]:
|
||||
fields = self._consume_returns_section()
|
||||
return self._format_fields(_('Yields'), fields)
|
||||
|
||||
def _partition_field_on_colon(self, line):
|
||||
# type: (str) -> Tuple[str, str, str]
|
||||
def _partition_field_on_colon(self, line: str) -> Tuple[str, str, str]:
|
||||
before_colon = []
|
||||
after_colon = []
|
||||
colon = ''
|
||||
@ -784,8 +735,7 @@ class GoogleDocstring:
|
||||
colon,
|
||||
"".join(after_colon).strip())
|
||||
|
||||
def _qualify_name(self, attr_name, klass):
|
||||
# type: (str, Type) -> str
|
||||
def _qualify_name(self, attr_name: str, klass: Type) -> str:
|
||||
if klass and '.' not in attr_name:
|
||||
if attr_name.startswith('~'):
|
||||
attr_name = attr_name[1:]
|
||||
@ -796,8 +746,7 @@ class GoogleDocstring:
|
||||
return '~%s.%s' % (q, attr_name)
|
||||
return attr_name
|
||||
|
||||
def _strip_empty(self, lines):
|
||||
# type: (List[str]) -> List[str]
|
||||
def _strip_empty(self, lines: List[str]) -> List[str]:
|
||||
if lines:
|
||||
start = -1
|
||||
for i, line in enumerate(lines):
|
||||
@ -910,14 +859,14 @@ class NumpyDocstring(GoogleDocstring):
|
||||
The lines of the docstring in a list.
|
||||
|
||||
"""
|
||||
def __init__(self, docstring, config=None, app=None, what='', name='',
|
||||
obj=None, options=None):
|
||||
# type: (Union[str, List[str]], SphinxConfig, Sphinx, str, str, Any, Any) -> None
|
||||
def __init__(self, docstring: Union[str, List[str]], config: SphinxConfig = None,
|
||||
app: Sphinx = None, what: str = '', name: str = '',
|
||||
obj: Any = None, options: Any = None) -> None:
|
||||
self._directive_sections = ['.. index::']
|
||||
super().__init__(docstring, config, app, what, name, obj, options)
|
||||
|
||||
def _consume_field(self, parse_type=True, prefer_type=False):
|
||||
# type: (bool, bool) -> Tuple[str, str, List[str]]
|
||||
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False
|
||||
) -> Tuple[str, str, List[str]]:
|
||||
line = next(self._line_iter)
|
||||
if parse_type:
|
||||
_name, _, _type = self._partition_field_on_colon(line)
|
||||
@ -933,20 +882,17 @@ class NumpyDocstring(GoogleDocstring):
|
||||
_desc = self.__class__(_desc, self._config).lines()
|
||||
return _name, _type, _desc
|
||||
|
||||
def _consume_returns_section(self):
|
||||
# type: () -> List[Tuple[str, str, List[str]]]
|
||||
def _consume_returns_section(self) -> List[Tuple[str, str, List[str]]]:
|
||||
return self._consume_fields(prefer_type=True)
|
||||
|
||||
def _consume_section_header(self):
|
||||
# type: () -> str
|
||||
def _consume_section_header(self) -> str:
|
||||
section = next(self._line_iter)
|
||||
if not _directive_regex.match(section):
|
||||
# Consume the header underline
|
||||
next(self._line_iter)
|
||||
return section
|
||||
|
||||
def _is_section_break(self):
|
||||
# type: () -> bool
|
||||
def _is_section_break(self) -> bool:
|
||||
line1, line2 = self._line_iter.peek(2)
|
||||
return (not self._line_iter.has_next() or
|
||||
self._is_section_header() or
|
||||
@ -955,8 +901,7 @@ class NumpyDocstring(GoogleDocstring):
|
||||
line1 and
|
||||
not self._is_indented(line1, self._section_indent)))
|
||||
|
||||
def _is_section_header(self):
|
||||
# type: () -> bool
|
||||
def _is_section_header(self) -> bool:
|
||||
section, underline = self._line_iter.peek(2)
|
||||
section = section.lower()
|
||||
if section in self._sections and isinstance(underline, str):
|
||||
@ -968,16 +913,14 @@ class NumpyDocstring(GoogleDocstring):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _parse_see_also_section(self, section):
|
||||
# type: (str) -> List[str]
|
||||
def _parse_see_also_section(self, section: str) -> List[str]:
|
||||
lines = self._consume_to_next_section()
|
||||
try:
|
||||
return self._parse_numpydoc_see_also_section(lines)
|
||||
except ValueError:
|
||||
return self._format_admonition('seealso', lines)
|
||||
|
||||
def _parse_numpydoc_see_also_section(self, content):
|
||||
# type: (List[str]) -> List[str]
|
||||
def _parse_numpydoc_see_also_section(self, content: List[str]) -> List[str]:
|
||||
"""
|
||||
Derived from the NumpyDoc implementation of _parse_see_also.
|
||||
|
||||
@ -991,8 +934,7 @@ class NumpyDocstring(GoogleDocstring):
|
||||
"""
|
||||
items = []
|
||||
|
||||
def parse_item_name(text):
|
||||
# type: (str) -> Tuple[str, str]
|
||||
def parse_item_name(text: str) -> Tuple[str, str]:
|
||||
"""Match ':role:`name`' or 'name'"""
|
||||
m = self._name_rgx.match(text)
|
||||
if m:
|
||||
@ -1003,8 +945,7 @@ class NumpyDocstring(GoogleDocstring):
|
||||
return g[2], g[1]
|
||||
raise ValueError("%s is not a item name" % text)
|
||||
|
||||
def push_item(name, rest):
|
||||
# type: (str, List[str]) -> None
|
||||
def push_item(name: str, rest: List[str]) -> None:
|
||||
if not name:
|
||||
return
|
||||
name, role = parse_item_name(name)
|
||||
|
@ -11,10 +11,7 @@
|
||||
"""
|
||||
|
||||
import collections
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Iterable # NOQA
|
||||
from typing import Any, Iterable
|
||||
|
||||
|
||||
class peek_iter:
|
||||
@ -50,8 +47,7 @@ class peek_iter:
|
||||
be set to a new object instance: ``object()``.
|
||||
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
# type: (Any) -> None
|
||||
def __init__(self, *args) -> None:
|
||||
"""__init__(o, sentinel=None)"""
|
||||
self._iterable = iter(*args) # type: Iterable
|
||||
self._cache = collections.deque() # type: collections.deque
|
||||
@ -60,18 +56,15 @@ class peek_iter:
|
||||
else:
|
||||
self.sentinel = object()
|
||||
|
||||
def __iter__(self):
|
||||
# type: () -> peek_iter
|
||||
def __iter__(self) -> "peek_iter":
|
||||
return self
|
||||
|
||||
def __next__(self, n=None):
|
||||
# type: (int) -> Any
|
||||
def __next__(self, n: int = None) -> Any:
|
||||
# note: prevent 2to3 to transform self.next() in next(self) which
|
||||
# causes an infinite loop !
|
||||
return getattr(self, 'next')(n)
|
||||
|
||||
def _fillcache(self, n):
|
||||
# type: (int) -> None
|
||||
def _fillcache(self, n: int) -> None:
|
||||
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
|
||||
if not n:
|
||||
n = 1
|
||||
@ -82,8 +75,7 @@ class peek_iter:
|
||||
while len(self._cache) < n:
|
||||
self._cache.append(self.sentinel)
|
||||
|
||||
def has_next(self):
|
||||
# type: () -> bool
|
||||
def has_next(self) -> bool:
|
||||
"""Determine if iterator is exhausted.
|
||||
|
||||
Returns
|
||||
@ -98,8 +90,7 @@ class peek_iter:
|
||||
"""
|
||||
return self.peek() != self.sentinel
|
||||
|
||||
def next(self, n=None):
|
||||
# type: (int) -> Any
|
||||
def next(self, n: int = None) -> Any:
|
||||
"""Get the next item or `n` items of the iterator.
|
||||
|
||||
Parameters
|
||||
@ -134,8 +125,7 @@ class peek_iter:
|
||||
result = [self._cache.popleft() for i in range(n)]
|
||||
return result
|
||||
|
||||
def peek(self, n=None):
|
||||
# type: (int) -> Any
|
||||
def peek(self, n: int = None) -> Any:
|
||||
"""Preview the next item or `n` items of the iterator.
|
||||
|
||||
The iterator is not advanced when peek is called.
|
||||
@ -218,8 +208,7 @@ class modify_iter(peek_iter):
|
||||
"whitespace."
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
# type: (Any, Any) -> None
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
"""__init__(o, sentinel=None, modifier=lambda x: x)"""
|
||||
if 'modifier' in kwargs:
|
||||
self.modifier = kwargs['modifier']
|
||||
@ -233,8 +222,7 @@ class modify_iter(peek_iter):
|
||||
'modifier must be callable')
|
||||
super().__init__(*args)
|
||||
|
||||
def _fillcache(self, n):
|
||||
# type: (int) -> None
|
||||
def _fillcache(self, n: int) -> None:
|
||||
"""Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
|
||||
|
||||
Each item returned by the iterator is passed through the
|
||||
|
@ -12,29 +12,27 @@
|
||||
"""
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict, Iterable, List, Tuple
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.domains import Domain
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.errors import NoUri
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.texescape import tex_escape_map
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, Iterable, List, Tuple # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator # NOQA
|
||||
from sphinx.writers.latex import LaTeXTranslator # NOQA
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -62,12 +60,11 @@ class Todo(BaseAdmonition, SphinxDirective):
|
||||
'name': directives.unchanged,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
if not self.options.get('class'):
|
||||
self.options['class'] = ['admonition-todo']
|
||||
|
||||
(todo,) = super().run() # type: Tuple[nodes.Node]
|
||||
(todo,) = super().run() # type: Tuple[Node]
|
||||
if isinstance(todo, nodes.system_message):
|
||||
return [todo]
|
||||
elif isinstance(todo, todo_node):
|
||||
@ -86,21 +83,18 @@ class TodoDomain(Domain):
|
||||
label = 'todo'
|
||||
|
||||
@property
|
||||
def todos(self):
|
||||
# type: () -> Dict[str, List[todo_node]]
|
||||
def todos(self) -> Dict[str, List[todo_node]]:
|
||||
return self.data.setdefault('todos', {})
|
||||
|
||||
def clear_doc(self, docname):
|
||||
# type: (str) -> None
|
||||
def clear_doc(self, docname: str) -> None:
|
||||
self.todos.pop(docname, None)
|
||||
|
||||
def merge_domaindata(self, docnames, otherdata):
|
||||
# type: (List[str], Dict) -> None
|
||||
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
|
||||
for docname in docnames:
|
||||
self.todos[docname] = otherdata['todos'][docname]
|
||||
|
||||
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:
|
||||
todos = self.todos.setdefault(docname, [])
|
||||
for todo in document.traverse(todo_node):
|
||||
env.app.emit('todo-defined', todo)
|
||||
@ -111,8 +105,7 @@ class TodoDomain(Domain):
|
||||
location=todo)
|
||||
|
||||
|
||||
def process_todos(app, doctree):
|
||||
# type: (Sphinx, nodes.document) -> None
|
||||
def process_todos(app: Sphinx, doctree: nodes.document) -> None:
|
||||
warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning)
|
||||
# collect all todos in the environment
|
||||
# this is not done in the directive itself because it some transformations
|
||||
@ -150,16 +143,14 @@ class TodoList(SphinxDirective):
|
||||
final_argument_whitespace = False
|
||||
option_spec = {} # type: Dict
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
# Simply insert an empty todolist node which will be replaced later
|
||||
# when process_todo_nodes is called
|
||||
return [todolist('')]
|
||||
|
||||
|
||||
class TodoListProcessor:
|
||||
def __init__(self, app, doctree, docname):
|
||||
# type: (Sphinx, nodes.document, str) -> None
|
||||
def __init__(self, app: Sphinx, doctree: nodes.document, docname: str) -> None:
|
||||
self.builder = app.builder
|
||||
self.config = app.config
|
||||
self.env = app.env
|
||||
@ -167,8 +158,7 @@ class TodoListProcessor:
|
||||
|
||||
self.process(doctree, docname)
|
||||
|
||||
def process(self, doctree, docname):
|
||||
# type: (nodes.document, str) -> None
|
||||
def process(self, doctree: nodes.document, docname: str) -> None:
|
||||
todos = sum(self.domain.todos.values(), [])
|
||||
for node in doctree.traverse(todolist):
|
||||
if not self.config.todo_include_todos:
|
||||
@ -176,7 +166,7 @@ class TodoListProcessor:
|
||||
continue
|
||||
|
||||
if node.get('ids'):
|
||||
content = [nodes.target()] # type: List[nodes.Element]
|
||||
content = [nodes.target()] # type: List[Element]
|
||||
else:
|
||||
content = []
|
||||
|
||||
@ -194,8 +184,7 @@ class TodoListProcessor:
|
||||
|
||||
node.replace_self(content)
|
||||
|
||||
def create_todo_reference(self, todo, docname):
|
||||
# type: (todo_node, str) -> nodes.paragraph
|
||||
def create_todo_reference(self, todo: todo_node, docname: str) -> nodes.paragraph:
|
||||
if self.config.todo_link_only:
|
||||
description = _('<<original entry>>')
|
||||
else:
|
||||
@ -224,8 +213,7 @@ class TodoListProcessor:
|
||||
return para
|
||||
|
||||
|
||||
def process_todo_nodes(app, doctree, fromdocname):
|
||||
# type: (Sphinx, nodes.document, str) -> None
|
||||
def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) -> None:
|
||||
"""Replace all todolist nodes with a list of the collected todos.
|
||||
Augment each todo with a backlink to the original location.
|
||||
"""
|
||||
@ -236,7 +224,7 @@ def process_todo_nodes(app, doctree, fromdocname):
|
||||
|
||||
for node in doctree.traverse(todolist):
|
||||
if node.get('ids'):
|
||||
content = [nodes.target()] # type: List[nodes.Element]
|
||||
content = [nodes.target()] # type: List[Element]
|
||||
else:
|
||||
content = []
|
||||
|
||||
@ -280,8 +268,7 @@ def process_todo_nodes(app, doctree, fromdocname):
|
||||
node.replace_self(content)
|
||||
|
||||
|
||||
def purge_todos(app, env, docname):
|
||||
# type: (Sphinx, BuildEnvironment, str) -> None
|
||||
def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
||||
warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning)
|
||||
if not hasattr(env, 'todo_all_todos'):
|
||||
return
|
||||
@ -289,8 +276,8 @@ def purge_todos(app, env, docname):
|
||||
if todo['docname'] != docname]
|
||||
|
||||
|
||||
def merge_info(app, env, docnames, other):
|
||||
# type: (Sphinx, BuildEnvironment, Iterable[str], BuildEnvironment) -> None
|
||||
def merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||
other: BuildEnvironment) -> None:
|
||||
warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning)
|
||||
if not hasattr(other, 'todo_all_todos'):
|
||||
return
|
||||
@ -299,21 +286,18 @@ def merge_info(app, env, docnames, other):
|
||||
env.todo_all_todos.extend(other.todo_all_todos) # type: ignore
|
||||
|
||||
|
||||
def visit_todo_node(self, node):
|
||||
# type: (HTMLTranslator, todo_node) -> None
|
||||
def visit_todo_node(self: HTMLTranslator, node: todo_node) -> None:
|
||||
if self.config.todo_include_todos:
|
||||
self.visit_admonition(node)
|
||||
else:
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def depart_todo_node(self, node):
|
||||
# type: (HTMLTranslator, todo_node) -> None
|
||||
def depart_todo_node(self: HTMLTranslator, node: todo_node) -> None:
|
||||
self.depart_admonition(node)
|
||||
|
||||
|
||||
def latex_visit_todo_node(self, node):
|
||||
# type: (LaTeXTranslator, todo_node) -> None
|
||||
def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
|
||||
if self.config.todo_include_todos:
|
||||
self.body.append('\n\\begin{sphinxadmonition}{note}{')
|
||||
self.body.append(self.hypertarget_to(node))
|
||||
@ -324,13 +308,11 @@ def latex_visit_todo_node(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def latex_depart_todo_node(self, node):
|
||||
# type: (LaTeXTranslator, todo_node) -> None
|
||||
def latex_depart_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
|
||||
self.body.append('\\end{sphinxadmonition}\n')
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_event('todo-defined')
|
||||
app.add_config_value('todo_include_todos', False, 'html')
|
||||
app.add_config_value('todo_link_only', False, 'html')
|
||||
|
@ -10,29 +10,27 @@
|
||||
|
||||
import traceback
|
||||
import warnings
|
||||
from typing import Any, Dict, Iterable, Iterator, Set, Tuple
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
|
||||
import sphinx
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.config import Config
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.pycode import ModuleAnalyzer
|
||||
from sphinx.util import get_full_modname, logging, status_iterator
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, Iterable, Iterator, Set, Tuple # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_full_modname(app, modname, attribute):
|
||||
# type: (Sphinx, str, str) -> str
|
||||
def _get_full_modname(app: Sphinx, modname: str, attribute: str) -> str:
|
||||
try:
|
||||
return get_full_modname(modname, attribute)
|
||||
except AttributeError:
|
||||
@ -50,8 +48,7 @@ def _get_full_modname(app, modname, attribute):
|
||||
return None
|
||||
|
||||
|
||||
def doctree_read(app, doctree):
|
||||
# type: (Sphinx, nodes.Node) -> None
|
||||
def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||||
env = app.builder.env
|
||||
if not hasattr(env, '_viewcode_modules'):
|
||||
env._viewcode_modules = {} # type: ignore
|
||||
@ -122,8 +119,8 @@ def doctree_read(app, doctree):
|
||||
signode += onlynode
|
||||
|
||||
|
||||
def env_merge_info(app, env, docnames, other):
|
||||
# type: (Sphinx, BuildEnvironment, Iterable[str], BuildEnvironment) -> None
|
||||
def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||
other: BuildEnvironment) -> None:
|
||||
if not hasattr(other, '_viewcode_modules'):
|
||||
return
|
||||
# create a _viewcode_modules dict on the main environment
|
||||
@ -133,8 +130,8 @@ def env_merge_info(app, env, docnames, other):
|
||||
env._viewcode_modules.update(other._viewcode_modules) # type: ignore
|
||||
|
||||
|
||||
def missing_reference(app, env, node, contnode):
|
||||
# type: (Sphinx, BuildEnvironment, nodes.Element, nodes.Node) -> nodes.Node
|
||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
||||
) -> Node:
|
||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||
if node['reftype'] == 'viewcode':
|
||||
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||
@ -143,8 +140,7 @@ def missing_reference(app, env, node, contnode):
|
||||
return None
|
||||
|
||||
|
||||
def collect_pages(app):
|
||||
# type: (Sphinx) -> Iterator[Tuple[str, Dict[str, Any], str]]
|
||||
def collect_pages(app: Sphinx) -> Iterator[Tuple[str, Dict[str, Any], str]]:
|
||||
env = app.builder.env
|
||||
if not hasattr(env, '_viewcode_modules'):
|
||||
return
|
||||
@ -238,16 +234,14 @@ def collect_pages(app):
|
||||
yield ('_modules/index', context, 'page.html')
|
||||
|
||||
|
||||
def migrate_viewcode_import(app, config):
|
||||
# type: (Sphinx, Config) -> None
|
||||
def migrate_viewcode_import(app: Sphinx, config: Config) -> None:
|
||||
if config.viewcode_import is not None:
|
||||
warnings.warn('viewcode_import was renamed to viewcode_follow_imported_members. '
|
||||
'Please update your configuration.',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_config_value('viewcode_import', None, False)
|
||||
app.add_config_value('viewcode_enable_epub', False, False)
|
||||
app.add_config_value('viewcode_follow_imported_members', True, False)
|
||||
|
@ -139,7 +139,8 @@ class PygmentsBridge:
|
||||
lexer = lexers['none']
|
||||
|
||||
if lang in lexers:
|
||||
lexer = lexers[lang]
|
||||
# just return custom lexers here (without installing raiseonerror filter)
|
||||
return lexers[lang]
|
||||
elif lang in lexer_classes:
|
||||
lexer = lexer_classes[lang](**opts)
|
||||
else:
|
||||
|
@ -11,29 +11,25 @@
|
||||
import re
|
||||
from io import StringIO
|
||||
from os import path
|
||||
from typing import Any, Dict, IO, List, Tuple
|
||||
from zipfile import ZipFile
|
||||
|
||||
from sphinx.errors import PycodeError
|
||||
from sphinx.pycode.parser import Parser
|
||||
from sphinx.util import get_module_source, detect_encoding
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, IO, List, Tuple # NOQA
|
||||
|
||||
|
||||
class ModuleAnalyzer:
|
||||
# cache for analyzer objects -- caches both by module and file name
|
||||
cache = {} # type: Dict[Tuple[str, str], Any]
|
||||
|
||||
@classmethod
|
||||
def for_string(cls, string, modname, srcname='<string>'):
|
||||
# type: (str, str, str) -> ModuleAnalyzer
|
||||
def for_string(cls, string: str, modname: str, srcname: str = '<string>'
|
||||
) -> "ModuleAnalyzer":
|
||||
return cls(StringIO(string), modname, srcname, decoded=True)
|
||||
|
||||
@classmethod
|
||||
def for_file(cls, filename, modname):
|
||||
# type: (str, str) -> ModuleAnalyzer
|
||||
def for_file(cls, filename: str, modname: str) -> "ModuleAnalyzer":
|
||||
if ('file', filename) in cls.cache:
|
||||
return cls.cache['file', filename]
|
||||
try:
|
||||
@ -48,8 +44,7 @@ class ModuleAnalyzer:
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def for_egg(cls, filename, modname):
|
||||
# type: (str, str) -> ModuleAnalyzer
|
||||
def for_egg(cls, filename: str, modname: str) -> "ModuleAnalyzer":
|
||||
SEP = re.escape(path.sep)
|
||||
eggpath, relpath = re.split('(?<=\\.egg)' + SEP, filename)
|
||||
try:
|
||||
@ -60,8 +55,7 @@ class ModuleAnalyzer:
|
||||
raise PycodeError('error opening %r' % filename, exc)
|
||||
|
||||
@classmethod
|
||||
def for_module(cls, modname):
|
||||
# type: (str) -> ModuleAnalyzer
|
||||
def for_module(cls, modname: str) -> "ModuleAnalyzer":
|
||||
if ('module', modname) in cls.cache:
|
||||
entry = cls.cache['module', modname]
|
||||
if isinstance(entry, PycodeError):
|
||||
@ -80,8 +74,7 @@ class ModuleAnalyzer:
|
||||
cls.cache['module', modname] = obj
|
||||
return obj
|
||||
|
||||
def __init__(self, source, modname, srcname, decoded=False):
|
||||
# type: (IO, str, str, bool) -> None
|
||||
def __init__(self, source: IO, modname: str, srcname: str, decoded: bool = False) -> None:
|
||||
self.modname = modname # name of the module
|
||||
self.srcname = srcname # name of the source file
|
||||
|
||||
@ -100,8 +93,7 @@ class ModuleAnalyzer:
|
||||
self.tagorder = None # type: Dict[str, int]
|
||||
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
||||
|
||||
def parse(self):
|
||||
# type: () -> None
|
||||
def parse(self) -> None:
|
||||
"""Parse the source code."""
|
||||
try:
|
||||
parser = Parser(self.code, self.encoding)
|
||||
@ -119,16 +111,14 @@ class ModuleAnalyzer:
|
||||
except Exception as exc:
|
||||
raise PycodeError('parsing %r failed: %r' % (self.srcname, exc))
|
||||
|
||||
def find_attr_docs(self):
|
||||
# type: () -> Dict[Tuple[str, str], List[str]]
|
||||
def find_attr_docs(self) -> Dict[Tuple[str, str], List[str]]:
|
||||
"""Find class and module-level attributes and their documentation."""
|
||||
if self.attr_docs is None:
|
||||
self.parse()
|
||||
|
||||
return self.attr_docs
|
||||
|
||||
def find_tags(self):
|
||||
# type: () -> Dict[str, Tuple[str, int, int]]
|
||||
def find_tags(self) -> Dict[str, Tuple[str, int, int]]:
|
||||
"""Find class, function and method definitions and their location."""
|
||||
if self.tags is None:
|
||||
self.parse()
|
||||
|
@ -15,10 +15,8 @@ import sys
|
||||
import tokenize
|
||||
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
||||
from tokenize import COMMENT, NL
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List, Tuple # NOQA
|
||||
|
||||
comment_re = re.compile('^\\s*#: ?(.*)\r?\n?$')
|
||||
indent_re = re.compile('^\\s*$')
|
||||
@ -31,13 +29,11 @@ else:
|
||||
ASSIGN_NODES = (ast.Assign)
|
||||
|
||||
|
||||
def filter_whitespace(code):
|
||||
# type: (str) -> str
|
||||
def filter_whitespace(code: str) -> str:
|
||||
return code.replace('\f', ' ') # replace FF (form feed) with whitespace
|
||||
|
||||
|
||||
def get_assign_targets(node):
|
||||
# type: (ast.AST) -> List[ast.expr]
|
||||
def get_assign_targets(node: ast.AST) -> List[ast.expr]:
|
||||
"""Get list of targets from Assign and AnnAssign node."""
|
||||
if isinstance(node, ast.Assign):
|
||||
return node.targets
|
||||
@ -45,8 +41,7 @@ def get_assign_targets(node):
|
||||
return [node.target] # type: ignore
|
||||
|
||||
|
||||
def get_lvar_names(node, self=None):
|
||||
# type: (ast.AST, ast.arg) -> List[str]
|
||||
def get_lvar_names(node: ast.AST, self: ast.arg = None) -> List[str]:
|
||||
"""Convert assignment-AST to variable names.
|
||||
|
||||
This raises `TypeError` if the assignment does not create new variable::
|
||||
@ -88,11 +83,9 @@ def get_lvar_names(node, self=None):
|
||||
raise NotImplementedError('Unexpected node name %r' % node_name)
|
||||
|
||||
|
||||
def dedent_docstring(s):
|
||||
# type: (str) -> str
|
||||
def dedent_docstring(s: str) -> str:
|
||||
"""Remove common leading indentation from docstring."""
|
||||
def dummy():
|
||||
# type: () -> None
|
||||
def dummy() -> None:
|
||||
# dummy function to mock `inspect.getdoc`.
|
||||
pass
|
||||
|
||||
@ -104,16 +97,15 @@ def dedent_docstring(s):
|
||||
class Token:
|
||||
"""Better token wrapper for tokenize module."""
|
||||
|
||||
def __init__(self, kind, value, start, end, source):
|
||||
# type: (int, Any, Tuple[int, int], Tuple[int, int], str) -> None
|
||||
def __init__(self, kind: int, value: Any, start: Tuple[int, int], end: Tuple[int, int],
|
||||
source: str) -> None:
|
||||
self.kind = kind
|
||||
self.value = value
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.source = source
|
||||
|
||||
def __eq__(self, other):
|
||||
# type: (Any) -> bool
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, int):
|
||||
return self.kind == other
|
||||
elif isinstance(other, str):
|
||||
@ -125,32 +117,27 @@ class Token:
|
||||
else:
|
||||
raise ValueError('Unknown value: %r' % other)
|
||||
|
||||
def match(self, *conditions):
|
||||
# type: (Any) -> bool
|
||||
def match(self, *conditions) -> bool:
|
||||
return any(self == candidate for candidate in conditions)
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return '<Token kind=%r value=%r>' % (tokenize.tok_name[self.kind],
|
||||
self.value.strip())
|
||||
|
||||
|
||||
class TokenProcessor:
|
||||
def __init__(self, buffers):
|
||||
# type: (List[str]) -> None
|
||||
def __init__(self, buffers: List[str]) -> None:
|
||||
lines = iter(buffers)
|
||||
self.buffers = buffers
|
||||
self.tokens = tokenize.generate_tokens(lambda: next(lines))
|
||||
self.current = None # type: Token
|
||||
self.previous = None # type: Token
|
||||
|
||||
def get_line(self, lineno):
|
||||
# type: (int) -> str
|
||||
def get_line(self, lineno: int) -> str:
|
||||
"""Returns specified line."""
|
||||
return self.buffers[lineno - 1]
|
||||
|
||||
def fetch_token(self):
|
||||
# type: () -> Token
|
||||
def fetch_token(self) -> Token:
|
||||
"""Fetch a next token from source code.
|
||||
|
||||
Returns ``False`` if sequence finished.
|
||||
@ -163,8 +150,7 @@ class TokenProcessor:
|
||||
|
||||
return self.current
|
||||
|
||||
def fetch_until(self, condition):
|
||||
# type: (Any) -> List[Token]
|
||||
def fetch_until(self, condition: Any) -> List[Token]:
|
||||
"""Fetch tokens until specified token appeared.
|
||||
|
||||
.. note:: This also handles parenthesis well.
|
||||
@ -191,13 +177,11 @@ class AfterCommentParser(TokenProcessor):
|
||||
and returns the comments for variable if exists.
|
||||
"""
|
||||
|
||||
def __init__(self, lines):
|
||||
# type: (List[str]) -> None
|
||||
def __init__(self, lines: List[str]) -> None:
|
||||
super().__init__(lines)
|
||||
self.comment = None # type: str
|
||||
|
||||
def fetch_rvalue(self):
|
||||
# type: () -> List[Token]
|
||||
def fetch_rvalue(self) -> List[Token]:
|
||||
"""Fetch right-hand value of assignment."""
|
||||
tokens = []
|
||||
while self.fetch_token():
|
||||
@ -217,8 +201,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
|
||||
return tokens
|
||||
|
||||
def parse(self):
|
||||
# type: () -> None
|
||||
def parse(self) -> None:
|
||||
"""Parse the code and obtain comment after assignment."""
|
||||
# skip lvalue (or whole of AnnAssign)
|
||||
while not self.fetch_token().match([OP, '='], NEWLINE, COMMENT):
|
||||
@ -235,8 +218,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
class VariableCommentPicker(ast.NodeVisitor):
|
||||
"""Python source code parser to pick up variable comments."""
|
||||
|
||||
def __init__(self, buffers, encoding):
|
||||
# type: (List[str], str) -> None
|
||||
def __init__(self, buffers: List[str], encoding: str) -> None:
|
||||
self.counter = itertools.count()
|
||||
self.buffers = buffers
|
||||
self.encoding = encoding
|
||||
@ -248,8 +230,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
self.deforders = {} # type: Dict[str, int]
|
||||
super().__init__()
|
||||
|
||||
def add_entry(self, name):
|
||||
# type: (str) -> None
|
||||
def add_entry(self, name: str) -> None:
|
||||
if self.current_function:
|
||||
if self.current_classes and self.context[-1] == "__init__":
|
||||
# store variable comments inside __init__ method of classes
|
||||
@ -261,8 +242,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
|
||||
self.deforders[".".join(definition)] = next(self.counter)
|
||||
|
||||
def add_variable_comment(self, name, comment):
|
||||
# type: (str, str) -> None
|
||||
def add_variable_comment(self, name: str, comment: str) -> None:
|
||||
if self.current_function:
|
||||
if self.current_classes and self.context[-1] == "__init__":
|
||||
# store variable comments inside __init__ method of classes
|
||||
@ -274,27 +254,23 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
|
||||
self.comments[(context, name)] = comment
|
||||
|
||||
def get_self(self):
|
||||
# type: () -> ast.arg
|
||||
def get_self(self) -> ast.arg:
|
||||
"""Returns the name of first argument if in function."""
|
||||
if self.current_function and self.current_function.args.args:
|
||||
return self.current_function.args.args[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_line(self, lineno):
|
||||
# type: (int) -> str
|
||||
def get_line(self, lineno: int) -> str:
|
||||
"""Returns specified line."""
|
||||
return self.buffers[lineno - 1]
|
||||
|
||||
def visit(self, node):
|
||||
# type: (ast.AST) -> None
|
||||
def visit(self, node: ast.AST) -> None:
|
||||
"""Updates self.previous to ."""
|
||||
super().visit(node)
|
||||
self.previous = node
|
||||
|
||||
def visit_Assign(self, node):
|
||||
# type: (ast.Assign) -> None
|
||||
def visit_Assign(self, node: ast.Assign) -> None:
|
||||
"""Handles Assign node and pick up a variable comment."""
|
||||
try:
|
||||
targets = get_assign_targets(node)
|
||||
@ -334,13 +310,11 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
for varname in varnames:
|
||||
self.add_entry(varname)
|
||||
|
||||
def visit_AnnAssign(self, node):
|
||||
# type: (ast.AST) -> None
|
||||
def visit_AnnAssign(self, node: ast.AST) -> None: # Note: ast.AnnAssign not found in py35
|
||||
"""Handles AnnAssign node and pick up a variable comment."""
|
||||
self.visit_Assign(node) # type: ignore
|
||||
|
||||
def visit_Expr(self, node):
|
||||
# type: (ast.Expr) -> None
|
||||
def visit_Expr(self, node: ast.Expr) -> None:
|
||||
"""Handles Expr node and pick up a comment if string."""
|
||||
if (isinstance(self.previous, ASSIGN_NODES) and isinstance(node.value, ast.Str)):
|
||||
try:
|
||||
@ -357,8 +331,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
except TypeError:
|
||||
pass # this assignment is not new definition!
|
||||
|
||||
def visit_Try(self, node):
|
||||
# type: (ast.Try) -> None
|
||||
def visit_Try(self, node: ast.Try) -> None:
|
||||
"""Handles Try node and processes body and else-clause.
|
||||
|
||||
.. note:: pycode parser ignores objects definition in except-clause.
|
||||
@ -368,8 +341,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
for subnode in node.orelse:
|
||||
self.visit(subnode)
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
# type: (ast.ClassDef) -> None
|
||||
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
||||
"""Handles ClassDef node and set context."""
|
||||
self.current_classes.append(node.name)
|
||||
self.add_entry(node.name)
|
||||
@ -380,8 +352,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
self.context.pop()
|
||||
self.current_classes.pop()
|
||||
|
||||
def visit_FunctionDef(self, node):
|
||||
# type: (ast.FunctionDef) -> None
|
||||
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
||||
"""Handles FunctionDef node and set context."""
|
||||
if self.current_function is None:
|
||||
self.add_entry(node.name) # should be called before setting self.current_function
|
||||
@ -392,8 +363,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
||||
self.context.pop()
|
||||
self.current_function = None
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
# type: (ast.AsyncFunctionDef) -> None
|
||||
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
||||
"""Handles AsyncFunctionDef node and set context."""
|
||||
self.visit_FunctionDef(node) # type: ignore
|
||||
|
||||
@ -403,16 +373,14 @@ class DefinitionFinder(TokenProcessor):
|
||||
classes and methods.
|
||||
"""
|
||||
|
||||
def __init__(self, lines):
|
||||
# type: (List[str]) -> None
|
||||
def __init__(self, lines: List[str]) -> None:
|
||||
super().__init__(lines)
|
||||
self.decorator = None # type: Token
|
||||
self.context = [] # type: List[str]
|
||||
self.indents = [] # type: List
|
||||
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
||||
|
||||
def add_definition(self, name, entry):
|
||||
# type: (str, Tuple[str, int, int]) -> None
|
||||
def add_definition(self, name: str, entry: Tuple[str, int, int]) -> None:
|
||||
"""Add a location of definition."""
|
||||
if self.indents and self.indents[-1][0] == 'def' and entry[0] == 'def':
|
||||
# ignore definition of inner function
|
||||
@ -420,8 +388,7 @@ class DefinitionFinder(TokenProcessor):
|
||||
else:
|
||||
self.definitions[name] = entry
|
||||
|
||||
def parse(self):
|
||||
# type: () -> None
|
||||
def parse(self) -> None:
|
||||
"""Parse the code to obtain location of definitions."""
|
||||
while True:
|
||||
token = self.fetch_token()
|
||||
@ -442,8 +409,7 @@ class DefinitionFinder(TokenProcessor):
|
||||
elif token == DEDENT:
|
||||
self.finalize_block()
|
||||
|
||||
def parse_definition(self, typ):
|
||||
# type: (str) -> None
|
||||
def parse_definition(self, typ: str) -> None:
|
||||
"""Parse AST of definition."""
|
||||
name = self.fetch_token()
|
||||
self.context.append(name.value)
|
||||
@ -464,8 +430,7 @@ class DefinitionFinder(TokenProcessor):
|
||||
self.add_definition(funcname, (typ, start_pos, name.end[0]))
|
||||
self.context.pop()
|
||||
|
||||
def finalize_block(self):
|
||||
# type: () -> None
|
||||
def finalize_block(self) -> None:
|
||||
"""Finalize definition block."""
|
||||
definition = self.indents.pop()
|
||||
if definition[0] != 'other':
|
||||
@ -484,22 +449,19 @@ class Parser:
|
||||
This is a better wrapper for ``VariableCommentPicker``.
|
||||
"""
|
||||
|
||||
def __init__(self, code, encoding='utf-8'):
|
||||
# type: (str, str) -> None
|
||||
def __init__(self, code: str, encoding: str = 'utf-8') -> None:
|
||||
self.code = filter_whitespace(code)
|
||||
self.encoding = encoding
|
||||
self.comments = {} # type: Dict[Tuple[str, str], str]
|
||||
self.deforders = {} # type: Dict[str, int]
|
||||
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
||||
|
||||
def parse(self):
|
||||
# type: () -> None
|
||||
def parse(self) -> None:
|
||||
"""Parse the source code."""
|
||||
self.parse_comments()
|
||||
self.parse_definition()
|
||||
|
||||
def parse_comments(self):
|
||||
# type: () -> None
|
||||
def parse_comments(self) -> None:
|
||||
"""Parse the code and pick up comments."""
|
||||
tree = ast.parse(self.code.encode())
|
||||
picker = VariableCommentPicker(self.code.splitlines(True), self.encoding)
|
||||
@ -507,8 +469,7 @@ class Parser:
|
||||
self.comments = picker.comments
|
||||
self.deforders = picker.deforders
|
||||
|
||||
def parse_definition(self):
|
||||
# type: () -> None
|
||||
def parse_definition(self) -> None:
|
||||
"""Parse the location of definitions from the code."""
|
||||
parser = DefinitionFinder(self.code.splitlines(True))
|
||||
parser.parse()
|
||||
|
@ -14,25 +14,20 @@ import sys
|
||||
from collections import namedtuple
|
||||
from io import StringIO
|
||||
from subprocess import PIPE
|
||||
from typing import Any, Dict
|
||||
|
||||
import pytest
|
||||
|
||||
from . import util
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, Union # NOQA
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def rootdir():
|
||||
# type: () -> None
|
||||
def rootdir() -> None:
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir):
|
||||
# type: (Any, Any, Any, Any, Any) -> None
|
||||
"""
|
||||
parameters that is specified by 'pytest.mark.sphinx' for
|
||||
sphinx.application.Sphinx initialization
|
||||
@ -158,10 +153,10 @@ def make_app(test_params, monkeypatch):
|
||||
status, warning = StringIO(), StringIO()
|
||||
kwargs.setdefault('status', status)
|
||||
kwargs.setdefault('warning', warning)
|
||||
app_ = util.SphinxTestApp(*args, **kwargs) # type: Union[util.SphinxTestApp, util.SphinxTestAppWrapperForSkipBuilding] # NOQA
|
||||
app_ = util.SphinxTestApp(*args, **kwargs) # type: Any
|
||||
apps.append(app_)
|
||||
if test_params['shared_result']:
|
||||
app_ = util.SphinxTestAppWrapperForSkipBuilding(app_) # type: ignore
|
||||
app_ = util.SphinxTestAppWrapperForSkipBuilding(app_)
|
||||
return app_
|
||||
yield make
|
||||
|
||||
|
@ -5,14 +5,12 @@
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import builtins
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
import builtins # NOQA
|
||||
from typing import Any, Callable, IO, List # NOQA
|
||||
from typing import Any, Callable, IO, List
|
||||
|
||||
|
||||
FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
@ -24,61 +22,52 @@ class path(str):
|
||||
"""
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
# type: () -> path
|
||||
def parent(self) -> "path":
|
||||
"""
|
||||
The name of the directory the file or directory is in.
|
||||
"""
|
||||
return self.__class__(os.path.dirname(self))
|
||||
|
||||
def basename(self):
|
||||
# type: () -> str
|
||||
def basename(self) -> str:
|
||||
return os.path.basename(self)
|
||||
|
||||
def abspath(self):
|
||||
# type: () -> path
|
||||
def abspath(self) -> "path":
|
||||
"""
|
||||
Returns the absolute path.
|
||||
"""
|
||||
return self.__class__(os.path.abspath(self))
|
||||
|
||||
def isabs(self):
|
||||
# type: () -> bool
|
||||
def isabs(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path is absolute.
|
||||
"""
|
||||
return os.path.isabs(self)
|
||||
|
||||
def isdir(self):
|
||||
# type: () -> bool
|
||||
def isdir(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path is a directory.
|
||||
"""
|
||||
return os.path.isdir(self)
|
||||
|
||||
def isfile(self):
|
||||
# type: () -> bool
|
||||
def isfile(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path is a file.
|
||||
"""
|
||||
return os.path.isfile(self)
|
||||
|
||||
def islink(self):
|
||||
# type: () -> bool
|
||||
def islink(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path is a symbolic link.
|
||||
"""
|
||||
return os.path.islink(self)
|
||||
|
||||
def ismount(self):
|
||||
# type: () -> bool
|
||||
def ismount(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path is a mount point.
|
||||
"""
|
||||
return os.path.ismount(self)
|
||||
|
||||
def rmtree(self, ignore_errors=False, onerror=None):
|
||||
# type: (bool, Callable) -> None
|
||||
def rmtree(self, ignore_errors: bool = False, onerror: Callable = None) -> None:
|
||||
"""
|
||||
Removes the file or directory and any files or directories it may
|
||||
contain.
|
||||
@ -96,8 +85,7 @@ class path(str):
|
||||
"""
|
||||
shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
|
||||
|
||||
def copytree(self, destination, symlinks=False):
|
||||
# type: (str, bool) -> None
|
||||
def copytree(self, destination: str, symlinks: bool = False) -> None:
|
||||
"""
|
||||
Recursively copy a directory to the given `destination`. If the given
|
||||
`destination` does not exist it will be created.
|
||||
@ -109,8 +97,7 @@ class path(str):
|
||||
"""
|
||||
shutil.copytree(self, destination, symlinks=symlinks)
|
||||
|
||||
def movetree(self, destination):
|
||||
# type: (str) -> None
|
||||
def movetree(self, destination: str) -> None:
|
||||
"""
|
||||
Recursively move the file or directory to the given `destination`
|
||||
similar to the Unix "mv" command.
|
||||
@ -122,54 +109,46 @@ class path(str):
|
||||
|
||||
move = movetree
|
||||
|
||||
def unlink(self):
|
||||
# type: () -> None
|
||||
def unlink(self) -> None:
|
||||
"""
|
||||
Removes a file.
|
||||
"""
|
||||
os.unlink(self)
|
||||
|
||||
def stat(self):
|
||||
# type: () -> Any
|
||||
def stat(self) -> Any:
|
||||
"""
|
||||
Returns a stat of the file.
|
||||
"""
|
||||
return os.stat(self)
|
||||
|
||||
def utime(self, arg):
|
||||
# type: (Any) -> None
|
||||
def utime(self, arg: Any) -> None:
|
||||
os.utime(self, arg)
|
||||
|
||||
def open(self, mode='r', **kwargs):
|
||||
# type: (str, Any) -> IO
|
||||
def open(self, mode: str = 'r', **kwargs) -> IO:
|
||||
return open(self, mode, **kwargs)
|
||||
|
||||
def write_text(self, text, encoding='utf-8', **kwargs):
|
||||
# type: (str, str, Any) -> None
|
||||
def write_text(self, text: str, encoding: str = 'utf-8', **kwargs) -> None:
|
||||
"""
|
||||
Writes the given `text` to the file.
|
||||
"""
|
||||
with open(self, 'w', encoding=encoding, **kwargs) as f:
|
||||
f.write(text)
|
||||
|
||||
def text(self, encoding='utf-8', **kwargs):
|
||||
# type: (str, Any) -> str
|
||||
def text(self, encoding: str = 'utf-8', **kwargs) -> str:
|
||||
"""
|
||||
Returns the text in the file.
|
||||
"""
|
||||
with open(self, encoding=encoding, **kwargs) as f:
|
||||
return f.read()
|
||||
|
||||
def bytes(self):
|
||||
# type: () -> builtins.bytes
|
||||
def bytes(self) -> builtins.bytes:
|
||||
"""
|
||||
Returns the bytes in the file.
|
||||
"""
|
||||
with open(self, mode='rb') as f:
|
||||
return f.read()
|
||||
|
||||
def write_bytes(self, bytes, append=False):
|
||||
# type: (str, bool) -> None
|
||||
def write_bytes(self, bytes: str, append: bool = False) -> None:
|
||||
"""
|
||||
Writes the given `bytes` to the file.
|
||||
|
||||
@ -183,41 +162,35 @@ class path(str):
|
||||
with open(self, mode=mode) as f:
|
||||
f.write(bytes)
|
||||
|
||||
def exists(self):
|
||||
# type: () -> bool
|
||||
def exists(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path exist.
|
||||
"""
|
||||
return os.path.exists(self)
|
||||
|
||||
def lexists(self):
|
||||
# type: () -> bool
|
||||
def lexists(self) -> bool:
|
||||
"""
|
||||
Returns ``True`` if the path exists unless it is a broken symbolic
|
||||
link.
|
||||
"""
|
||||
return os.path.lexists(self)
|
||||
|
||||
def makedirs(self, mode=0o777, exist_ok=False):
|
||||
# type: (int, bool) -> None
|
||||
def makedirs(self, mode: int = 0o777, exist_ok: bool = False) -> None:
|
||||
"""
|
||||
Recursively create directories.
|
||||
"""
|
||||
os.makedirs(self, mode, exist_ok=exist_ok)
|
||||
|
||||
def joinpath(self, *args):
|
||||
# type: (Any) -> path
|
||||
def joinpath(self, *args) -> "path":
|
||||
"""
|
||||
Joins the path with the argument given and returns the result.
|
||||
"""
|
||||
return self.__class__(os.path.join(self, *map(self.__class__, args)))
|
||||
|
||||
def listdir(self):
|
||||
# type: () -> List[str]
|
||||
def listdir(self) -> List[str]:
|
||||
return os.listdir(self)
|
||||
|
||||
__div__ = __truediv__ = joinpath
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return '%s(%s)' % (self.__class__.__name__, super().__repr__())
|
||||
|
@ -8,21 +8,16 @@
|
||||
|
||||
from os import path
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.core import publish_doctree
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.io import SphinxStandaloneReader
|
||||
from sphinx.parsers import RSTParser
|
||||
from sphinx.util.docutils import sphinx_domains
|
||||
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from docutils import nodes # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
def parse(app, text, docname='index'):
|
||||
# type: (Sphinx, str, str) -> nodes.document
|
||||
def parse(app: Sphinx, text: str, docname: str = 'index') -> nodes.document:
|
||||
"""Parse a string as reStructuredText with Sphinx application."""
|
||||
try:
|
||||
app.env.temp_data['docname'] = docname
|
||||
|
@ -11,6 +11,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from typing import Any, Dict, Generator, IO, List, Pattern
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from docutils import nodes
|
||||
@ -23,10 +24,6 @@ from sphinx.pycode import ModuleAnalyzer
|
||||
from sphinx.testing.path import path
|
||||
from sphinx.util.osutil import relpath
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, Generator, IO, List, Pattern # NOQA
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Struct',
|
||||
@ -35,26 +32,22 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
def assert_re_search(regex, text, flags=0):
|
||||
# type: (Pattern, str, int) -> None
|
||||
def assert_re_search(regex: Pattern, text: str, flags: int = 0) -> None:
|
||||
if not re.search(regex, text, flags):
|
||||
assert False, '%r did not match %r' % (regex, text)
|
||||
|
||||
|
||||
def assert_not_re_search(regex, text, flags=0):
|
||||
# type: (Pattern, str, int) -> None
|
||||
def assert_not_re_search(regex: Pattern, text: str, flags: int = 0) -> None:
|
||||
if re.search(regex, text, flags):
|
||||
assert False, '%r did match %r' % (regex, text)
|
||||
|
||||
|
||||
def assert_startswith(thing, prefix):
|
||||
# type: (str, str) -> None
|
||||
def assert_startswith(thing: str, prefix: str) -> None:
|
||||
if not thing.startswith(prefix):
|
||||
assert False, '%r does not start with %r' % (thing, prefix)
|
||||
|
||||
|
||||
def assert_node(node, cls=None, xpath="", **kwargs):
|
||||
# type: (nodes.Node, Any, str, Any) -> None
|
||||
def assert_node(node: nodes.Node, cls: Any = None, xpath: str = "", **kwargs) -> None:
|
||||
if cls:
|
||||
if isinstance(cls, list):
|
||||
assert_node(node, cls[0], xpath=xpath, **kwargs)
|
||||
@ -92,16 +85,14 @@ def assert_node(node, cls=None, xpath="", **kwargs):
|
||||
'The node%s[%s] is not %r: %r' % (xpath, key, value, node[key])
|
||||
|
||||
|
||||
def etree_parse(path):
|
||||
# type: (str) -> Any
|
||||
def etree_parse(path: str) -> Any:
|
||||
with warnings.catch_warnings(record=False):
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
return ElementTree.parse(path)
|
||||
|
||||
|
||||
class Struct:
|
||||
def __init__(self, **kwds):
|
||||
# type: (Any) -> None
|
||||
def __init__(self, **kwds) -> None:
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
|
||||
@ -111,10 +102,9 @@ class SphinxTestApp(application.Sphinx):
|
||||
better default values for the initialization parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, buildername='html', srcdir=None,
|
||||
freshenv=False, confoverrides=None, status=None, warning=None,
|
||||
tags=None, docutilsconf=None):
|
||||
# type: (str, path, bool, Dict, IO, IO, List[str], str) -> None
|
||||
def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False,
|
||||
confoverrides: Dict = None, status: IO = None, warning: IO = None,
|
||||
tags: List[str] = None, docutilsconf: str = None) -> None:
|
||||
|
||||
if docutilsconf is not None:
|
||||
(srcdir / 'docutils.conf').write_text(docutilsconf)
|
||||
@ -144,8 +134,7 @@ class SphinxTestApp(application.Sphinx):
|
||||
self.cleanup()
|
||||
raise
|
||||
|
||||
def cleanup(self, doctrees=False):
|
||||
# type: (bool) -> None
|
||||
def cleanup(self, doctrees: bool = False) -> None:
|
||||
ModuleAnalyzer.cache.clear()
|
||||
LaTeXBuilder.usepackages = []
|
||||
locale.translators.clear()
|
||||
@ -159,8 +148,7 @@ class SphinxTestApp(application.Sphinx):
|
||||
delattr(nodes.GenericNodeVisitor, 'visit_' + method[6:])
|
||||
delattr(nodes.GenericNodeVisitor, 'depart_' + method[6:])
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name)
|
||||
|
||||
|
||||
@ -171,16 +159,13 @@ class SphinxTestAppWrapperForSkipBuilding:
|
||||
file.
|
||||
"""
|
||||
|
||||
def __init__(self, app_):
|
||||
# type: (SphinxTestApp) -> None
|
||||
def __init__(self, app_: SphinxTestApp) -> None:
|
||||
self.app = app_
|
||||
|
||||
def __getattr__(self, name):
|
||||
# type: (str) -> Any
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self.app, name)
|
||||
|
||||
def build(self, *args, **kw):
|
||||
# type: (Any, Any) -> None
|
||||
def build(self, *args, **kw) -> None:
|
||||
if not self.app.outdir.listdir(): # type: ignore
|
||||
# if listdir is empty, do build.
|
||||
self.app.build(*args, **kw)
|
||||
@ -190,15 +175,13 @@ class SphinxTestAppWrapperForSkipBuilding:
|
||||
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
|
||||
|
||||
|
||||
def remove_unicode_literals(s):
|
||||
# type: (str) -> str
|
||||
def remove_unicode_literals(s: str) -> str:
|
||||
warnings.warn('remove_unicode_literals() is deprecated.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)
|
||||
|
||||
|
||||
def find_files(root, suffix=None):
|
||||
# type: (str, bool) -> Generator
|
||||
def find_files(root: str, suffix: bool = None) -> Generator[str, None, None]:
|
||||
for dirpath, dirs, files in os.walk(root, followlinks=True):
|
||||
dirpath = path(dirpath)
|
||||
for f in [f for f in files if not suffix or f.endswith(suffix)]: # type: ignore
|
||||
@ -206,6 +189,5 @@ def find_files(root, suffix=None):
|
||||
yield relpath(fpath, root)
|
||||
|
||||
|
||||
def strip_escseq(text):
|
||||
# type: (str) -> str
|
||||
def strip_escseq(text: str) -> str:
|
||||
return re.sub('\x1b.*?m', '', text)
|
||||
|
@ -413,6 +413,18 @@
|
||||
\newcommand\sphinxsetup[1]{\setkeys{sphinx}{#1}}
|
||||
|
||||
|
||||
%% ALPHANUMERIC LIST ITEMS
|
||||
\newcommand\sphinxsetlistlabels[5]
|
||||
{% #1 = style, #2 = enum, #3 = enumnext, #4 = prefix, #5 = suffix
|
||||
% #2 and #3 are counters used by enumerate environement e.g. enumi, enumii.
|
||||
% #1 is a macro such as \arabic or \alph
|
||||
% prefix and suffix are strings (by default empty and a dot).
|
||||
\@namedef{the#2}{#1{#2}}%
|
||||
\@namedef{label#2}{#4\@nameuse{the#2}#5}%
|
||||
\@namedef{p@#3}{\@nameuse{p@#2}#4\@nameuse{the#2}#5}%
|
||||
}%
|
||||
|
||||
|
||||
%% MAXLISTDEPTH
|
||||
%
|
||||
% remove LaTeX's cap on nesting depth if 'maxlistdepth' key used.
|
||||
|
@ -33,7 +33,7 @@
|
||||
containing fewer words won't appear in the result list.{% endtrans %}
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="text" name="q" aria-labelledby="search-documentation" value="" />
|
||||
<input type="submit" value="{{ _('search') }}" />
|
||||
<span id="search-progress" style="padding-left: 10px"></span>
|
||||
</form>
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -15,5 +15,3 @@ class Derived(Base):
|
||||
def inheritedmeth(self):
|
||||
# no docstring here
|
||||
pass
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -23,8 +23,8 @@ link to external1_ and external2_.
|
||||
|
||||
link to `Sphinx Site <http://sphinx-doc.org>`_ and `Python Site <http://python.org>`_.
|
||||
|
||||
.. _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
|
||||
|
@ -6,11 +6,11 @@ This is from CPython documentation.
|
||||
|
||||
Some additional anchors to exercise ignore code
|
||||
|
||||
* `Example Bar invalid <http://example.com/#!bar>`_
|
||||
* `Example Bar invalid <http://example.com#!bar>`_ tests that default ignore anchor of #! does not need to be prefixed with /
|
||||
* `Example Bar invalid <http://example.com/#top>`_
|
||||
* `Example Bar invalid <https://www.google.com/#!bar>`_
|
||||
* `Example Bar invalid <https://www.google.com#!bar>`_ tests that default ignore anchor of #! does not need to be prefixed with /
|
||||
* `Example Bar invalid <https://www.google.com/#top>`_
|
||||
* `Example anchor invalid <http://www.sphinx-doc.org/en/1.7/intro.html#does-not-exist>`_
|
||||
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
|
||||
|
||||
.. 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
|
||||
|
@ -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
|
||||
|
@ -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']
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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'})
|
||||
|
@ -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()
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -107,7 +107,7 @@ def test_text_emit_warnings(app, warning):
|
||||
# test warnings in translation
|
||||
warnings = getwarning(warning)
|
||||
warning_expr = ('.*/warnings.txt:4:<translated>: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', '.'],
|
||||
|
@ -279,9 +279,9 @@ def get_verifier(verify, verify_re):
|
||||
(
|
||||
# in URIs
|
||||
'verify_re',
|
||||
'`test <http://example.com/~me/>`_',
|
||||
'`test <https://www.google.com/~me/>`_',
|
||||
None,
|
||||
r'\\sphinxhref{http://example.com/~me/}{test}.*',
|
||||
r'\\sphinxhref{https://www.google.com/~me/}{test}.*',
|
||||
),
|
||||
(
|
||||
# description list: simple
|
||||
|
@ -13,7 +13,6 @@ import datetime
|
||||
import functools
|
||||
import sys
|
||||
import types
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -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_
|
||||
|
Loading…
Reference in New Issue
Block a user