summaryrefslogtreecommitdiff
path: root/sphinx/application.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/application.py')
-rw-r--r--sphinx/application.py193
1 files changed, 121 insertions, 72 deletions
diff --git a/sphinx/application.py b/sphinx/application.py
index 325cf9a5..e8e9fad6 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -7,58 +7,33 @@
Gracefully adapted from the TextPress system by Armin.
-
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
+import types
import posixpath
from cStringIO import StringIO
from docutils import nodes
from docutils.parsers.rst import directives, roles
-# create the error classes before importing the rest of Sphinx, so that
-# they can be imported in a circular fashion
-
-class SphinxError(Exception):
- """
- Base class for Sphinx errors that are shown to the user in a nicer
- way than normal exceptions.
- """
- category = 'Sphinx error'
-
-class ExtensionError(SphinxError):
- """Raised if something's wrong with the configuration."""
- category = 'Extension error'
-
- def __init__(self, message, orig_exc=None):
- super(ExtensionError, self).__init__(message)
- self.orig_exc = orig_exc
-
- def __repr__(self):
- if self.orig_exc:
- return '%s(%r, %r)' % (self.__class__.__name__,
- self.message, self.orig_exc)
- return '%s(%r)' % (self.__class__.__name__, self.message)
-
- def __str__(self):
- parent_str = super(ExtensionError, self).__str__()
- if self.orig_exc:
- return '%s (exception: %s)' % (parent_str, self.orig_exc)
- return parent_str
-
-
import sphinx
from sphinx.roles import xfileref_role, innernodetypes
from sphinx.config import Config
-from sphinx.builder import builtin_builders, StandaloneHTMLBuilder
-from sphinx.directives import desc_directive, target_directive, additional_xref_types
+from sphinx.errors import SphinxError, SphinxWarning, ExtensionError
+from sphinx.builders import BUILTIN_BUILDERS
+from sphinx.directives import GenericDesc, Target, additional_xref_types
from sphinx.environment import SphinxStandaloneReader
+from sphinx.util.tags import Tags
+from sphinx.util.compat import Directive, directive_dwim
from sphinx.util.console import bold
+# Directive is either new-style or old-style
+clstypes = (type, types.ClassType)
+
# List of all known core events. Maps name to arguments description.
events = {
'builder-inited': '',
@@ -74,13 +49,16 @@ events = {
CONFIG_FILENAME = 'conf.py'
+
class Sphinx(object):
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
- confoverrides, status, warning=sys.stderr, freshenv=False):
+ confoverrides, status, warning=sys.stderr, freshenv=False,
+ warningiserror=False, tags=None):
self.next_listener_id = 0
+ self._extensions = {}
self._listeners = {}
- self.builderclasses = builtin_builders.copy()
+ self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None
self.srcdir = srcdir
@@ -94,19 +72,25 @@ class Sphinx(object):
else:
self._status = status
self.quiet = False
+
if warning is None:
self._warning = StringIO()
else:
self._warning = warning
self._warncount = 0
+ self.warningiserror = warningiserror
self._events = events.copy()
+ # say hello to the world
+ self.info(bold('Running Sphinx v%s' % sphinx.__released__))
+
# status code for command-line application
self.statuscode = 0
# read config
- self.config = Config(confdir, CONFIG_FILENAME, confoverrides)
+ self.tags = Tags(tags)
+ self.config = Config(confdir, CONFIG_FILENAME, confoverrides, self.tags)
# load all extension modules
for extension in self.config.extensions:
@@ -124,11 +108,15 @@ class Sphinx(object):
if buildername not in self.builderclasses:
raise SphinxError('Builder name %s not registered' % buildername)
- self.info(bold('Sphinx v%s, building %s' % (sphinx.__released__,
- buildername)))
-
builderclass = self.builderclasses[buildername]
+ if isinstance(builderclass, tuple):
+ # builtin builder
+ mod, cls = builderclass
+ builderclass = getattr(
+ __import__('sphinx.builders.' + mod, None, None, [cls]), cls)
self.builder = builderclass(self, freshenv=freshenv)
+ self.builder.tags = self.tags
+ self.builder.tags.add(self.builder.format)
self.emit('builder-inited')
def build(self, all_files, filenames):
@@ -144,14 +132,19 @@ class Sphinx(object):
raise
else:
self.emit('build-finished', None)
+ self.builder.cleanup()
- def warn(self, message):
+ def warn(self, message, location=None, prefix='WARNING: '):
+ warntext = location and '%s: %s%s\n' % (location, prefix, message) or \
+ '%s%s\n' % (prefix, message)
+ if self.warningiserror:
+ raise SphinxWarning(warntext)
self._warncount += 1
try:
- self._warning.write('WARNING: %s\n' % message)
+ self._warning.write(warntext)
except UnicodeEncodeError:
encoding = getattr(self._warning, 'encoding', 'ascii')
- self._warning.write(('WARNING: %s\n' % message).encode(encoding, 'replace'))
+ self._warning.write(warntext.encode(encoding, 'replace'))
def info(self, message='', nonl=False):
try:
@@ -166,13 +159,17 @@ class Sphinx(object):
# general extensibility interface
def setup_extension(self, extension):
- """Import and setup a Sphinx extension module."""
+ """Import and setup a Sphinx extension module. No-op if called twice."""
+ if extension in self._extensions:
+ return
try:
mod = __import__(extension, None, None, ['setup'])
except ImportError, err:
- raise ExtensionError('Could not import extension %s' % extension, err)
+ raise ExtensionError('Could not import extension %s' % extension,
+ err)
if hasattr(mod, 'setup'):
mod.setup(self)
+ self._extensions[extension] = mod
def import_object(self, objname, source=None):
"""Import an object from a 'module.name' string."""
@@ -180,15 +177,18 @@ class Sphinx(object):
module, name = objname.rsplit('.', 1)
except ValueError, err:
raise ExtensionError('Invalid full object name %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
+ (source and ' (needed for %s)' % source or ''),
+ err)
try:
return getattr(__import__(module, None, None, [name]), name)
except ImportError, err:
raise ExtensionError('Could not import %s' % module +
- (source and ' (needed for %s)' % source or ''), err)
+ (source and ' (needed for %s)' % source or ''),
+ err)
except AttributeError, err:
raise ExtensionError('Could not find %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
+ (source and ' (needed for %s)' % source or ''),
+ err)
# event interface
@@ -228,16 +228,24 @@ class Sphinx(object):
def add_builder(self, builder):
if not hasattr(builder, 'name'):
- raise ExtensionError('Builder class %s has no "name" attribute' % builder)
+ raise ExtensionError('Builder class %s has no "name" attribute'
+ % builder)
if builder.name in self.builderclasses:
- raise ExtensionError('Builder %r already exists (in module %s)' % (
- builder.name, self.builderclasses[builder.name].__module__))
+ if isinstance(self.builderclasses[builder.name], tuple):
+ raise ExtensionError('Builder %r is a builtin builder' %
+ builder.name)
+ else:
+ raise ExtensionError(
+ 'Builder %r already exists (in module %s)' % (
+ builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
- def add_config_value(self, name, default, rebuild_env):
+ def add_config_value(self, name, default, rebuild):
if name in self.config.values:
raise ExtensionError('Config value %r already present' % name)
- self.config.values[name] = (default, rebuild_env)
+ if rebuild in (False, True):
+ rebuild = rebuild and 'env' or ''
+ self.config.values[name] = (default, rebuild)
def add_event(self, name):
if name in self._events:
@@ -250,14 +258,14 @@ class Sphinx(object):
try:
visit, depart = val
except ValueError:
- raise ExtensionError('Value for key %r must be a (visit, depart) '
- 'function tuple' % key)
+ raise ExtensionError('Value for key %r must be a '
+ '(visit, depart) function tuple' % key)
if key == 'html':
- from sphinx.htmlwriter import HTMLTranslator as translator
+ from sphinx.writers.html import HTMLTranslator as translator
elif key == 'latex':
- from sphinx.latexwriter import LaTeXTranslator as translator
+ from sphinx.writers.latex import LaTeXTranslator as translator
elif key == 'text':
- from sphinx.textwriter import TextTranslator as translator
+ from sphinx.writers.text import TextTranslator as translator
else:
# ignore invalid keys for compatibility
continue
@@ -265,19 +273,33 @@ class Sphinx(object):
if depart:
setattr(translator, 'depart_'+node.__name__, depart)
- def add_directive(self, name, func, content, arguments, **options):
- func.content = content
- func.arguments = arguments
- func.options = options
- directives.register_directive(name, func)
+ def add_directive(self, name, obj, content=None, arguments=None, **options):
+ if isinstance(obj, clstypes) and issubclass(obj, Directive):
+ if content or arguments or options:
+ raise ExtensionError('when adding directive classes, no '
+ 'additional arguments may be given')
+ directives.register_directive(name, directive_dwim(obj))
+ else:
+ obj.content = content
+ obj.arguments = arguments
+ obj.options = options
+ directives.register_directive(name, obj)
def add_role(self, name, role):
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
+ role = roles.GenericRole(name, nodeclass)
+ roles.register_local_role(name, role)
+
def add_description_unit(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None):
- additional_xref_types[directivename] = (rolename, indextemplate, parse_node)
- directives.register_directive(directivename, desc_directive)
+ additional_xref_types[directivename] = (rolename, indextemplate,
+ parse_node)
+ directives.register_directive(directivename,
+ directive_dwim(GenericDesc))
roles.register_local_role(rolename, xfileref_role)
if ref_nodeclass is not None:
innernodetypes[rolename] = ref_nodeclass
@@ -285,7 +307,7 @@ class Sphinx(object):
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None):
additional_xref_types[directivename] = (rolename, indextemplate, None)
- directives.register_directive(directivename, target_directive)
+ directives.register_directive(directivename, directive_dwim(Target))
roles.register_local_role(rolename, xfileref_role)
if ref_nodeclass is not None:
innernodetypes[rolename] = ref_nodeclass
@@ -294,9 +316,25 @@ class Sphinx(object):
SphinxStandaloneReader.transforms.append(transform)
def add_javascript(self, filename):
+ from sphinx.builders.html import StandaloneHTMLBuilder
StandaloneHTMLBuilder.script_files.append(
posixpath.join('_static', filename))
+ def add_lexer(self, alias, lexer):
+ from sphinx.highlighting import lexers
+ if lexers is None:
+ return
+ lexers[alias] = lexer
+
+ def add_autodocumenter(self, cls):
+ from sphinx.ext import autodoc
+ autodoc.add_documenter(cls)
+ self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
+
+ def add_autodoc_attrgetter(self, type, getter):
+ from sphinx.ext import autodoc
+ autodoc.AutoDirective._special_attrgetters[type] = getter
+
class TemplateBridge(object):
"""
@@ -304,11 +342,15 @@ class TemplateBridge(object):
that renders templates given a template name and a context.
"""
- def init(self, builder):
+ def init(self, builder, theme=None, dirs=None):
"""
- Called by the builder to initialize the template system. *builder*
- is the builder object; you'll probably want to look at the value of
- ``builder.config.templates_path``.
+ Called by the builder to initialize the template system.
+
+ *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.
"""
raise NotImplementedError('must be implemented in subclasses')
@@ -322,7 +364,14 @@ class TemplateBridge(object):
def render(self, template, context):
"""
- Called by the builder to render a *template* with a specified
- context (a Python dictionary).
+ Called by the builder to render a template given as a filename with a
+ specified context (a Python dictionary).
+ """
+ raise NotImplementedError('must be implemented in subclasses')
+
+ def render_string(self, template, context):
+ """
+ Called by the builder to render a template given as a string with a
+ specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')