2008-01-27 14:23:25 -06:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
sphinx.application
|
|
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
|
2017-10-22 08:59:09 -05:00
|
|
|
Sphinx application class and extensibility interface.
|
2008-01-27 14:23:25 -06:00
|
|
|
|
|
|
|
Gracefully adapted from the TextPress system by Armin.
|
|
|
|
|
2017-12-31 10:06:58 -06:00
|
|
|
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
2008-12-27 05:19:17 -06:00
|
|
|
:license: BSD, see LICENSE for details.
|
2008-01-27 14:23:25 -06:00
|
|
|
"""
|
2014-01-19 04:17:10 -06:00
|
|
|
from __future__ import print_function
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2013-01-03 13:34:47 -06:00
|
|
|
import os
|
2018-01-27 10:52:16 -06:00
|
|
|
import posixpath
|
2008-01-27 14:23:25 -06:00
|
|
|
import sys
|
2016-12-20 08:13:41 -06:00
|
|
|
import warnings
|
2014-09-17 05:22:14 -05:00
|
|
|
from collections import deque
|
2018-02-11 19:23:01 -06:00
|
|
|
from inspect import isclass
|
2018-01-27 10:52:16 -06:00
|
|
|
from os import path
|
2016-11-08 20:45:27 -06:00
|
|
|
|
2018-02-05 09:45:49 -06:00
|
|
|
from docutils.parsers.rst import Directive, directives, roles
|
2018-02-10 02:24:49 -06:00
|
|
|
from six import itervalues
|
2018-01-27 10:52:16 -06:00
|
|
|
from six.moves import cStringIO
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2009-02-12 05:46:25 -06:00
|
|
|
import sphinx
|
2017-03-25 20:58:17 -05:00
|
|
|
from sphinx import package_dir, locale
|
2009-02-12 05:46:25 -06:00
|
|
|
from sphinx.config import Config
|
2018-02-05 09:36:55 -06:00
|
|
|
from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning
|
2016-02-02 17:57:54 -06:00
|
|
|
from sphinx.environment import BuildEnvironment
|
2018-01-31 05:40:56 -06:00
|
|
|
from sphinx.errors import (
|
|
|
|
ApplicationError, ConfigError, ExtensionError, VersionRequirementError
|
|
|
|
)
|
2017-03-11 00:25:20 -06:00
|
|
|
from sphinx.events import EventManager
|
2017-06-25 03:35:42 -05:00
|
|
|
from sphinx.locale import __
|
2017-04-23 02:29:38 -05:00
|
|
|
from sphinx.registry import SphinxComponentRegistry
|
2018-02-10 06:04:30 -06:00
|
|
|
from sphinx.util import docutils
|
2015-03-08 07:11:28 -05:00
|
|
|
from sphinx.util import import_object
|
2016-12-20 08:13:41 -06:00
|
|
|
from sphinx.util import logging
|
2018-01-27 10:52:16 -06:00
|
|
|
from sphinx.util import pycompat # noqa: F401
|
2018-02-16 04:26:54 -06:00
|
|
|
from sphinx.util.build_phase import BuildPhase
|
2017-04-27 07:49:19 -05:00
|
|
|
from sphinx.util.console import bold # type: ignore
|
2018-02-10 02:24:49 -06:00
|
|
|
from sphinx.util.docutils import directive_helper
|
2016-05-05 09:26:19 -05:00
|
|
|
from sphinx.util.i18n import find_catalog_source_files
|
2018-02-05 09:15:29 -06:00
|
|
|
from sphinx.util.osutil import abspath, ensuredir
|
2018-01-27 10:52:16 -06:00
|
|
|
from sphinx.util.tags import Tags
|
2009-02-12 05:46:25 -06:00
|
|
|
|
2018-03-13 09:01:11 -05:00
|
|
|
if False:
|
|
|
|
# For type annotation
|
2017-03-02 21:19:09 -06:00
|
|
|
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Tuple, Type, Union # NOQA
|
2018-02-10 06:04:30 -06:00
|
|
|
from docutils import nodes # NOQA
|
2016-11-08 20:45:27 -06:00
|
|
|
from docutils.parsers import Parser # NOQA
|
2018-03-13 12:07:53 -05:00
|
|
|
from docutils.transforms import Transform # NOQA
|
2016-11-08 20:45:27 -06:00
|
|
|
from sphinx.builders import Builder # NOQA
|
2017-01-05 20:34:10 -06:00
|
|
|
from sphinx.domains import Domain, Index # NOQA
|
2016-12-17 05:40:48 -06:00
|
|
|
from sphinx.environment.collectors import EnvironmentCollector # NOQA
|
2017-03-25 20:58:17 -05:00
|
|
|
from sphinx.extension import Extension # NOQA
|
2017-12-15 07:17:03 -06:00
|
|
|
from sphinx.roles import XRefRole # NOQA
|
2017-04-20 07:31:03 -05:00
|
|
|
from sphinx.theming import Theme # NOQA
|
2018-02-10 01:59:43 -06:00
|
|
|
from sphinx.util.typing import RoleFunction, TitleGetter # NOQA
|
2016-11-08 20:45:27 -06:00
|
|
|
|
2016-07-03 20:46:27 -05:00
|
|
|
builtin_extensions = (
|
|
|
|
'sphinx.builders.applehelp',
|
|
|
|
'sphinx.builders.changes',
|
|
|
|
'sphinx.builders.epub3',
|
|
|
|
'sphinx.builders.devhelp',
|
|
|
|
'sphinx.builders.dummy',
|
|
|
|
'sphinx.builders.gettext',
|
|
|
|
'sphinx.builders.html',
|
|
|
|
'sphinx.builders.htmlhelp',
|
|
|
|
'sphinx.builders.latex',
|
|
|
|
'sphinx.builders.linkcheck',
|
|
|
|
'sphinx.builders.manpage',
|
|
|
|
'sphinx.builders.qthelp',
|
|
|
|
'sphinx.builders.texinfo',
|
|
|
|
'sphinx.builders.text',
|
|
|
|
'sphinx.builders.websupport',
|
|
|
|
'sphinx.builders.xml',
|
2018-01-20 07:28:46 -06:00
|
|
|
'sphinx.config',
|
2016-07-03 20:46:27 -05:00
|
|
|
'sphinx.domains.c',
|
|
|
|
'sphinx.domains.cpp',
|
|
|
|
'sphinx.domains.javascript',
|
|
|
|
'sphinx.domains.python',
|
|
|
|
'sphinx.domains.rst',
|
|
|
|
'sphinx.domains.std',
|
2016-07-27 21:23:10 -05:00
|
|
|
'sphinx.directives',
|
|
|
|
'sphinx.directives.code',
|
|
|
|
'sphinx.directives.other',
|
|
|
|
'sphinx.directives.patches',
|
2018-01-27 07:07:27 -06:00
|
|
|
'sphinx.extension',
|
2017-11-14 23:48:30 -06:00
|
|
|
'sphinx.io',
|
2017-05-28 08:08:52 -05:00
|
|
|
'sphinx.parsers',
|
2018-01-23 09:48:02 -06:00
|
|
|
'sphinx.registry',
|
2016-07-27 21:23:10 -05:00
|
|
|
'sphinx.roles',
|
2017-03-05 22:06:30 -06:00
|
|
|
'sphinx.transforms.post_transforms',
|
2017-03-27 09:46:11 -05:00
|
|
|
'sphinx.transforms.post_transforms.images',
|
2017-10-29 23:46:12 -05:00
|
|
|
'sphinx.util.compat',
|
2016-12-17 09:12:54 -06:00
|
|
|
# collectors should be loaded by specific order
|
|
|
|
'sphinx.environment.collectors.dependencies',
|
|
|
|
'sphinx.environment.collectors.asset',
|
2016-12-17 09:22:37 -06:00
|
|
|
'sphinx.environment.collectors.metadata',
|
2016-12-17 09:36:28 -06:00
|
|
|
'sphinx.environment.collectors.title',
|
2017-01-10 06:43:31 -06:00
|
|
|
'sphinx.environment.collectors.toctree',
|
2017-01-10 09:55:57 -06:00
|
|
|
'sphinx.environment.collectors.indexentries',
|
2017-03-21 19:57:25 -05:00
|
|
|
# Strictly, alabaster theme is not a builtin extension,
|
|
|
|
# but it is loaded automatically to use it as default theme.
|
|
|
|
'alabaster',
|
2016-11-08 20:45:27 -06:00
|
|
|
) # type: Tuple[unicode, ...]
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2008-06-05 03:58:43 -05:00
|
|
|
CONFIG_FILENAME = 'conf.py'
|
2009-07-13 11:02:42 -05:00
|
|
|
ENV_PICKLE_FILENAME = 'environment.pickle'
|
2008-06-05 03:58:43 -05:00
|
|
|
|
2016-12-20 08:13:41 -06:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2009-02-24 12:15:39 -06:00
|
|
|
|
2008-02-23 09:24:30 -06:00
|
|
|
class Sphinx(object):
|
2017-10-22 08:59:09 -05:00
|
|
|
"""The main application class and extensibility interface.
|
|
|
|
|
|
|
|
:ivar srcdir: Directory containing source.
|
|
|
|
:ivar confdir: Directory containing ``conf.py``.
|
|
|
|
:ivar doctreedir: Directory for storing pickled doctrees.
|
|
|
|
:ivar outdir: Directory for storing build documents.
|
|
|
|
"""
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2008-05-02 05:32:08 -05:00
|
|
|
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
|
2010-02-21 08:36:26 -06:00
|
|
|
confoverrides=None, status=sys.stdout, warning=sys.stderr,
|
2013-01-13 07:15:45 -06:00
|
|
|
freshenv=False, warningiserror=False, tags=None, verbosity=0,
|
|
|
|
parallel=0):
|
2017-02-07 23:31:59 -06:00
|
|
|
# type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA
|
2018-02-16 04:26:54 -06:00
|
|
|
self.phase = BuildPhase.INITIALIZATION
|
2013-01-05 02:41:12 -06:00
|
|
|
self.verbosity = verbosity
|
2017-03-21 19:57:25 -05:00
|
|
|
self.extensions = {} # type: Dict[unicode, Extension]
|
2016-11-08 20:45:27 -06:00
|
|
|
self._setting_up_extension = ['?'] # type: List[unicode]
|
|
|
|
self.builder = None # type: Builder
|
|
|
|
self.env = None # type: BuildEnvironment
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry = SphinxComponentRegistry()
|
2017-04-20 06:19:55 -05:00
|
|
|
self.html_themes = {} # type: Dict[unicode, unicode]
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2018-01-14 15:55:51 -06:00
|
|
|
# validate provided directories
|
2018-02-17 21:39:07 -06:00
|
|
|
self.srcdir = abspath(srcdir) # type: unicode
|
|
|
|
self.outdir = abspath(outdir) # type: unicode
|
|
|
|
self.doctreedir = abspath(doctreedir) # type: unicode
|
2008-08-04 17:20:44 -05:00
|
|
|
self.confdir = confdir
|
2018-01-14 15:55:51 -06:00
|
|
|
if self.confdir: # confdir is optional
|
|
|
|
self.confdir = abspath(self.confdir)
|
|
|
|
if not path.isfile(path.join(self.confdir, 'conf.py')):
|
2018-02-25 07:16:09 -06:00
|
|
|
raise ApplicationError(__("config directory doesn't contain a "
|
|
|
|
"conf.py file (%s)") % confdir)
|
2018-01-14 15:55:51 -06:00
|
|
|
|
|
|
|
if not path.isdir(self.srcdir):
|
2018-02-25 07:16:09 -06:00
|
|
|
raise ApplicationError(__('Cannot find source directory (%s)') %
|
2018-01-14 15:55:51 -06:00
|
|
|
self.srcdir)
|
|
|
|
|
|
|
|
if self.srcdir == self.outdir:
|
2018-02-25 07:16:09 -06:00
|
|
|
raise ApplicationError(__('Source directory and destination '
|
|
|
|
'directory cannot be identical'))
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2013-01-13 07:15:45 -06:00
|
|
|
self.parallel = parallel
|
|
|
|
|
2008-11-08 09:28:01 -06:00
|
|
|
if status is None:
|
2016-11-08 20:45:27 -06:00
|
|
|
self._status = cStringIO() # type: IO
|
2008-11-08 09:28:01 -06:00
|
|
|
self.quiet = True
|
|
|
|
else:
|
|
|
|
self._status = status
|
|
|
|
self.quiet = False
|
2009-02-17 12:19:09 -06:00
|
|
|
|
2008-11-08 09:28:01 -06:00
|
|
|
if warning is None:
|
2016-11-08 20:45:27 -06:00
|
|
|
self._warning = cStringIO() # type: IO
|
2008-11-08 09:28:01 -06:00
|
|
|
else:
|
|
|
|
self._warning = warning
|
2008-02-09 17:09:36 -06:00
|
|
|
self._warncount = 0
|
2009-02-17 12:19:09 -06:00
|
|
|
self.warningiserror = warningiserror
|
2016-12-20 08:13:41 -06:00
|
|
|
logging.setup(self, self._status, self._warning)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2017-03-11 00:25:20 -06:00
|
|
|
self.events = EventManager()
|
2008-03-09 16:31:52 -05:00
|
|
|
|
2014-09-17 05:22:14 -05:00
|
|
|
# keep last few messages for traceback
|
2017-03-26 04:53:08 -05:00
|
|
|
# This will be filled by sphinx.util.logging.LastMessagesWriter
|
2016-11-08 20:45:27 -06:00
|
|
|
self.messagelog = deque(maxlen=10) # type: deque
|
2014-09-17 05:22:14 -05:00
|
|
|
|
2009-03-15 12:36:00 -05:00
|
|
|
# say hello to the world
|
2018-02-25 07:16:09 -06:00
|
|
|
logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__))
|
2009-03-15 12:36:00 -05:00
|
|
|
|
2008-11-08 09:28:01 -06:00
|
|
|
# status code for command-line application
|
|
|
|
self.statuscode = 0
|
|
|
|
|
2008-01-27 14:23:25 -06:00
|
|
|
# read config
|
2009-02-19 14:56:34 -06:00
|
|
|
self.tags = Tags(tags)
|
2018-03-20 08:52:10 -05:00
|
|
|
self.config = Config(self.confdir, CONFIG_FILENAME,
|
2010-02-21 08:36:26 -06:00
|
|
|
confoverrides or {}, self.tags)
|
2016-12-20 08:21:30 -06:00
|
|
|
self.config.check_unicode()
|
2014-10-30 09:46:02 -05:00
|
|
|
# defer checking types until i18n has been initialized
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2017-03-08 21:23:23 -06:00
|
|
|
# initialize some limited config variables before initialize i18n and loading
|
|
|
|
# extensions
|
2016-12-20 08:21:30 -06:00
|
|
|
self.config.pre_init_values()
|
2016-04-16 22:40:09 -05:00
|
|
|
|
2017-03-08 21:23:23 -06:00
|
|
|
# set up translation infrastructure
|
|
|
|
self._init_i18n()
|
|
|
|
|
2016-04-16 02:55:59 -05:00
|
|
|
# check the Sphinx version if requested
|
2016-04-16 22:40:09 -05:00
|
|
|
if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
|
2016-04-16 02:55:59 -05:00
|
|
|
raise VersionRequirementError(
|
2017-06-25 03:35:42 -05:00
|
|
|
__('This project needs at least Sphinx v%s and therefore cannot '
|
|
|
|
'be built with this version.') % self.config.needs_sphinx)
|
2016-04-16 02:55:59 -05:00
|
|
|
|
2010-02-23 14:35:48 -06:00
|
|
|
# set confdir to srcdir if -C given (!= no confdir); a few pieces
|
|
|
|
# of code expect a confdir to be set
|
|
|
|
if self.confdir is None:
|
|
|
|
self.confdir = self.srcdir
|
|
|
|
|
2016-07-03 20:46:27 -05:00
|
|
|
# load all built-in extension modules
|
|
|
|
for extension in builtin_extensions:
|
|
|
|
self.setup_extension(extension)
|
|
|
|
|
2010-07-02 04:44:01 -05:00
|
|
|
# load all user-given extension modules
|
2008-05-24 13:03:56 -05:00
|
|
|
for extension in self.config.extensions:
|
2008-01-27 14:23:25 -06:00
|
|
|
self.setup_extension(extension)
|
2016-12-12 08:19:48 -06:00
|
|
|
|
|
|
|
# preload builder module (before init config values)
|
|
|
|
self.preload_builder(buildername)
|
|
|
|
|
2018-01-07 05:04:53 -06:00
|
|
|
if not path.isdir(outdir):
|
2018-02-25 07:16:09 -06:00
|
|
|
logger.info(__('making output directory...'))
|
2018-01-07 05:04:53 -06:00
|
|
|
ensuredir(outdir)
|
|
|
|
|
2008-04-06 12:38:55 -05:00
|
|
|
# the config file itself can be an extension
|
2008-05-24 13:03:56 -05:00
|
|
|
if self.config.setup:
|
2015-07-22 12:29:22 -05:00
|
|
|
self._setting_up_extension = ['conf.py']
|
2018-01-24 06:30:33 -06:00
|
|
|
if callable(self.config.setup):
|
2017-03-21 20:21:54 -05:00
|
|
|
self.config.setup(self)
|
|
|
|
else:
|
|
|
|
raise ConfigError(
|
2017-06-25 03:35:42 -05:00
|
|
|
__("'setup' as currently defined in conf.py isn't a Python callable. "
|
|
|
|
"Please modify its definition to make it a callable function. This is "
|
|
|
|
"needed for conf.py to behave as a Sphinx extension.")
|
2017-03-21 20:21:54 -05:00
|
|
|
)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2008-06-04 15:25:27 -05:00
|
|
|
# now that we know all config values, collect them from conf.py
|
2016-12-20 08:21:30 -06:00
|
|
|
self.config.init_values()
|
2018-01-21 00:44:37 -06:00
|
|
|
self.emit('config-inited', self.config)
|
2008-06-04 15:25:27 -05:00
|
|
|
|
2016-06-11 01:36:30 -05:00
|
|
|
# check primary_domain if requested
|
2017-03-09 04:25:42 -06:00
|
|
|
primary_domain = self.config.primary_domain
|
2017-04-23 02:29:38 -05:00
|
|
|
if primary_domain and not self.registry.has_domain(primary_domain):
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.warning(__('primary_domain %r not found, ignored.'), primary_domain)
|
2016-06-11 01:36:30 -05:00
|
|
|
|
2017-03-19 09:16:37 -05:00
|
|
|
# create the builder
|
|
|
|
self.builder = self.create_builder(buildername)
|
2014-10-30 09:46:02 -05:00
|
|
|
# check all configuration values for permissible types
|
2016-12-20 08:21:30 -06:00
|
|
|
self.config.check_types()
|
2009-07-13 11:02:42 -05:00
|
|
|
# set up the build environment
|
|
|
|
self._init_env(freshenv)
|
|
|
|
# set up the builder
|
2017-03-19 09:16:37 -05:00
|
|
|
self._init_builder()
|
2009-07-13 11:02:42 -05:00
|
|
|
|
|
|
|
def _init_i18n(self):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: () -> None
|
2010-08-22 04:36:08 -05:00
|
|
|
"""Load translated strings from the configured localedirs if enabled in
|
|
|
|
the configuration.
|
2009-07-13 11:02:42 -05:00
|
|
|
"""
|
|
|
|
if self.config.language is not None:
|
2018-02-25 07:16:09 -06:00
|
|
|
logger.info(bold(__('loading translations [%s]... ') % self.config.language),
|
2016-12-21 22:41:56 -06:00
|
|
|
nonl=True)
|
2016-05-05 09:26:19 -05:00
|
|
|
user_locale_dirs = [
|
|
|
|
path.join(self.srcdir, x) for x in self.config.locale_dirs]
|
|
|
|
# compile mo files if sphinx.po file in user locale directories are updated
|
|
|
|
for catinfo in find_catalog_source_files(
|
|
|
|
user_locale_dirs, self.config.language, domains=['sphinx'],
|
|
|
|
charset=self.config.source_encoding):
|
|
|
|
catinfo.write_mo(self.config.language)
|
2017-07-15 02:47:32 -05:00
|
|
|
locale_dirs = [None, path.join(package_dir, 'locale')] + user_locale_dirs # type: ignore # NOQA
|
2009-07-13 11:02:42 -05:00
|
|
|
else:
|
|
|
|
locale_dirs = []
|
2018-02-25 10:49:49 -06:00
|
|
|
self.translator, has_translation = locale.init(locale_dirs, self.config.language) # type: ignore # NOQA
|
2009-07-13 11:02:42 -05:00
|
|
|
if self.config.language is not None:
|
2013-09-16 00:26:46 -05:00
|
|
|
if has_translation or self.config.language == 'en':
|
|
|
|
# "en" never needs to be translated
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.info(__('done'))
|
2009-07-13 11:02:42 -05:00
|
|
|
else:
|
2018-02-25 07:16:09 -06:00
|
|
|
logger.info(__('not available for built-in messages'))
|
2009-07-13 09:59:45 -05:00
|
|
|
|
2009-07-13 11:02:42 -05:00
|
|
|
def _init_env(self, freshenv):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (bool) -> None
|
2018-02-05 09:15:29 -06:00
|
|
|
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
|
|
|
if freshenv or not os.path.exists(filename):
|
2017-03-05 09:30:12 -06:00
|
|
|
self.env = BuildEnvironment(self)
|
2017-03-19 11:20:19 -05:00
|
|
|
self.env.find_files(self.config, self.builder)
|
2017-04-23 02:29:38 -05:00
|
|
|
for domain in self.registry.create_domains(self.env):
|
2017-03-09 04:25:42 -06:00
|
|
|
self.env.domains[domain.name] = domain
|
2009-07-13 11:02:42 -05:00
|
|
|
else:
|
|
|
|
try:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.info(bold(__('loading pickled environment... ')), nonl=True)
|
2017-03-05 09:30:12 -06:00
|
|
|
self.env = BuildEnvironment.frompickle(filename, self)
|
2018-01-24 06:37:54 -06:00
|
|
|
needed, reason = self.env.need_refresh(self)
|
|
|
|
if needed:
|
|
|
|
raise IOError(reason)
|
2009-07-13 13:53:11 -05:00
|
|
|
self.env.domains = {}
|
2017-04-23 02:29:38 -05:00
|
|
|
for domain in self.registry.create_domains(self.env):
|
2009-07-13 13:53:11 -05:00
|
|
|
# this can raise if the data version doesn't fit
|
2017-03-09 04:25:42 -06:00
|
|
|
self.env.domains[domain.name] = domain
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.info(__('done'))
|
2014-01-19 04:17:10 -06:00
|
|
|
except Exception as err:
|
2018-02-05 09:15:29 -06:00
|
|
|
logger.info(__('failed: %s'), err)
|
2016-11-17 05:32:45 -06:00
|
|
|
self._init_env(freshenv=True)
|
2009-07-13 13:53:11 -05:00
|
|
|
|
2017-03-08 09:21:10 -06:00
|
|
|
def preload_builder(self, name):
|
2016-12-12 08:19:48 -06:00
|
|
|
# type: (unicode) -> None
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.preload_builder(self, name)
|
2016-12-12 08:19:48 -06:00
|
|
|
|
2017-03-08 09:21:10 -06:00
|
|
|
def create_builder(self, name):
|
2016-12-12 08:19:48 -06:00
|
|
|
# type: (unicode) -> Builder
|
2017-03-08 09:21:10 -06:00
|
|
|
if name is None:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.info(__('No builder selected, using default: html'))
|
2017-03-08 09:21:10 -06:00
|
|
|
name = 'html'
|
2016-12-12 08:19:48 -06:00
|
|
|
|
2017-04-23 02:29:38 -05:00
|
|
|
return self.registry.create_builder(self, name)
|
2017-03-19 09:16:37 -05:00
|
|
|
|
|
|
|
def _init_builder(self):
|
|
|
|
# type: () -> None
|
|
|
|
self.builder.set_environment(self.env)
|
|
|
|
self.builder.init()
|
2008-01-27 14:23:25 -06:00
|
|
|
self.emit('builder-inited')
|
|
|
|
|
2013-01-12 05:35:04 -06:00
|
|
|
# ---- main "build" method -------------------------------------------------
|
|
|
|
|
2010-02-21 08:36:26 -06:00
|
|
|
def build(self, force_all=False, filenames=None):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (bool, List[unicode]) -> None
|
2018-02-16 04:26:54 -06:00
|
|
|
self.phase = BuildPhase.READING
|
2008-08-06 08:04:14 -05:00
|
|
|
try:
|
2010-02-21 08:36:26 -06:00
|
|
|
if force_all:
|
2014-08-03 02:22:08 -05:00
|
|
|
self.builder.compile_all_catalogs()
|
2008-08-06 08:04:14 -05:00
|
|
|
self.builder.build_all()
|
|
|
|
elif filenames:
|
2014-08-03 02:22:08 -05:00
|
|
|
self.builder.compile_specific_catalogs(filenames)
|
2008-08-06 08:04:14 -05:00
|
|
|
self.builder.build_specific(filenames)
|
|
|
|
else:
|
2014-08-03 02:22:08 -05:00
|
|
|
self.builder.compile_update_catalogs()
|
2008-08-06 08:04:14 -05:00
|
|
|
self.builder.build_update()
|
2014-09-22 11:16:53 -05:00
|
|
|
|
2015-03-08 10:28:23 -05:00
|
|
|
status = (self.statuscode == 0 and
|
2017-06-25 03:35:42 -05:00
|
|
|
__('succeeded') or __('finished with problems'))
|
2014-09-22 11:16:53 -05:00
|
|
|
if self._warncount:
|
2017-08-05 11:52:09 -05:00
|
|
|
logger.info(bold(__('build %s, %s warning.',
|
2018-02-25 07:16:09 -06:00
|
|
|
'build %s, %s warnings.', self._warncount) %
|
2017-08-05 11:52:09 -05:00
|
|
|
(status, self._warncount)))
|
2014-09-22 11:16:53 -05:00
|
|
|
else:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.info(bold(__('build %s.') % status))
|
2017-12-28 14:45:02 -06:00
|
|
|
|
|
|
|
if self.statuscode == 0 and self.builder.epilog:
|
|
|
|
logger.info('')
|
|
|
|
logger.info(self.builder.epilog % {
|
|
|
|
'outdir': path.relpath(self.outdir),
|
|
|
|
'project': self.config.project
|
|
|
|
})
|
2014-01-19 04:17:10 -06:00
|
|
|
except Exception as err:
|
2013-01-03 13:34:47 -06:00
|
|
|
# delete the saved env to force a fresh build next time
|
|
|
|
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
|
|
|
if path.isfile(envfile):
|
|
|
|
os.unlink(envfile)
|
2008-08-06 08:04:14 -05:00
|
|
|
self.emit('build-finished', err)
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
self.emit('build-finished', None)
|
2009-02-14 17:07:05 -06:00
|
|
|
self.builder.cleanup()
|
2008-08-06 08:04:14 -05:00
|
|
|
|
2013-01-12 05:35:04 -06:00
|
|
|
# ---- logging handling ----------------------------------------------------
|
2017-04-27 07:49:19 -05:00
|
|
|
def warn(self, message, location=None, type=None, subtype=None):
|
|
|
|
# type: (unicode, unicode, unicode, unicode) -> None
|
2014-01-20 10:21:44 -06:00
|
|
|
"""Emit a warning.
|
|
|
|
|
2018-01-21 05:15:20 -06:00
|
|
|
If *location* is given, it should either be a tuple of (*docname*,
|
|
|
|
*lineno*) or a string describing the location of the warning as well as
|
|
|
|
possible.
|
2014-01-20 10:21:44 -06:00
|
|
|
|
2018-01-21 05:15:20 -06:00
|
|
|
*type* and *subtype* are used to suppress warnings with
|
|
|
|
:confval:`suppress_warnings`.
|
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :mod:`sphinx.util.logging` instead.
|
2014-01-20 10:21:44 -06:00
|
|
|
"""
|
2016-12-24 10:14:08 -06:00
|
|
|
warnings.warn('app.warning() is now deprecated. Use sphinx.util.logging instead.',
|
|
|
|
RemovedInSphinx20Warning)
|
2016-12-20 08:13:41 -06:00
|
|
|
logger.warning(message, type=type, subtype=subtype, location=location)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
|
|
|
def info(self, message='', nonl=False):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, bool) -> None
|
2014-01-20 10:21:44 -06:00
|
|
|
"""Emit an informational message.
|
|
|
|
|
|
|
|
If *nonl* is true, don't emit a newline at the end (which implies that
|
|
|
|
more info output will follow soon.)
|
2018-01-21 05:15:20 -06:00
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :mod:`sphinx.util.logging` instead.
|
2014-01-20 10:21:44 -06:00
|
|
|
"""
|
2016-12-24 10:14:08 -06:00
|
|
|
warnings.warn('app.info() is now deprecated. Use sphinx.util.logging instead.',
|
|
|
|
RemovedInSphinx20Warning)
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.info(message, nonl=nonl)
|
2013-01-05 02:41:12 -06:00
|
|
|
|
|
|
|
def verbose(self, message, *args, **kwargs):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any, Any) -> None
|
2018-01-21 05:15:20 -06:00
|
|
|
"""Emit a verbose informational message.
|
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :mod:`sphinx.util.logging` instead.
|
|
|
|
"""
|
2016-12-24 10:14:08 -06:00
|
|
|
warnings.warn('app.verbose() is now deprecated. Use sphinx.util.logging instead.',
|
|
|
|
RemovedInSphinx20Warning)
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.verbose(message, *args, **kwargs)
|
2013-01-05 02:41:12 -06:00
|
|
|
|
|
|
|
def debug(self, message, *args, **kwargs):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any, Any) -> None
|
2018-01-21 05:15:20 -06:00
|
|
|
"""Emit a debug-level informational message.
|
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :mod:`sphinx.util.logging` instead.
|
|
|
|
"""
|
2016-12-24 10:14:08 -06:00
|
|
|
warnings.warn('app.debug() is now deprecated. Use sphinx.util.logging instead.',
|
|
|
|
RemovedInSphinx20Warning)
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug(message, *args, **kwargs)
|
2013-01-12 05:35:04 -06:00
|
|
|
|
|
|
|
def debug2(self, message, *args, **kwargs):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any, Any) -> None
|
2018-01-21 05:15:20 -06:00
|
|
|
"""Emit a lowlevel debug-level informational message.
|
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :mod:`sphinx.util.logging` instead.
|
|
|
|
"""
|
2017-01-07 00:03:06 -06:00
|
|
|
warnings.warn('app.debug2() is now deprecated. Use debug() instead.',
|
2016-12-24 10:14:08 -06:00
|
|
|
RemovedInSphinx20Warning)
|
2017-01-07 00:03:06 -06:00
|
|
|
logger.debug(message, *args, **kwargs)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2013-01-12 05:35:04 -06:00
|
|
|
# ---- general extensibility interface -------------------------------------
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2017-03-25 20:58:17 -05:00
|
|
|
def setup_extension(self, extname):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Import and setup a Sphinx extension module.
|
|
|
|
|
|
|
|
Load the extension given by the module *name*. Use this if your
|
|
|
|
extension needs the features provided by another extension. No-op if
|
|
|
|
called twice.
|
|
|
|
"""
|
2017-03-25 20:58:17 -05:00
|
|
|
logger.debug('[app] setting up extension: %r', extname)
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.load_extension(self, extname)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2010-01-17 11:24:35 -06:00
|
|
|
def require_sphinx(self, version):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Check the Sphinx version if requested.
|
|
|
|
|
|
|
|
Compare *version* (which must be a ``major.minor`` version string, e.g.
|
|
|
|
``'1.1'``) with the version of the running Sphinx, and abort the build
|
|
|
|
when it is too old.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
"""
|
2015-03-14 02:46:24 -05:00
|
|
|
if version > sphinx.__display_version__[:3]:
|
2010-01-17 11:24:35 -06:00
|
|
|
raise VersionRequirementError(version)
|
|
|
|
|
2008-01-27 14:23:25 -06:00
|
|
|
def import_object(self, objname, source=None):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (str, unicode) -> Any
|
2018-02-05 09:36:55 -06:00
|
|
|
"""Import an object from a ``module.name`` string.
|
|
|
|
|
|
|
|
.. deprecated:: 1.8
|
|
|
|
Use ``sphinx.util.import_object()`` instead.
|
|
|
|
"""
|
|
|
|
warnings.warn('app.import_object() is deprecated. '
|
|
|
|
'Use sphinx.util.add_object_type() instead.',
|
|
|
|
RemovedInSphinx30Warning)
|
2015-03-08 07:11:28 -05:00
|
|
|
return import_object(objname, source=None)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
|
|
|
# event interface
|
|
|
|
def connect(self, event, callback):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Callable) -> int
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register *callback* to be called when *event* is emitted.
|
|
|
|
|
|
|
|
For details on available core events and the arguments of callback
|
|
|
|
functions, please see :ref:`events`.
|
|
|
|
|
|
|
|
The method returns a "listener ID" that can be used as an argument to
|
|
|
|
:meth:`disconnect`.
|
|
|
|
"""
|
2017-03-11 00:25:20 -06:00
|
|
|
listener_id = self.events.connect(event, callback)
|
2017-05-11 06:19:13 -05:00
|
|
|
logger.debug('[app] connecting event %r: %r [id=%s]', event, callback, listener_id)
|
2008-01-27 14:23:25 -06:00
|
|
|
return listener_id
|
|
|
|
|
|
|
|
def disconnect(self, listener_id):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (int) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Unregister callback by *listener_id*."""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
2017-03-11 00:25:20 -06:00
|
|
|
self.events.disconnect(listener_id)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
|
|
|
def emit(self, event, *args):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any) -> List
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Emit *event* and pass *arguments* to the callback functions.
|
|
|
|
|
|
|
|
Return the return values of all callbacks as a list. Do not emit core
|
|
|
|
Sphinx events in extensions!
|
|
|
|
"""
|
2013-09-16 02:31:05 -05:00
|
|
|
try:
|
2017-01-09 03:14:53 -06:00
|
|
|
logger.debug('[app] emitting event: %r%s', event, repr(args)[:100])
|
2015-03-08 10:28:23 -05:00
|
|
|
except Exception:
|
|
|
|
# not every object likes to be repr()'d (think
|
|
|
|
# random stuff coming via autodoc)
|
2013-09-16 02:31:05 -05:00
|
|
|
pass
|
2017-03-11 00:25:20 -06:00
|
|
|
return self.events.emit(event, self, *args)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2008-08-04 04:54:45 -05:00
|
|
|
def emit_firstresult(self, event, *args):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any) -> Any
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Emit *event* and pass *arguments* to the callback functions.
|
|
|
|
|
|
|
|
Return the result of the first callback that doesn't return ``None``.
|
|
|
|
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
"""
|
2017-03-11 00:25:20 -06:00
|
|
|
return self.events.emit_firstresult(event, self, *args)
|
2008-08-04 04:54:45 -05:00
|
|
|
|
2008-01-27 14:23:25 -06:00
|
|
|
# registering addon parts
|
|
|
|
|
|
|
|
def add_builder(self, builder):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (Type[Builder]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new builder.
|
|
|
|
|
|
|
|
*builder* must be a class that inherits from
|
|
|
|
:class:`~sphinx.builders.Builder`.
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_builder(builder)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2017-10-22 08:59:09 -05:00
|
|
|
# TODO(stephenfin): Describe 'types' parameter
|
2015-09-11 02:35:46 -05:00
|
|
|
def add_config_value(self, name, default, rebuild, types=()):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any, Union[bool, unicode], Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a configuration value.
|
|
|
|
|
|
|
|
This is necessary for Sphinx to recognize new values and set default
|
|
|
|
values accordingly. The *name* should be prefixed with the extension
|
|
|
|
name, to avoid clashes. The *default* value can be any Python object.
|
|
|
|
The string value *rebuild* must be one of those values:
|
|
|
|
|
|
|
|
* ``'env'`` if a change in the setting only takes effect when a
|
|
|
|
document is parsed -- this means that the whole environment must be
|
|
|
|
rebuilt.
|
|
|
|
* ``'html'`` if a change in the setting needs a full rebuild of HTML
|
|
|
|
documents.
|
|
|
|
* ``''`` if a change in the setting will not need any special rebuild.
|
|
|
|
|
|
|
|
.. versionchanged:: 0.6
|
|
|
|
Changed *rebuild* from a simple boolean (equivalent to ``''`` or
|
|
|
|
``'env'``) to a string. However, booleans are still accepted and
|
|
|
|
converted internally.
|
|
|
|
|
|
|
|
.. versionchanged:: 0.4
|
|
|
|
If the *default* value is a callable, it will be called with the
|
|
|
|
config object as its argument in order to get the default value.
|
|
|
|
This can be used to implement config values whose default depends on
|
|
|
|
other values.
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding config value: %r',
|
|
|
|
(name, default, rebuild) + ((types,) if types else ())) # type: ignore
|
2016-11-17 05:24:58 -06:00
|
|
|
if name in self.config:
|
2017-06-25 03:35:42 -05:00
|
|
|
raise ExtensionError(__('Config value %r already present') % name)
|
2009-02-19 09:15:36 -06:00
|
|
|
if rebuild in (False, True):
|
|
|
|
rebuild = rebuild and 'env' or ''
|
2016-11-17 05:24:58 -06:00
|
|
|
self.config.add(name, default, rebuild, types)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2008-03-09 16:31:52 -05:00
|
|
|
def add_event(self, name):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register an event called *name*.
|
|
|
|
|
|
|
|
This is needed to be able to emit it.
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding event: %r', name)
|
2017-03-11 00:25:20 -06:00
|
|
|
self.events.add(name)
|
2008-03-09 16:31:52 -05:00
|
|
|
|
2014-08-04 10:18:54 -05:00
|
|
|
def set_translator(self, name, translator_class):
|
2017-03-11 22:42:12 -06:00
|
|
|
# type: (unicode, Type[nodes.NodeVisitor]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register or override a Docutils translator class.
|
|
|
|
|
|
|
|
This is used to register a custom output translator or to replace a
|
|
|
|
builtin translator. This allows extensions to use custom translator
|
|
|
|
and define custom nodes for the translator (see :meth:`add_node`).
|
|
|
|
|
|
|
|
.. versionadded:: 1.3
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_translator(name, translator_class)
|
2014-03-19 20:34:11 -05:00
|
|
|
|
2008-08-05 05:25:40 -05:00
|
|
|
def add_node(self, node, **kwds):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (nodes.Node, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils node class.
|
|
|
|
|
|
|
|
This is necessary for Docutils internals. It may also be used in the
|
|
|
|
future to validate nodes in the parsed documents.
|
|
|
|
|
|
|
|
Node visitor functions for the Sphinx HTML, LaTeX, text and manpage
|
|
|
|
writers can be given as keyword arguments: the keyword should be one or
|
|
|
|
more of ``'html'``, ``'latex'``, ``'text'``, ``'man'``, ``'texinfo'``
|
|
|
|
or any other supported translators, the value a 2-tuple of ``(visit,
|
|
|
|
depart)`` methods. ``depart`` can be ``None`` if the ``visit``
|
|
|
|
function raises :exc:`docutils.nodes.SkipNode`. Example:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
class math(docutils.nodes.Element): pass
|
|
|
|
|
|
|
|
def visit_math_html(self, node):
|
|
|
|
self.body.append(self.starttag(node, 'math'))
|
|
|
|
def depart_math_html(self, node):
|
|
|
|
self.body.append('</math>')
|
|
|
|
|
|
|
|
app.add_node(math, html=(visit_math_html, depart_math_html))
|
|
|
|
|
|
|
|
Obviously, translators for which you don't specify visitor methods will
|
|
|
|
choke on the node when encountered in a document to translate.
|
|
|
|
|
|
|
|
.. versionchanged:: 0.5
|
|
|
|
Added the support for keyword arguments giving visit functions.
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding node: %r', (node, kwds))
|
2018-02-10 06:04:30 -06:00
|
|
|
if not kwds.pop('override', False) and docutils.is_node_registered(node):
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.warning(__('while setting up extension %s: node class %r is '
|
|
|
|
'already registered, its visitors will be overridden'),
|
2016-12-20 08:21:30 -06:00
|
|
|
self._setting_up_extension, node.__name__,
|
|
|
|
type='app', subtype='add_node')
|
2018-02-10 06:04:30 -06:00
|
|
|
docutils.register_node(node)
|
2018-02-10 02:24:49 -06:00
|
|
|
self.registry.add_translation_handlers(node, **kwds)
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2016-02-13 09:38:27 -06:00
|
|
|
def add_enumerable_node(self, node, figtype, title_getter=None, **kwds):
|
2018-02-10 01:59:43 -06:00
|
|
|
# type: (nodes.Node, unicode, TitleGetter, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils node class as a numfig target.
|
|
|
|
|
|
|
|
Sphinx numbers the node automatically. And then the users can refer it
|
|
|
|
using :rst:role:`numref`.
|
|
|
|
|
|
|
|
*figtype* is a type of enumerable nodes. Each figtypes have individual
|
|
|
|
numbering sequences. As a system figtypes, ``figure``, ``table`` and
|
|
|
|
``code-block`` are defined. It is able to add custom nodes to these
|
|
|
|
default figtypes. It is also able to define new custom figtype if new
|
|
|
|
figtype is given.
|
|
|
|
|
|
|
|
*title_getter* is a getter function to obtain the title of node. It
|
|
|
|
takes an instance of the enumerable node, and it must return its title
|
|
|
|
as string. The title is used to the default title of references for
|
|
|
|
:rst:role:`ref`. By default, Sphinx searches
|
|
|
|
``docutils.nodes.caption`` or ``docutils.nodes.title`` from the node as
|
|
|
|
a title.
|
|
|
|
|
|
|
|
Other keyword arguments are used for node visitor functions. See the
|
|
|
|
:meth:`Sphinx.add_node` for details.
|
|
|
|
|
|
|
|
.. versionadded:: 1.4
|
|
|
|
"""
|
2018-02-10 01:59:43 -06:00
|
|
|
self.registry.add_enumerable_node(node, figtype, title_getter)
|
2016-02-13 06:22:21 -06:00
|
|
|
self.add_node(node, **kwds)
|
|
|
|
|
2018-02-10 01:59:43 -06:00
|
|
|
@property
|
|
|
|
def enumerable_nodes(self):
|
|
|
|
# type: () -> Dict[nodes.Node, Tuple[unicode, TitleGetter]]
|
|
|
|
warnings.warn('app.enumerable_nodes() is deprecated. '
|
|
|
|
'Use app.get_domain("std").enumerable_nodes instead.',
|
|
|
|
RemovedInSphinx30Warning)
|
|
|
|
return self.registry.enumerable_nodes
|
|
|
|
|
2009-07-28 12:45:02 -05:00
|
|
|
def add_directive(self, name, obj, content=None, arguments=None, **options):
|
2017-03-09 03:30:40 -06:00
|
|
|
# type: (unicode, Any, bool, Tuple[int, int, bool], Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils directive.
|
|
|
|
|
|
|
|
*name* must be the prospective directive name. There are two possible
|
|
|
|
ways to write a directive:
|
|
|
|
|
|
|
|
- In the docutils 0.4 style, *obj* is the directive function.
|
|
|
|
*content*, *arguments* and *options* are set as attributes on the
|
|
|
|
function and determine whether the directive has content, arguments
|
|
|
|
and options, respectively. **This style is deprecated.**
|
|
|
|
|
|
|
|
- In the docutils 0.5 style, *directiveclass* is the directive class.
|
|
|
|
It must already have attributes named *has_content*,
|
|
|
|
*required_arguments*, *optional_arguments*,
|
|
|
|
*final_argument_whitespace* and *option_spec* that correspond to the
|
|
|
|
options for the function way. See `the Docutils docs
|
|
|
|
<http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_
|
|
|
|
for details.
|
|
|
|
|
|
|
|
The directive class must inherit from the class
|
|
|
|
``docutils.parsers.rst.Directive``.
|
|
|
|
|
|
|
|
For example, the (already existing) :rst:dir:`literalinclude` directive
|
|
|
|
would be added like this:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
from docutils.parsers.rst import directives
|
|
|
|
add_directive('literalinclude', literalinclude_directive,
|
|
|
|
content = 0, arguments = (1, 0, 0),
|
|
|
|
linenos = directives.flag,
|
|
|
|
language = directives.unchanged,
|
|
|
|
encoding = directives.encoding)
|
|
|
|
|
|
|
|
.. versionchanged:: 0.6
|
|
|
|
Docutils 0.5-style directive classes are now supported.
|
2018-02-05 09:45:49 -06:00
|
|
|
.. deprecated:: 1.8
|
|
|
|
Docutils 0.4-style (function based) directives support is deprecated.
|
2017-10-22 08:59:09 -05:00
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding directive: %r',
|
|
|
|
(name, obj, content, arguments, options))
|
2015-07-21 05:42:13 -05:00
|
|
|
if name in directives._directives:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.warning(__('while setting up extension %s: directive %r is '
|
|
|
|
'already registered, it will be overridden'),
|
2016-12-20 08:21:30 -06:00
|
|
|
self._setting_up_extension[-1], name,
|
|
|
|
type='app', subtype='add_directive')
|
2008-01-27 14:23:25 -06:00
|
|
|
|
2018-02-11 19:23:01 -06:00
|
|
|
if not isclass(obj) or not issubclass(obj, Directive):
|
2018-02-10 05:06:54 -06:00
|
|
|
directive = directive_helper(obj, content, arguments, **options)
|
|
|
|
directives.register_directive(name, directive)
|
|
|
|
else:
|
|
|
|
directives.register_directive(name, obj)
|
2018-02-05 09:45:49 -06:00
|
|
|
|
2008-01-27 14:23:25 -06:00
|
|
|
def add_role(self, name, role):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils role.
|
|
|
|
|
|
|
|
*name* must be the role name that occurs in the source, *role* the role
|
|
|
|
function. Refer to the `Docutils documentation
|
|
|
|
<http://docutils.sourceforge.net/docs/howto/rst-roles.html>`_ for
|
|
|
|
more information.
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding role: %r', (name, role))
|
2015-07-21 05:42:13 -05:00
|
|
|
if name in roles._roles:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.warning(__('while setting up extension %s: role %r is '
|
|
|
|
'already registered, it will be overridden'),
|
2016-12-20 08:21:30 -06:00
|
|
|
self._setting_up_extension[-1], name,
|
|
|
|
type='app', subtype='add_role')
|
2009-02-21 10:25:48 -06:00
|
|
|
roles.register_local_role(name, role)
|
2008-03-09 13:18:41 -05:00
|
|
|
|
2009-01-22 13:01:01 -06:00
|
|
|
def add_generic_role(self, name, nodeclass):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a generic Docutils role.
|
|
|
|
|
|
|
|
Register a Docutils role that does nothing but wrap its contents in the
|
|
|
|
node given by *nodeclass*.
|
|
|
|
|
|
|
|
.. versionadded:: 0.6
|
|
|
|
"""
|
|
|
|
# Don't use ``roles.register_generic_role`` because it uses
|
|
|
|
# ``register_canonical_role``.
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding generic role: %r', (name, nodeclass))
|
2015-07-21 05:42:13 -05:00
|
|
|
if name in roles._roles:
|
2017-06-25 03:35:42 -05:00
|
|
|
logger.warning(__('while setting up extension %s: role %r is '
|
|
|
|
'already registered, it will be overridden'),
|
2016-12-20 08:21:30 -06:00
|
|
|
self._setting_up_extension[-1], name,
|
|
|
|
type='app', subtype='add_generic_role')
|
2009-02-21 10:36:38 -06:00
|
|
|
role = roles.GenericRole(name, nodeclass)
|
|
|
|
roles.register_local_role(name, role)
|
2009-01-22 13:01:01 -06:00
|
|
|
|
2009-06-29 10:04:33 -05:00
|
|
|
def add_domain(self, domain):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (Type[Domain]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a domain.
|
|
|
|
|
|
|
|
Make the given *domain* (which must be a class; more precisely, a
|
|
|
|
subclass of :class:`~sphinx.domains.Domain`) known to Sphinx.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_domain(domain)
|
2009-06-29 10:04:33 -05:00
|
|
|
|
2010-02-27 16:13:51 -06:00
|
|
|
def override_domain(self, domain):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (Type[Domain]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Override a registered domain.
|
|
|
|
|
|
|
|
Make the given *domain* class known to Sphinx, assuming that there is
|
|
|
|
already a domain with its ``.name``. The new domain must be a subclass
|
|
|
|
of the existing one.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.override_domain(domain)
|
2010-02-27 16:13:51 -06:00
|
|
|
|
2009-10-27 13:12:47 -05:00
|
|
|
def add_directive_to_domain(self, domain, name, obj,
|
2017-03-09 03:30:40 -06:00
|
|
|
has_content=None, argument_spec=None, **option_spec):
|
|
|
|
# type: (unicode, unicode, Any, bool, Any, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils directive in a domain.
|
|
|
|
|
|
|
|
Like :meth:`add_directive`, but the directive is added to the domain
|
|
|
|
named *domain*.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_directive_to_domain(domain, name, obj,
|
|
|
|
has_content, argument_spec, **option_spec)
|
2009-07-28 12:45:02 -05:00
|
|
|
|
|
|
|
def add_role_to_domain(self, domain, name, role):
|
2017-12-15 07:17:03 -06:00
|
|
|
# type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils role in a domain.
|
|
|
|
|
|
|
|
Like :meth:`add_role`, but the role is added to the domain named
|
|
|
|
*domain*.
|
|
|
|
|
2018-02-10 01:29:41 -06:00
|
|
|
.. versionadded:: 1.0
|
2017-10-22 08:59:09 -05:00
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_role_to_domain(domain, name, role)
|
2009-07-28 12:45:02 -05:00
|
|
|
|
2011-11-01 02:59:53 -05:00
|
|
|
def add_index_to_domain(self, domain, index):
|
2017-01-05 20:34:10 -06:00
|
|
|
# type: (unicode, Type[Index]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a custom index for a domain.
|
|
|
|
|
|
|
|
Add a custom *index* class to the domain named *domain*. *index* must
|
|
|
|
be a subclass of :class:`~sphinx.domains.Index`.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_index_to_domain(domain, index)
|
2010-02-21 14:49:38 -06:00
|
|
|
|
2009-09-07 15:52:26 -05:00
|
|
|
def add_object_type(self, directivename, rolename, indextemplate='',
|
2011-01-07 09:27:47 -06:00
|
|
|
parse_node=None, ref_nodeclass=None, objname='',
|
|
|
|
doc_field_types=[]):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new object type.
|
|
|
|
|
|
|
|
This method is a very convenient way to add a new :term:`object` type
|
|
|
|
that can be cross-referenced. It will do this:
|
|
|
|
|
|
|
|
- Create a new directive (called *directivename*) for documenting an
|
|
|
|
object. It will automatically add index entries if *indextemplate*
|
|
|
|
is nonempty; if given, it must contain exactly one instance of
|
|
|
|
``%s``. See the example below for how the template will be
|
|
|
|
interpreted. * Create a new role (called *rolename*) to
|
|
|
|
cross-reference to these object descriptions.
|
|
|
|
- If you provide *parse_node*, it must be a function that takes a
|
|
|
|
string and a docutils node, and it must populate the node with
|
|
|
|
children parsed from the string. It must then return the name of the
|
|
|
|
item to be used in cross-referencing and index entries. See the
|
|
|
|
:file:`conf.py` file in the source for this documentation for an
|
|
|
|
example.
|
|
|
|
- The *objname* (if not given, will default to *directivename*) names
|
|
|
|
the type of object. It is used when listing objects, e.g. in search
|
|
|
|
results.
|
|
|
|
|
|
|
|
For example, if you have this call in a custom Sphinx extension::
|
|
|
|
|
|
|
|
app.add_object_type('directive', 'dir', 'pair: %s; directive')
|
|
|
|
|
|
|
|
you can use this markup in your documents::
|
|
|
|
|
|
|
|
.. rst:directive:: function
|
|
|
|
|
|
|
|
Document a function.
|
|
|
|
|
|
|
|
<...>
|
|
|
|
|
|
|
|
See also the :rst:dir:`function` directive.
|
|
|
|
|
|
|
|
For the directive, an index entry will be generated as if you had prepended ::
|
|
|
|
|
|
|
|
.. index:: pair: function; directive
|
|
|
|
|
|
|
|
The reference node will be of class ``literal`` (so it will be rendered
|
|
|
|
in a proportional font, as appropriate for code) unless you give the
|
|
|
|
*ref_nodeclass* argument, which must be a docutils node class. Most
|
|
|
|
useful are ``docutils.nodes.emphasis`` or ``docutils.nodes.strong`` --
|
|
|
|
you can also use ``docutils.nodes.generated`` if you want no further
|
|
|
|
text decoration. If the text should be treated as literal (e.g. no
|
|
|
|
smart quote replacement), but not have typewriter styling, use
|
|
|
|
``sphinx.addnodes.literal_emphasis`` or
|
|
|
|
``sphinx.addnodes.literal_strong``.
|
|
|
|
|
|
|
|
For the role content, you have the same syntactical possibilities as
|
|
|
|
for standard Sphinx roles (see :ref:`xref-syntax`).
|
|
|
|
|
|
|
|
This method is also available under the deprecated alias
|
|
|
|
:meth:`add_description_unit`.
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_object_type(directivename, rolename, indextemplate, parse_node,
|
|
|
|
ref_nodeclass, objname, doc_field_types)
|
2017-03-09 04:25:42 -06:00
|
|
|
|
|
|
|
def add_description_unit(self, directivename, rolename, indextemplate='',
|
|
|
|
parse_node=None, ref_nodeclass=None, objname='',
|
|
|
|
doc_field_types=[]):
|
|
|
|
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
|
2018-01-21 05:15:20 -06:00
|
|
|
"""Deprecated alias for :meth:`add_object_type`.
|
|
|
|
|
|
|
|
.. deprecated:: 1.6
|
|
|
|
Use :meth:`add_object_type` instead.
|
|
|
|
"""
|
2017-03-09 04:25:42 -06:00
|
|
|
warnings.warn('app.add_description_unit() is now deprecated. '
|
|
|
|
'Use app.add_object_type() instead.',
|
|
|
|
RemovedInSphinx20Warning)
|
|
|
|
self.add_object_type(directivename, rolename, indextemplate, parse_node,
|
|
|
|
ref_nodeclass, objname, doc_field_types)
|
2008-03-28 13:45:32 -05:00
|
|
|
|
|
|
|
def add_crossref_type(self, directivename, rolename, indextemplate='',
|
2009-09-07 17:04:05 -05:00
|
|
|
ref_nodeclass=None, objname=''):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new crossref object type.
|
|
|
|
|
|
|
|
This method is very similar to :meth:`add_object_type` except that the
|
|
|
|
directive it generates must be empty, and will produce no output.
|
|
|
|
|
|
|
|
That means that you can add semantic targets to your sources, and refer
|
|
|
|
to them using custom roles instead of generic ones (like
|
|
|
|
:rst:role:`ref`). Example call::
|
|
|
|
|
|
|
|
app.add_crossref_type('topic', 'topic', 'single: %s',
|
|
|
|
docutils.nodes.emphasis)
|
|
|
|
|
|
|
|
Example usage::
|
|
|
|
|
|
|
|
.. topic:: application API
|
|
|
|
|
|
|
|
The application API
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
Some random text here.
|
|
|
|
|
|
|
|
See also :topic:`this section <application API>`.
|
|
|
|
|
|
|
|
(Of course, the element following the ``topic`` directive needn't be a
|
|
|
|
section.)
|
|
|
|
"""
|
2017-04-23 02:29:38 -05:00
|
|
|
self.registry.add_crossref_type(directivename, rolename,
|
|
|
|
indextemplate, ref_nodeclass, objname)
|
2008-04-13 03:20:11 -05:00
|
|
|
|
|
|
|
def add_transform(self, transform):
|
2017-03-05 05:46:36 -06:00
|
|
|
# type: (Type[Transform]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils transform to be applied after parsing.
|
|
|
|
|
|
|
|
Add the standard docutils :class:`Transform` subclass *transform* to
|
|
|
|
the list of transforms that are applied after Sphinx parses a reST
|
|
|
|
document.
|
|
|
|
"""
|
2017-05-01 00:09:32 -05:00
|
|
|
self.registry.add_transform(transform)
|
2008-04-13 13:16:55 -05:00
|
|
|
|
2017-03-05 05:46:36 -06:00
|
|
|
def add_post_transform(self, transform):
|
|
|
|
# type: (Type[Transform]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a Docutils transform to be applied before writing.
|
|
|
|
|
|
|
|
Add the standard docutils :class:`Transform` subclass *transform* to
|
|
|
|
the list of transforms that are applied before Sphinx writes a
|
|
|
|
document.
|
|
|
|
"""
|
2017-05-01 00:40:02 -05:00
|
|
|
self.registry.add_post_transform(transform)
|
2017-03-05 05:46:36 -06:00
|
|
|
|
2008-08-06 08:04:14 -05:00
|
|
|
def add_javascript(self, filename):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a JavaScript file to include in the HTML output.
|
|
|
|
|
|
|
|
Add *filename* to the list of JavaScript files that the default HTML
|
|
|
|
template will include. The filename must be relative to the HTML
|
|
|
|
static path, see :confval:`the docs for the config value
|
|
|
|
<html_static_path>`. A full URI with scheme, like
|
|
|
|
``http://example.org/foo.js``, is also supported.
|
|
|
|
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding javascript: %r', filename)
|
2008-11-29 12:56:58 -06:00
|
|
|
from sphinx.builders.html import StandaloneHTMLBuilder
|
2010-08-25 05:26:15 -05:00
|
|
|
if '://' in filename:
|
|
|
|
StandaloneHTMLBuilder.script_files.append(filename)
|
|
|
|
else:
|
|
|
|
StandaloneHTMLBuilder.script_files.append(
|
|
|
|
posixpath.join('_static', filename))
|
2009-06-16 14:05:20 -05:00
|
|
|
|
2017-04-15 10:07:33 -05:00
|
|
|
def add_stylesheet(self, filename, alternate=False, title=None):
|
|
|
|
# type: (unicode, bool, unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a stylesheet to include in the HTML output.
|
|
|
|
|
|
|
|
Add *filename* to the list of CSS files that the default HTML template
|
|
|
|
will include. Like for :meth:`add_javascript`, the filename must be
|
|
|
|
relative to the HTML static path, or a full URI with scheme.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0
|
|
|
|
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
Optional ``alternate`` and/or ``title`` attributes can be supplied
|
|
|
|
with the *alternate* (of boolean type) and *title* (a string)
|
|
|
|
arguments. The default is no title and *alternate* = ``False``. For
|
|
|
|
more information, refer to the `documentation
|
|
|
|
<https://mdn.io/Web/CSS/Alternative_style_sheets>`__.
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding stylesheet: %r', filename)
|
2017-04-18 10:43:39 -05:00
|
|
|
from sphinx.builders.html import StandaloneHTMLBuilder, Stylesheet
|
2017-04-15 10:07:33 -05:00
|
|
|
if '://' not in filename:
|
2017-04-18 10:43:39 -05:00
|
|
|
filename = posixpath.join('_static', filename)
|
2017-04-15 10:07:33 -05:00
|
|
|
if alternate:
|
2017-04-18 10:43:39 -05:00
|
|
|
rel = u'alternate stylesheet'
|
|
|
|
else:
|
|
|
|
rel = u'stylesheet'
|
|
|
|
css = Stylesheet(filename, title, rel) # type: ignore
|
|
|
|
StandaloneHTMLBuilder.css_files.append(css)
|
2008-08-06 08:04:14 -05:00
|
|
|
|
2014-09-28 07:10:26 -05:00
|
|
|
def add_latex_package(self, packagename, options=None):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
r"""Register a package to include in the LaTeX source code.
|
|
|
|
|
|
|
|
Add *packagename* to the list of packages that LaTeX source code will
|
|
|
|
include. If you provide *options*, it will be taken to `\usepackage`
|
|
|
|
declaration.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
app.add_latex_package('mypackage')
|
|
|
|
# => \usepackage{mypackage}
|
|
|
|
app.add_latex_package('mypackage', 'foo,bar')
|
|
|
|
# => \usepackage[foo,bar]{mypackage}
|
|
|
|
|
|
|
|
.. versionadded:: 1.3
|
|
|
|
"""
|
2018-02-10 01:27:21 -06:00
|
|
|
self.registry.add_latex_package(packagename, options)
|
2014-09-26 22:47:27 -05:00
|
|
|
|
2008-11-30 12:58:29 -06:00
|
|
|
def add_lexer(self, alias, lexer):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new lexer for source code.
|
|
|
|
|
|
|
|
Use *lexer*, which must be an instance of a Pygments lexer class, to
|
|
|
|
highlight code blocks with the given language *alias*.
|
|
|
|
|
|
|
|
.. versionadded:: 0.6
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding lexer: %r', (alias, lexer))
|
2008-11-30 12:58:29 -06:00
|
|
|
from sphinx.highlighting import lexers
|
|
|
|
if lexers is None:
|
|
|
|
return
|
|
|
|
lexers[alias] = lexer
|
|
|
|
|
2009-02-17 09:36:30 -06:00
|
|
|
def add_autodocumenter(self, cls):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new documenter class for the autodoc extension.
|
|
|
|
|
|
|
|
Add *cls* as a new documenter class for the :mod:`sphinx.ext.autodoc`
|
|
|
|
extension. It must be a subclass of
|
|
|
|
:class:`sphinx.ext.autodoc.Documenter`. This allows to auto-document
|
|
|
|
new types of objects. See the source of the autodoc module for
|
|
|
|
examples on how to subclass :class:`Documenter`.
|
|
|
|
|
|
|
|
.. todo:: Add real docs for Documenter and subclassing
|
|
|
|
|
|
|
|
.. versionadded:: 0.6
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding autodocumenter: %r', cls)
|
2017-12-16 09:03:56 -06:00
|
|
|
from sphinx.ext.autodoc.directive import AutodocDirective
|
2017-06-17 20:25:34 -05:00
|
|
|
self.registry.add_documenter(cls.objtype, cls)
|
2017-12-16 09:03:56 -06:00
|
|
|
self.add_directive('auto' + cls.objtype, AutodocDirective)
|
2009-02-17 09:36:30 -06:00
|
|
|
|
2017-06-18 05:21:46 -05:00
|
|
|
def add_autodoc_attrgetter(self, typ, getter):
|
|
|
|
# type: (Type, Callable[[Any, unicode, Any], Any]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new ``getattr``-like function for the autodoc extension.
|
|
|
|
|
|
|
|
Add *getter*, which must be a function with an interface compatible to
|
|
|
|
the :func:`getattr` builtin, as the autodoc attribute getter for
|
|
|
|
objects that are instances of *typ*. All cases where autodoc needs to
|
|
|
|
get an attribute of a type are then handled by this function instead of
|
|
|
|
:func:`getattr`.
|
|
|
|
|
|
|
|
.. versionadded:: 0.6
|
|
|
|
"""
|
2017-06-18 05:21:46 -05:00
|
|
|
logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
|
2018-01-06 09:51:45 -06:00
|
|
|
self.registry.add_autodoc_attrgetter(typ, getter)
|
2009-02-19 15:54:34 -06:00
|
|
|
|
2011-01-04 05:40:19 -06:00
|
|
|
def add_search_language(self, cls):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (Any) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a new language for the HTML search index.
|
|
|
|
|
|
|
|
Add *cls*, which must be a subclass of
|
|
|
|
:class:`sphinx.search.SearchLanguage`, as a support language for
|
|
|
|
building the HTML full-text search index. The class must have a *lang*
|
|
|
|
attribute that indicates the language it should be used for. See
|
|
|
|
:confval:`html_search_language`.
|
|
|
|
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
"""
|
2016-12-21 22:41:56 -06:00
|
|
|
logger.debug('[app] adding search language: %r', cls)
|
2011-01-04 05:40:19 -06:00
|
|
|
from sphinx.search import languages, SearchLanguage
|
2014-09-09 03:27:00 -05:00
|
|
|
assert issubclass(cls, SearchLanguage)
|
2011-01-04 05:40:19 -06:00
|
|
|
languages[cls.lang] = cls
|
|
|
|
|
2018-02-11 06:13:32 -06:00
|
|
|
def add_source_suffix(self, suffix, filetype):
|
|
|
|
# type: (unicode, unicode) -> None
|
|
|
|
"""Register a suffix of source files.
|
|
|
|
|
|
|
|
Same as :confval:`source_suffix`. The users can override this
|
|
|
|
using the setting.
|
|
|
|
"""
|
|
|
|
self.registry.add_source_suffix(suffix, filetype)
|
|
|
|
|
|
|
|
def add_source_parser(self, *args):
|
|
|
|
# type: (Any) -> None
|
|
|
|
"""Register a parser class.
|
2017-10-22 08:59:09 -05:00
|
|
|
|
|
|
|
.. versionadded:: 1.4
|
2018-02-11 06:13:32 -06:00
|
|
|
.. versionchanged:: 1.8
|
|
|
|
*suffix* argument is deprecated. It only accepts *parser* argument.
|
|
|
|
Use :meth:`add_source_suffix` API to register suffix instead.
|
2017-10-22 08:59:09 -05:00
|
|
|
"""
|
2018-02-11 06:13:32 -06:00
|
|
|
self.registry.add_source_parser(*args)
|
2015-12-31 21:06:46 -06:00
|
|
|
|
2016-12-17 05:40:48 -06:00
|
|
|
def add_env_collector(self, collector):
|
|
|
|
# type: (Type[EnvironmentCollector]) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register an environment collector class.
|
|
|
|
|
|
|
|
Refer to :ref:`collector-api`.
|
|
|
|
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
"""
|
2016-12-17 05:40:48 -06:00
|
|
|
logger.debug('[app] adding environment collector: %r', collector)
|
|
|
|
collector().enable(self)
|
|
|
|
|
2017-04-20 07:21:04 -05:00
|
|
|
def add_html_theme(self, name, theme_path):
|
|
|
|
# type: (unicode, unicode) -> None
|
2017-10-22 08:59:09 -05:00
|
|
|
"""Register a HTML Theme.
|
|
|
|
|
|
|
|
The *name* is a name of theme, and *path* is a full path to the theme
|
|
|
|
(refs: :ref:`distribute-your-theme`).
|
|
|
|
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
"""
|
2017-04-20 07:21:04 -05:00
|
|
|
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
|
|
|
|
self.html_themes[name] = theme_path
|
|
|
|
|
2018-02-23 07:50:21 -06:00
|
|
|
def add_message_catalog(self, catalog, locale_dir):
|
|
|
|
# type: (unicode, unicode) -> None
|
|
|
|
"""Register a message catalog.
|
|
|
|
|
|
|
|
The *catalog* is a name of catalog, and *locale_dir* is a base path
|
|
|
|
of message catalog. For more details, see
|
2018-02-25 06:41:39 -06:00
|
|
|
:func:`sphinx.locale.get_translation()`.
|
2018-02-23 07:50:21 -06:00
|
|
|
|
|
|
|
.. versionadded:: 1.8
|
|
|
|
"""
|
|
|
|
locale.init([locale_dir], self.config.language, catalog)
|
2018-02-24 07:30:30 -06:00
|
|
|
locale.init_console(locale_dir, catalog)
|
2018-02-23 07:50:21 -06:00
|
|
|
|
2017-12-29 09:54:55 -06:00
|
|
|
# ---- other methods -------------------------------------------------
|
|
|
|
def is_parallel_allowed(self, typ):
|
|
|
|
# type: (unicode) -> bool
|
|
|
|
"""Check parallel processing is allowed or not.
|
|
|
|
|
|
|
|
``typ`` is a type of processing; ``'read'`` or ``'write'``.
|
|
|
|
"""
|
|
|
|
if typ == 'read':
|
|
|
|
attrname = 'parallel_read_safe'
|
2018-02-06 11:12:22 -06:00
|
|
|
message = __("the %s extension does not declare if it is safe "
|
|
|
|
"for parallel reading, assuming it isn't - please "
|
|
|
|
"ask the extension author to check and make it "
|
|
|
|
"explicit")
|
2017-12-29 09:54:55 -06:00
|
|
|
elif typ == 'write':
|
|
|
|
attrname = 'parallel_write_safe'
|
2018-02-06 11:12:22 -06:00
|
|
|
message = __("the %s extension does not declare if it is safe "
|
|
|
|
"for parallel writing, assuming it isn't - please "
|
|
|
|
"ask the extension author to check and make it "
|
|
|
|
"explicit")
|
2017-12-29 09:54:55 -06:00
|
|
|
else:
|
|
|
|
raise ValueError('parallel type %s is not supported' % typ)
|
|
|
|
|
|
|
|
for ext in itervalues(self.extensions):
|
|
|
|
allowed = getattr(ext, attrname, None)
|
|
|
|
if allowed is None:
|
2018-02-06 11:12:22 -06:00
|
|
|
logger.warning(message, ext.name)
|
2018-02-25 07:16:09 -06:00
|
|
|
logger.warning(__('doing serial %s'), typ)
|
2017-12-29 09:54:55 -06:00
|
|
|
return False
|
|
|
|
elif not allowed:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2008-04-13 13:16:55 -05:00
|
|
|
|
|
|
|
class TemplateBridge(object):
|
|
|
|
"""
|
2008-05-04 03:28:00 -05:00
|
|
|
This class defines the interface for a "template bridge", that is, a class
|
|
|
|
that renders templates given a template name and a context.
|
2008-04-13 13:16:55 -05:00
|
|
|
"""
|
|
|
|
|
2009-02-21 10:17:21 -06:00
|
|
|
def init(self, builder, theme=None, dirs=None):
|
2017-04-20 07:31:03 -05:00
|
|
|
# type: (Builder, Theme, List[unicode]) -> None
|
2010-08-22 04:36:08 -05:00
|
|
|
"""Called by the builder to initialize the template system.
|
2009-02-21 10:17:21 -06:00
|
|
|
|
|
|
|
*builder* is the builder object; you'll probably want to look at the
|
|
|
|
value of ``builder.config.templates_path``.
|
|
|
|
|
|
|
|
*theme* is a :class:`sphinx.theming.Theme` object or None; in the latter
|
|
|
|
case, *dirs* can be list of fixed directories to look for templates.
|
2008-04-13 13:16:55 -05:00
|
|
|
"""
|
|
|
|
raise NotImplementedError('must be implemented in subclasses')
|
|
|
|
|
|
|
|
def newest_template_mtime(self):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: () -> float
|
2010-08-22 04:36:08 -05:00
|
|
|
"""Called by the builder to determine if output files are outdated
|
2008-04-13 13:16:55 -05:00
|
|
|
because of template changes. Return the mtime of the newest template
|
|
|
|
file that was changed. The default implementation returns ``0``.
|
|
|
|
"""
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def render(self, template, context):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Dict) -> None
|
2010-08-22 04:36:08 -05:00
|
|
|
"""Called by the builder to render a template given as a filename with
|
|
|
|
a specified context (a Python dictionary).
|
2009-02-15 00:19:48 -06:00
|
|
|
"""
|
|
|
|
raise NotImplementedError('must be implemented in subclasses')
|
|
|
|
|
|
|
|
def render_string(self, template, context):
|
2016-11-08 20:45:27 -06:00
|
|
|
# type: (unicode, Dict) -> unicode
|
2010-08-22 04:36:08 -05:00
|
|
|
"""Called by the builder to render a template given as a string with a
|
2009-02-15 00:19:48 -06:00
|
|
|
specified context (a Python dictionary).
|
2008-04-13 13:16:55 -05:00
|
|
|
"""
|
|
|
|
raise NotImplementedError('must be implemented in subclasses')
|