summaryrefslogtreecommitdiff
path: root/sphinx/application.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/application.py')
-rw-r--r--sphinx/application.py89
1 files changed, 77 insertions, 12 deletions
diff --git a/sphinx/application.py b/sphinx/application.py
index f65592b1d..9ff99b691 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin.
- :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -35,11 +35,13 @@ from sphinx.errors import SphinxError, SphinxWarning, ExtensionError, \
from sphinx.domains import ObjType, BUILTIN_DOMAINS
from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.builders import BUILTIN_BUILDERS
-from sphinx.environment import BuildEnvironment, SphinxStandaloneReader
+from sphinx.environment import BuildEnvironment
+from sphinx.io import SphinxStandaloneReader
from sphinx.util import pycompat # noqa: imported for side-effects
from sphinx.util import import_object
from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT
+from sphinx.util.logging import is_suppressed_warning
from sphinx.util.console import bold, lightgray, darkgray, darkgreen, \
term_width_line
@@ -77,11 +79,15 @@ class Sphinx(object):
self.next_listener_id = 0
self._extensions = {}
self._extension_metadata = {}
+ self._additional_source_parsers = {}
self._listeners = {}
+ self._setting_up_extension = ['?']
self.domains = BUILTIN_DOMAINS.copy()
+ self.buildername = buildername
self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None
self.env = None
+ self.enumerable_nodes = {}
self.srcdir = srcdir
self.confdir = confdir
@@ -143,6 +149,7 @@ class Sphinx(object):
self.setup_extension(extension)
# the config file itself can be an extension
if self.config.setup:
+ self._setting_up_extension = ['conf.py']
# py31 doesn't have 'callable' function for below check
if hasattr(self.config.setup, '__call__'):
self.config.setup(self)
@@ -158,7 +165,7 @@ class Sphinx(object):
# check the Sphinx version if requested
if self.config.needs_sphinx and \
- self.config.needs_sphinx > sphinx.__display_version__[:3]:
+ self.config.needs_sphinx > sphinx.__display_version__:
raise VersionRequirementError(
'This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.' % self.config.needs_sphinx)
@@ -182,10 +189,14 @@ class Sphinx(object):
self._init_i18n()
# check all configuration values for permissible types
self.config.check_types(self.warn)
+ # set up source_parsers
+ self._init_source_parsers()
# set up the build environment
self._init_env(freshenv)
# set up the builder
- self._init_builder(buildername)
+ self._init_builder(self.buildername)
+ # set up the enumerable nodes
+ self._init_enumerable_nodes()
def _init_i18n(self):
"""Load translated strings from the configured localedirs if enabled in
@@ -199,7 +210,8 @@ class Sphinx(object):
else:
locale_dirs = []
self.translator, has_translation = locale.init(locale_dirs,
- self.config.language)
+ self.config.language,
+ charset=self.config.source_encoding)
if self.config.language is not None:
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
@@ -207,6 +219,13 @@ class Sphinx(object):
else:
self.info('not available for built-in messages')
+ def _init_source_parsers(self):
+ for suffix, parser in iteritems(self._additional_source_parsers):
+ if suffix not in self.config.source_suffix:
+ self.config.source_suffix.append(suffix)
+ if suffix not in self.config.source_parsers:
+ self.config.source_parsers[suffix] = parser
+
def _init_env(self, freshenv):
if freshenv:
self.env = BuildEnvironment(self.srcdir, self.doctreedir,
@@ -218,7 +237,7 @@ class Sphinx(object):
try:
self.info(bold('loading pickled environment... '), nonl=True)
self.env = BuildEnvironment.frompickle(
- self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME))
+ self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.env.domains = {}
for domain in self.domains.keys():
# this can raise if the data version doesn't fit
@@ -249,6 +268,10 @@ class Sphinx(object):
self.builder = builderclass(self)
self.emit('builder-inited')
+ def _init_enumerable_nodes(self):
+ for node, settings in iteritems(self.enumerable_nodes):
+ self.env.domains['std'].enumerable_nodes[node] = settings
+
# ---- main "build" method -------------------------------------------------
def build(self, force_all=False, filenames=None):
@@ -296,7 +319,7 @@ class Sphinx(object):
wfile.flush()
self.messagelog.append(message)
- def warn(self, message, location=None, prefix='WARNING: '):
+ def warn(self, message, location=None, prefix='WARNING: ', type=None, subtype=None):
"""Emit a warning.
If *location* is given, it should either be a tuple of (docname, lineno)
@@ -304,12 +327,17 @@ class Sphinx(object):
*prefix* usually should not be changed.
+ *type* and *subtype* are used to suppress warnings with :confval:`suppress_warnings`.
+
.. note::
For warnings emitted during parsing, you should use
:meth:`.BuildEnvironment.warn` since that will collect all
warnings during parsing for later output.
"""
+ if is_suppressed_warning(type, subtype, self.config.suppress_warnings):
+ return
+
if isinstance(location, tuple):
docname, lineno = location
if docname:
@@ -425,6 +453,7 @@ class Sphinx(object):
self.debug('[app] setting up extension: %r', extension)
if extension in self._extensions:
return
+ self._setting_up_extension.append(extension)
try:
mod = __import__(extension, None, None, ['setup'])
except ImportError as err:
@@ -446,10 +475,20 @@ class Sphinx(object):
'version.' % (extension, err))
if ext_meta is None:
ext_meta = {}
- if not ext_meta.get('version'):
- ext_meta['version'] = 'unknown version'
+ # special-case for compatibility
+ if extension == 'rst2pdf.pdfbuilder':
+ ext_meta = {'parallel_read_safe': True}
+ try:
+ if not ext_meta.get('version'):
+ ext_meta['version'] = 'unknown version'
+ except Exception:
+ self.warn('extension %r returned an unsupported object from '
+ 'its setup() function; it should return None or a '
+ 'metadata dictionary' % extension)
+ ext_meta = {'version': 'unknown version'}
self._extensions[extension] = mod
self._extension_metadata[extension] = ext_meta
+ self._setting_up_extension.pop()
def require_sphinx(self, version):
# check the Sphinx version if requested
@@ -520,13 +559,14 @@ class Sphinx(object):
builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
- def add_config_value(self, name, default, rebuild):
- self.debug('[app] adding config value: %r', (name, default, rebuild))
+ def add_config_value(self, name, default, rebuild, types=()):
+ self.debug('[app] adding config value: %r',
+ (name, default, rebuild) + ((types,) if types else ()))
if name in self.config.values:
raise ExtensionError('Config value %r already present' % name)
if rebuild in (False, True):
rebuild = rebuild and 'env' or ''
- self.config.values[name] = (default, rebuild)
+ self.config.values[name] = (default, rebuild, types)
def add_event(self, name):
self.debug('[app] adding event: %r', name)
@@ -540,6 +580,11 @@ class Sphinx(object):
def add_node(self, node, **kwds):
self.debug('[app] adding node: %r', (node, kwds))
+ if not kwds.pop('override', False) and \
+ hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
+ self.warn('while setting up extension %s: node class %r is '
+ 'already registered, its visitors will be overridden' %
+ (self._setting_up_extension, node.__name__))
nodes._add_node_class_names([node.__name__])
for key, val in iteritems(kwds):
try:
@@ -569,6 +614,10 @@ class Sphinx(object):
if depart:
setattr(translator, 'depart_'+node.__name__, depart)
+ def add_enumerable_node(self, node, figtype, title_getter=None, **kwds):
+ self.enumerable_nodes[node] = (figtype, title_getter)
+ self.add_node(node, **kwds)
+
def _directive_helper(self, obj, content=None, arguments=None, **options):
if isinstance(obj, (types.FunctionType, types.MethodType)):
obj.content = content
@@ -584,17 +633,29 @@ class Sphinx(object):
def add_directive(self, name, obj, content=None, arguments=None, **options):
self.debug('[app] adding directive: %r',
(name, obj, content, arguments, options))
+ if name in directives._directives:
+ self.warn('while setting up extension %s: directive %r is '
+ 'already registered, it will be overridden' %
+ (self._setting_up_extension[-1], name))
directives.register_directive(
name, self._directive_helper(obj, content, arguments, **options))
def add_role(self, name, role):
self.debug('[app] adding role: %r', (name, role))
+ if name in roles._roles:
+ self.warn('while setting up extension %s: role %r is '
+ 'already registered, it will be overridden' %
+ (self._setting_up_extension[-1], name))
roles.register_local_role(name, role)
def add_generic_role(self, name, nodeclass):
# don't use roles.register_generic_role because it uses
# register_canonical_role
self.debug('[app] adding generic role: %r', (name, nodeclass))
+ if name in roles._roles:
+ self.warn('while setting up extension %s: role %r is '
+ 'already registered, it will be overridden' %
+ (self._setting_up_extension[-1], name))
role = roles.GenericRole(name, nodeclass)
roles.register_local_role(name, role)
@@ -719,6 +780,10 @@ class Sphinx(object):
assert issubclass(cls, SearchLanguage)
languages[cls.lang] = cls
+ def add_source_parser(self, suffix, parser):
+ self.debug('[app] adding search source_parser: %r, %r', (suffix, parser))
+ self._additional_source_parsers[suffix] = parser
+
class TemplateBridge(object):
"""