buildframework/helium/external/python/lib/common/Sphinx-0.5.1-py2.5.egg/sphinx/application.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # -*- coding: utf-8 -*-
       
     2 """
       
     3     sphinx.application
       
     4     ~~~~~~~~~~~~~~~~~~
       
     5 
       
     6     Sphinx application object.
       
     7 
       
     8     Gracefully adapted from the TextPress system by Armin.
       
     9 
       
    10 
       
    11     :copyright: 2008 by Georg Brandl, Armin Ronacher.
       
    12     :license: BSD.
       
    13 """
       
    14 
       
    15 import sys
       
    16 import posixpath
       
    17 from cStringIO import StringIO
       
    18 
       
    19 from docutils import nodes
       
    20 from docutils.parsers.rst import directives, roles
       
    21 
       
    22 import sphinx
       
    23 from sphinx.roles import xfileref_role, innernodetypes
       
    24 from sphinx.config import Config
       
    25 from sphinx.builder import builtin_builders, StandaloneHTMLBuilder
       
    26 from sphinx.directives import desc_directive, target_directive, additional_xref_types
       
    27 from sphinx.environment import SphinxStandaloneReader
       
    28 from sphinx.util.console import bold
       
    29 
       
    30 
       
    31 class SphinxError(Exception):
       
    32     """
       
    33     Base class for Sphinx errors that are shown to the user in a nicer
       
    34     way than normal exceptions.
       
    35     """
       
    36     category = 'Sphinx error'
       
    37 
       
    38 class ExtensionError(SphinxError):
       
    39     """Raised if something's wrong with the configuration."""
       
    40     category = 'Extension error'
       
    41 
       
    42     def __init__(self, message, orig_exc=None):
       
    43         super(ExtensionError, self).__init__(message)
       
    44         self.orig_exc = orig_exc
       
    45 
       
    46     def __repr__(self):
       
    47         if self.orig_exc:
       
    48             return '%s(%r, %r)' % (self.__class__.__name__,
       
    49                                    self.message, self.orig_exc)
       
    50         return '%s(%r)' % (self.__class__.__name__, self.message)
       
    51 
       
    52     def __str__(self):
       
    53         parent_str = super(ExtensionError, self).__str__()
       
    54         if self.orig_exc:
       
    55             return '%s (exception: %s)' % (parent_str, self.orig_exc)
       
    56         return parent_str
       
    57 
       
    58 
       
    59 # List of all known core events. Maps name to arguments description.
       
    60 events = {
       
    61     'builder-inited': '',
       
    62     'env-purge-doc': 'env, docname',
       
    63     'source-read': 'docname, source text',
       
    64     'doctree-read': 'the doctree before being pickled',
       
    65     'missing-reference': 'env, node, contnode',
       
    66     'doctree-resolved': 'doctree, docname',
       
    67     'env-updated': 'env',
       
    68     'html-page-context': 'pagename, context, doctree or None',
       
    69     'build-finished': 'exception',
       
    70 }
       
    71 
       
    72 CONFIG_FILENAME = 'conf.py'
       
    73 
       
    74 class Sphinx(object):
       
    75 
       
    76     def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
       
    77                  confoverrides, status, warning=sys.stderr, freshenv=False):
       
    78         self.next_listener_id = 0
       
    79         self._listeners = {}
       
    80         self.builderclasses = builtin_builders.copy()
       
    81         self.builder = None
       
    82 
       
    83         self.srcdir = srcdir
       
    84         self.confdir = confdir
       
    85         self.outdir = outdir
       
    86         self.doctreedir = doctreedir
       
    87 
       
    88         if status is None:
       
    89             self._status = StringIO()
       
    90             self.quiet = True
       
    91         else:
       
    92             self._status = status
       
    93             self.quiet = False
       
    94         if warning is None:
       
    95             self._warning = StringIO()
       
    96         else:
       
    97             self._warning = warning
       
    98         self._warncount = 0
       
    99 
       
   100         self._events = events.copy()
       
   101 
       
   102         # status code for command-line application
       
   103         self.statuscode = 0
       
   104 
       
   105         # read config
       
   106         self.config = Config(confdir, CONFIG_FILENAME, confoverrides)
       
   107 
       
   108         # load all extension modules
       
   109         for extension in self.config.extensions:
       
   110             self.setup_extension(extension)
       
   111         # the config file itself can be an extension
       
   112         if self.config.setup:
       
   113             self.config.setup(self)
       
   114 
       
   115         # now that we know all config values, collect them from conf.py
       
   116         self.config.init_values()
       
   117 
       
   118         if buildername is None:
       
   119             print >>status, 'No builder selected, using default: html'
       
   120             buildername = 'html'
       
   121         if buildername not in self.builderclasses:
       
   122             raise SphinxError('Builder name %s not registered' % buildername)
       
   123 
       
   124         self.info(bold('Sphinx v%s, building %s' % (sphinx.__released__,
       
   125                                                     buildername)))
       
   126 
       
   127         builderclass = self.builderclasses[buildername]
       
   128         self.builder = builderclass(self, freshenv=freshenv)
       
   129         self.emit('builder-inited')
       
   130 
       
   131     def build(self, all_files, filenames):
       
   132         try:
       
   133             if all_files:
       
   134                 self.builder.build_all()
       
   135             elif filenames:
       
   136                 self.builder.build_specific(filenames)
       
   137             else:
       
   138                 self.builder.build_update()
       
   139         except Exception, err:
       
   140             self.emit('build-finished', err)
       
   141             raise
       
   142         else:
       
   143             self.emit('build-finished', None)
       
   144 
       
   145     def warn(self, message):
       
   146         self._warncount += 1
       
   147         try:
       
   148             self._warning.write('WARNING: %s\n' % message)
       
   149         except UnicodeEncodeError:
       
   150             encoding = getattr(self._warning, 'encoding', 'ascii')
       
   151             self._warning.write(('WARNING: %s\n' % message).encode(encoding, 'replace'))
       
   152 
       
   153     def info(self, message='', nonl=False):
       
   154         try:
       
   155             self._status.write(message)
       
   156         except UnicodeEncodeError:
       
   157             encoding = getattr(self._status, 'encoding', 'ascii')
       
   158             self._status.write(message.encode(encoding, 'replace'))
       
   159         if not nonl:
       
   160             self._status.write('\n')
       
   161         self._status.flush()
       
   162 
       
   163     # general extensibility interface
       
   164 
       
   165     def setup_extension(self, extension):
       
   166         """Import and setup a Sphinx extension module."""
       
   167         try:
       
   168             mod = __import__(extension, None, None, ['setup'])
       
   169         except ImportError, err:
       
   170             raise ExtensionError('Could not import extension %s' % extension, err)
       
   171         if hasattr(mod, 'setup'):
       
   172             mod.setup(self)
       
   173 
       
   174     def import_object(self, objname, source=None):
       
   175         """Import an object from a 'module.name' string."""
       
   176         try:
       
   177             module, name = objname.rsplit('.', 1)
       
   178         except ValueError, err:
       
   179             raise ExtensionError('Invalid full object name %s' % objname +
       
   180                                  (source and ' (needed for %s)' % source or ''), err)
       
   181         try:
       
   182             return getattr(__import__(module, None, None, [name]), name)
       
   183         except ImportError, err:
       
   184             raise ExtensionError('Could not import %s' % module +
       
   185                                  (source and ' (needed for %s)' % source or ''), err)
       
   186         except AttributeError, err:
       
   187             raise ExtensionError('Could not find %s' % objname +
       
   188                                  (source and ' (needed for %s)' % source or ''), err)
       
   189 
       
   190     # event interface
       
   191 
       
   192     def _validate_event(self, event):
       
   193         event = intern(event)
       
   194         if event not in self._events:
       
   195             raise ExtensionError('Unknown event name: %s' % event)
       
   196 
       
   197     def connect(self, event, callback):
       
   198         self._validate_event(event)
       
   199         listener_id = self.next_listener_id
       
   200         if event not in self._listeners:
       
   201             self._listeners[event] = {listener_id: callback}
       
   202         else:
       
   203             self._listeners[event][listener_id] = callback
       
   204         self.next_listener_id += 1
       
   205         return listener_id
       
   206 
       
   207     def disconnect(self, listener_id):
       
   208         for event in self._listeners.itervalues():
       
   209             event.pop(listener_id, None)
       
   210 
       
   211     def emit(self, event, *args):
       
   212         result = []
       
   213         if event in self._listeners:
       
   214             for _, callback in self._listeners[event].iteritems():
       
   215                 result.append(callback(self, *args))
       
   216         return result
       
   217 
       
   218     def emit_firstresult(self, event, *args):
       
   219         for result in self.emit(event, *args):
       
   220             if result is not None:
       
   221                 return result
       
   222         return None
       
   223 
       
   224     # registering addon parts
       
   225 
       
   226     def add_builder(self, builder):
       
   227         if not hasattr(builder, 'name'):
       
   228             raise ExtensionError('Builder class %s has no "name" attribute' % builder)
       
   229         if builder.name in self.builderclasses:
       
   230             raise ExtensionError('Builder %r already exists (in module %s)' % (
       
   231                 builder.name, self.builderclasses[builder.name].__module__))
       
   232         self.builderclasses[builder.name] = builder
       
   233 
       
   234     def add_config_value(self, name, default, rebuild_env):
       
   235         if name in self.config.values:
       
   236             raise ExtensionError('Config value %r already present' % name)
       
   237         self.config.values[name] = (default, rebuild_env)
       
   238 
       
   239     def add_event(self, name):
       
   240         if name in self._events:
       
   241             raise ExtensionError('Event %r already present' % name)
       
   242         self._events[name] = ''
       
   243 
       
   244     def add_node(self, node, **kwds):
       
   245         nodes._add_node_class_names([node.__name__])
       
   246         for key, val in kwds.iteritems():
       
   247             try:
       
   248                 visit, depart = val
       
   249             except ValueError:
       
   250                 raise ExtensionError('Value for key %r must be a (visit, depart) '
       
   251                                      'function tuple' % key)
       
   252             if key == 'html':
       
   253                 from sphinx.htmlwriter import HTMLTranslator as translator
       
   254             elif key == 'latex':
       
   255                 from sphinx.latexwriter import LaTeXTranslator as translator
       
   256             elif key == 'text':
       
   257                 from sphinx.textwriter import TextTranslator as translator
       
   258             else:
       
   259                 # ignore invalid keys for compatibility
       
   260                 continue
       
   261             setattr(translator, 'visit_'+node.__name__, visit)
       
   262             if depart:
       
   263                 setattr(translator, 'depart_'+node.__name__, depart)
       
   264 
       
   265     def add_directive(self, name, func, content, arguments, **options):
       
   266         func.content = content
       
   267         func.arguments = arguments
       
   268         func.options = options
       
   269         directives.register_directive(name, func)
       
   270 
       
   271     def add_role(self, name, role):
       
   272         roles.register_canonical_role(name, role)
       
   273 
       
   274     def add_description_unit(self, directivename, rolename, indextemplate='',
       
   275                              parse_node=None, ref_nodeclass=None):
       
   276         additional_xref_types[directivename] = (rolename, indextemplate, parse_node)
       
   277         directives.register_directive(directivename, desc_directive)
       
   278         roles.register_canonical_role(rolename, xfileref_role)
       
   279         if ref_nodeclass is not None:
       
   280             innernodetypes[rolename] = ref_nodeclass
       
   281 
       
   282     def add_crossref_type(self, directivename, rolename, indextemplate='',
       
   283                           ref_nodeclass=None):
       
   284         additional_xref_types[directivename] = (rolename, indextemplate, None)
       
   285         directives.register_directive(directivename, target_directive)
       
   286         roles.register_canonical_role(rolename, xfileref_role)
       
   287         if ref_nodeclass is not None:
       
   288             innernodetypes[rolename] = ref_nodeclass
       
   289 
       
   290     def add_transform(self, transform):
       
   291         SphinxStandaloneReader.transforms.append(transform)
       
   292 
       
   293     def add_javascript(self, filename):
       
   294         StandaloneHTMLBuilder.script_files.append(
       
   295             posixpath.join('_static', filename))
       
   296 
       
   297 
       
   298 class TemplateBridge(object):
       
   299     """
       
   300     This class defines the interface for a "template bridge", that is, a class
       
   301     that renders templates given a template name and a context.
       
   302     """
       
   303 
       
   304     def init(self, builder):
       
   305         """
       
   306         Called by the builder to initialize the template system.  *builder*
       
   307         is the builder object; you'll probably want to look at the value of
       
   308         ``builder.config.templates_path``.
       
   309         """
       
   310         raise NotImplementedError('must be implemented in subclasses')
       
   311 
       
   312     def newest_template_mtime(self):
       
   313         """
       
   314         Called by the builder to determine if output files are outdated
       
   315         because of template changes.  Return the mtime of the newest template
       
   316         file that was changed.  The default implementation returns ``0``.
       
   317         """
       
   318         return 0
       
   319 
       
   320     def render(self, template, context):
       
   321         """
       
   322         Called by the builder to render a *template* with a specified
       
   323         context (a Python dictionary).
       
   324         """
       
   325         raise NotImplementedError('must be implemented in subclasses')