diff options
Diffstat (limited to 'sphinx/application.py')
| -rw-r--r-- | sphinx/application.py | 193 |
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') |
