[Python-checkins] r60374 - in doctools/trunk/sphinx: __init__.py addnodes.py addons addons/__init__.py addons/ifconfig.py addons/refcounting.py application.py builder.py config.py directives.py environment.py extension.py latexwriter.py refcounting.py util/__init__.py util/console.py util/smartypants.py
georg.brandl
python-checkins at python.org
Sun Jan 27 21:23:25 CET 2008
Author: georg.brandl
Date: Sun Jan 27 21:23:25 2008
New Revision: 60374
Added:
doctools/trunk/sphinx/addons/
doctools/trunk/sphinx/addons/__init__.py
doctools/trunk/sphinx/addons/ifconfig.py
doctools/trunk/sphinx/addons/refcounting.py
doctools/trunk/sphinx/application.py
- copied, changed from r60174, doctools/trunk/sphinx/extension.py
Removed:
doctools/trunk/sphinx/extension.py
doctools/trunk/sphinx/refcounting.py
Modified:
doctools/trunk/sphinx/__init__.py
doctools/trunk/sphinx/addnodes.py
doctools/trunk/sphinx/builder.py
doctools/trunk/sphinx/config.py
doctools/trunk/sphinx/directives.py
doctools/trunk/sphinx/environment.py
doctools/trunk/sphinx/latexwriter.py
doctools/trunk/sphinx/util/__init__.py
doctools/trunk/sphinx/util/console.py
doctools/trunk/sphinx/util/smartypants.py
Log:
More refactoring:
* Move refcounting into an addon module.
* Rename the extension manager to Application and use it throughout.
* Fix some bugs found by pylint.
* Add "ifconfig" addon.
Modified: doctools/trunk/sphinx/__init__.py
==============================================================================
--- doctools/trunk/sphinx/__init__.py (original)
+++ doctools/trunk/sphinx/__init__.py Sun Jan 27 21:23:25 2008
@@ -15,55 +15,19 @@
from os import path
from cStringIO import StringIO
-from sphinx.config import Config, ConfigError
-from sphinx.builder import builders
-from sphinx.extension import EventManager
+from sphinx.application import Application
from sphinx.util.console import nocolor
__version__ = '$Revision: 5369 $'[11:-2]
-def init_builder(buildername, srcdirname, outdirname, doctreedir,
- confoverrides, status, warning=sys.stderr, freshenv=False):
- # read config
- config = Config(srcdirname, 'conf.py')
- if confoverrides:
- for key, val in confoverrides.items():
- setattr(config, key, val)
-
- # extensibility
- events = EventManager()
- for extension in config.extensions:
- try:
- mod = __import__(extension, None, None, ['setup'])
- except ImportError, err:
- raise ConfigError('Could not import extension %s' % module, err)
- if hasattr(mod, 'setup'):
- mod.setup(events, builders)
-
- if buildername not in builders:
- print >>warning, 'Builder name %s not registered' % buildername
- return None
-
- if buildername is None:
- print >>status, 'No builder selected, using default: html'
- buildername = 'html'
-
- builderclass = builders[buildername]
- builder = builderclass(srcdirname, outdirname, doctreedir,
- status_stream=status, warning_stream=warning,
- events=events, config=config, freshenv=freshenv)
- events.emit('builder-created', builder)
- return builder
-
-
def usage(argv, msg=None):
if msg:
print >>sys.stderr, msg
print >>sys.stderr
print >>sys.stderr, """\
usage: %s [options] sourcedir outdir [filenames...]"
-options: -b <builder> -- builder to use (one of %s)
+options: -b <builder> -- builder to use; default is html
-a -- write all files; default is to only write new and changed files
-E -- don't use a saved environment, always read all files
-d <path> -- path for the cached environment and doctree files
@@ -75,7 +39,7 @@
modi:
* without -a and without filenames, write new and changed files.
* with -a, write all files.
-* with filenames, write these.""" % (argv[0], ', '.join(builders))
+* with filenames, write these.""" % (argv[0],)
def main(argv):
@@ -85,15 +49,15 @@
try:
opts, args = getopt.getopt(argv[1:], 'ab:d:D:NEqP')
- srcdirname = path.abspath(args[0])
- if not path.isdir(srcdirname):
+ srcdir = path.abspath(args[0])
+ if not path.isdir(srcdir):
print >>sys.stderr, 'Error: Cannot find source directory.'
return 1
- if not path.isfile(path.join(srcdirname, 'conf.py')):
+ if not path.isfile(path.join(srcdir, 'conf.py')):
print >>sys.stderr, 'Error: Source directory doesn\'t contain conf.py file.'
return 1
- outdirname = path.abspath(args[1])
- if not path.isdir(outdirname):
+ outdir = path.abspath(args[1])
+ if not path.isdir(outdir):
print >>sys.stderr, 'Error: Cannot find output directory.'
return 1
except (IndexError, getopt.error):
@@ -113,7 +77,7 @@
freshenv = use_pdb = False
status = sys.stdout
confoverrides = {}
- doctreedir = path.join(outdirname, '.doctrees')
+ doctreedir = path.join(outdir, '.doctrees')
for opt, val in opts:
if opt == '-b':
buildername = val
@@ -139,18 +103,18 @@
elif opt == '-P':
use_pdb = True
- builder = init_builder(buildername, srcdirname, outdirname, doctreedir,
- confoverrides, status, sys.stderr, freshenv)
- if not builder:
+ app = Application(srcdir, outdir, doctreedir, buildername,
+ confoverrides, status, sys.stderr, freshenv)
+ if not app.builder:
return 1
try:
if all_files:
- builder.build_all()
+ app.builder.build_all()
elif filenames:
- builder.build_specific(filenames)
+ app.builder.build_specific(filenames)
else:
- builder.build_update()
+ app.builder.build_update()
except:
if not use_pdb:
raise
Modified: doctools/trunk/sphinx/addnodes.py
==============================================================================
--- doctools/trunk/sphinx/addnodes.py (original)
+++ doctools/trunk/sphinx/addnodes.py Sun Jan 27 21:23:25 2008
@@ -29,9 +29,6 @@
def astext(self):
return '[' + nodes.TextElement.astext(self) + ']'
-# refcount annotation
-class refcount(nodes.emphasis): pass
-
# \versionadded, \versionchanged, \deprecated
class versionmodified(nodes.Admonition, nodes.TextElement): pass
Added: doctools/trunk/sphinx/addons/__init__.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/__init__.py Sun Jan 27 21:23:25 2008
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons
+ ~~~~~~~~~~~~~
+
+ Contains Sphinx features not activated by default.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
Added: doctools/trunk/sphinx/addons/ifconfig.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/ifconfig.py Sun Jan 27 21:23:25 2008
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons.ifconfig
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Provides the ``ifconfig`` directive that allows to write documentation
+ that is included depending on configuration variables.
+
+ Usage::
+
+ .. ifconfig:: releaselevel in ('alpha', 'beta', 'rc')
+
+ This stuff is only included in the built docs for unstable versions.
+
+ The argument for ``ifconfig`` is a plain Python expression, evaluated in the
+ namespace of the project configuration (that is, all variables from ``conf.py``
+ are available.)
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+from docutils import nodes
+
+
+class ifconfig(nodes.Element): pass
+
+
+def ifconfig_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ node = ifconfig()
+ node['expr'] = arguments[0]
+ state.nested_parse(content, content_offset, node)
+ return [node]
+
+
+def process_ifconfig_nodes(app, doctree, docfilename):
+ ns = app.config.__dict__.copy()
+ ns['builder'] = app.builder.name
+ for node in doctree.traverse(ifconfig):
+ if not eval(node['expr'], ns):
+ node.replace_self([])
+ else:
+ node.replace_self(node.children)
+
+
+def setup(app):
+ app.add_node(ifconfig)
+ app.add_directive('ifconfig', ifconfig_directive, 1, (1, 0, 1))
+ app.connect('doctree-resolved', process_ifconfig_nodes)
Added: doctools/trunk/sphinx/addons/refcounting.py
==============================================================================
--- (empty file)
+++ doctools/trunk/sphinx/addons/refcounting.py Sun Jan 27 21:23:25 2008
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.addons.refcounting
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Supports reference count annotations for C API functions. Based on
+ refcount.py and anno-api.py in the old Python documentation tools.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD.
+"""
+
+from os import path
+from docutils import nodes
+
+from sphinx import addnodes
+
+
+# refcount annotation
+class refcount(nodes.emphasis): pass
+
+
+class RCEntry:
+ def __init__(self, name):
+ self.name = name
+ self.args = []
+ self.result_type = ''
+ self.result_refs = None
+
+
+class Refcounts(dict):
+ @classmethod
+ def fromfile(cls, filename):
+ d = cls()
+ fp = open(filename, 'r')
+ try:
+ for line in fp:
+ line = line.strip()
+ if line[:1] in ("", "#"):
+ # blank lines and comments
+ continue
+ parts = line.split(":", 4)
+ if len(parts) != 5:
+ raise ValueError("Wrong field count in %r" % line)
+ function, type, arg, refcount, comment = parts
+ # Get the entry, creating it if needed:
+ try:
+ entry = d[function]
+ except KeyError:
+ entry = d[function] = RCEntry(function)
+ if not refcount or refcount == "null":
+ refcount = None
+ else:
+ refcount = int(refcount)
+ # Update the entry with the new parameter or the result information.
+ if arg:
+ entry.args.append((arg, type, refcount))
+ else:
+ entry.result_type = type
+ entry.result_refs = refcount
+ finally:
+ fp.close()
+ return d
+
+ def add_refcount_annotations(self, app, doctree):
+ for node in doctree.traverse(addnodes.desc_content):
+ par = node.parent
+ if par['desctype'] != 'cfunction':
+ continue
+ if not par[0].has_key('names') or not par[0]['names']:
+ continue
+ entry = self.get(par[0]['names'][0])
+ if not entry:
+ continue
+ elif entry.result_type not in ("PyObject*", "PyVarObject*"):
+ continue
+ rc = 'Return value: '
+ if entry.result_refs is None:
+ rc += "Always NULL."
+ else:
+ rc += (entry.result_refs and "New" or "Borrowed") + " reference."
+ node.insert(0, refcount(rc, rc))
+
+
+def init_refcounts(app):
+ if app.config.refcount_file:
+ refcounts = Refcounts.fromfile(
+ path.join(app.srcdir, app.config.refcount_file))
+ app.connect('doctree-read', refcounts.add_refcount_annotations)
+
+
+def setup(app):
+ app.add_node(refcount)
+ app.add_config_value('refcount_file', '', True)
+ app.connect('builder-inited', init_refcounts)
Copied: doctools/trunk/sphinx/application.py (from r60174, doctools/trunk/sphinx/extension.py)
==============================================================================
--- doctools/trunk/sphinx/extension.py (original)
+++ doctools/trunk/sphinx/application.py Sun Jan 27 21:23:25 2008
@@ -1,60 +1,139 @@
# -*- coding: utf-8 -*-
"""
- sphinx.extension
- ~~~~~~~~~~~~~~~~
+ sphinx.application
+ ~~~~~~~~~~~~~~~~~~
+
+ Sphinx application object.
+
+ Gracefully adapted from the TextPress system by Armin.
- Gracefully adapted from the TextPress event system by Armin.
:copyright: 2008 by Georg Brandl, Armin Ronacher.
:license: BSD.
"""
-from sphinx.config import ConfigError
+import sys
+
+from docutils import nodes
+from docutils.parsers.rst import directives, roles
+
+from sphinx.config import Config
+from sphinx.builder import builtin_builders
-def import_object(objname, source=None):
- """Import an object from a 'module.name' string."""
- try:
- module, name = objname.rsplit('.', 1)
- except ValueError, err:
- raise ConfigError('Invalid full object name %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
- try:
- return getattr(__import__(module, None, None, [name]), name)
- except ImportError, err:
- raise ConfigError('Could not import %s' % module +
- (source and ' (needed for %s)' % source or ''), err)
- except AttributeError, err:
- raise ConfigError('Could not find %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
+class ExtensionError(Exception):
+ """Raised if something's wrong with the configuration."""
+
+ def __init__(self, message, orig_exc=None):
+ self.message = 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):
+ if self.orig_exc:
+ return '%s (exception: %s)' % (self.message, self.orig_exc)
+ return self.message
# List of all known events. Maps name to arguments description.
events = {
- 'builder-created' : 'builder instance',
+ 'builder-inited': 'builder instance',
'doctree-read' : 'the doctree before being pickled',
+ 'doctree-resolved' : 'the doctree, the filename, the builder',
}
-class EventManager(object):
- """
- Helper class that handles event listeners and events.
-
- This is *not* a public interface. Always use the emit_event()
- functions to access it or the connect_event() / disconnect_event()
- functions on the application.
- """
+class Application(object):
- def __init__(self):
+ def __init__(self, srcdir, outdir, doctreedir, buildername,
+ confoverrides, status, warning=sys.stderr, freshenv=False):
self.next_listener_id = 0
self._listeners = {}
+ self.builderclasses = builtin_builders.copy()
+ self.builder = None
- def _validate(self, event):
+ self.srcdir = srcdir
+ self.outdir = outdir
+ self.doctreedir = doctreedir
+
+ self._status = status
+ self._warning = warning
+
+ # read config
+ self.config = Config(srcdir, 'conf.py')
+ if confoverrides:
+ for key, val in confoverrides.items():
+ setattr(self.config, key, val)
+
+ # load all extension modules
+ for extension in getattr(self.config, 'extensions', ()):
+ self.setup_extension(extension)
+
+ # this must happen after loading extension modules, since they
+ # can add custom config values
+ self.config.init_defaults()
+
+ if buildername is None:
+ print >>status, 'No builder selected, using default: html'
+ buildername = 'html'
+ if buildername not in self.builderclasses:
+ print >>warning, 'Builder name %s not registered' % buildername
+ return
+
+ builderclass = self.builderclasses[buildername]
+ self.builder = builderclass(self, freshenv=freshenv)
+ self.emit('builder-inited')
+
+ def warn(self, message):
+ self._warning.write('WARNING: %s\n' % message)
+
+ def info(self, message='', nonl=False):
+ if nonl:
+ self._status.write(message)
+ else:
+ self._status.write(message + '\n')
+ self._status.flush()
+
+ # general extensibility interface
+
+ def setup_extension(self, extension):
+ """Import and setup a Sphinx extension module."""
+ try:
+ mod = __import__(extension, None, None, ['setup'])
+ except ImportError, err:
+ raise ExtensionError('Could not import extension %s' % extension, err)
+ if hasattr(mod, 'setup'):
+ mod.setup(self)
+
+ def import_object(self, objname, source=None):
+ """Import an object from a 'module.name' string."""
+ try:
+ module, name = objname.rsplit('.', 1)
+ except ValueError, err:
+ raise ExtensionError('Invalid full object name %s' % objname +
+ (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)
+ except AttributeError, err:
+ raise ExtensionError('Could not find %s' % objname +
+ (source and ' (needed for %s)' % source or ''), err)
+
+ # event interface
+
+ def _validate_event(self, event):
event = intern(event)
if event not in events:
- raise RuntimeError('unknown event name: %s' % event)
+ raise ExtensionError('Unknown event name: %s' % event)
def connect(self, event, callback):
- self._validate(event)
+ self._validate_event(event)
listener_id = self.next_listener_id
if event not in self._listeners:
self._listeners[event] = {listener_id: callback}
@@ -63,21 +142,39 @@
self.next_listener_id += 1
return listener_id
- def remove(self, listener_id):
+ def disconnect(self, listener_id):
for event in self._listeners:
event.pop(listener_id, None)
def emit(self, event, *args):
- self._validate(event)
+ result = []
if event in self._listeners:
- for listener_id, callback in self._listeners[event].iteritems():
- yield listener_id, callback(*args)
+ for _, callback in self._listeners[event].iteritems():
+ result.append(callback(self, *args))
+ return result
+
+ # registering addon parts
+
+ def add_builder(self, builder):
+ if not hasattr(builder, 'name'):
+ 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__))
+ self.builderclasses[builder.name] = builder
+
+ def add_config_value(self, name, default, rebuild_env):
+ if name in self.config.values:
+ raise ExtensionError('Config value %r already present')
+ self.config.values[name] = (default, rebuild_env)
+
+ def add_node(self, node):
+ nodes._add_node_class_names([node.__name__])
+
+ def add_directive(self, name, cls, content, arguments):
+ cls.content = content
+ cls.arguments = arguments
+ directives.register_directive(name, cls)
-
-class DummyEventManager(EventManager):
- def connect(self, event, callback):
- self._validate(event)
- def remove(self, listener_id):
- pass
- def emit(self, event, *args):
- self._validate(event)
+ def add_role(self, name, role):
+ roles.register_canonical_role(name, role)
Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py (original)
+++ doctools/trunk/sphinx/builder.py Sun Jan 27 21:23:25 2008
@@ -10,12 +10,10 @@
"""
import os
-import sys
import time
import codecs
import shutil
import cPickle as pickle
-import cStringIO as StringIO
from os import path
from cgi import escape
@@ -23,18 +21,16 @@
from docutils.io import StringOutput, FileOutput, DocTreeInput
from docutils.core import publish_parts
from docutils.utils import new_document
-from docutils.readers import doctree
from docutils.frontend import OptionParser
+from docutils.readers.doctree import Reader as DoctreeReader
from sphinx import addnodes
-from sphinx.util import (get_matching_files, attrdict, status_iterator,
- ensuredir, relative_uri, os_path, SEP)
+from sphinx.util import (get_matching_files, ensuredir, relative_uri, os_path, SEP)
from sphinx.htmlhelp import build_hhx
-from sphinx.extension import DummyEventManager, import_object
from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
from sphinx.latexwriter import LaTeXWriter
from sphinx.environment import BuildEnvironment, NoUri
-from sphinx.highlighting import pygments, highlight_block, get_stylesheet
+from sphinx.highlighting import pygments, get_stylesheet
from sphinx.util.console import bold, purple, green
# side effect: registers roles and directives
@@ -49,40 +45,26 @@
Builds target formats from the reST sources.
"""
- def __init__(self, srcdirname, outdirname, doctreedirname,
- config, env=None, freshenv=False, events=None,
- status_stream=None, warning_stream=None):
- self.srcdir = srcdirname
- self.outdir = outdirname
- self.doctreedir = doctreedirname
- if not path.isdir(doctreedirname):
- os.mkdir(doctreedirname)
- self.freshenv = freshenv
-
- self.status_stream = status_stream or sys.stdout
- self.warning_stream = warning_stream or sys.stderr
+ def __init__(self, app, env=None, freshenv=False):
+ self.srcdir = app.srcdir
+ self.outdir = app.outdir
+ self.doctreedir = app.doctreedir
+ if not path.isdir(self.doctreedir):
+ os.mkdir(self.doctreedir)
+
+ self.app = app
+ self.warn = app.warn
+ self.info = app.info
+ self.config = app.config
- self.config = config
# if None, this is set in load_env()
self.env = env
-
- self.events = events or DummyEventManager()
+ self.freshenv = freshenv
self.init()
# helper methods
- def msg(self, message='', nonl=False, nobold=False):
- if not nobold: message = bold(message)
- if nonl:
- print >>self.status_stream, message,
- else:
- print >>self.status_stream, message
- self.status_stream.flush()
-
- def warn(self, message):
- print >>self.warning_stream, 'WARNING:', message
-
def init(self):
"""Load necessary templates and perform initialization."""
raise NotImplementedError
@@ -122,6 +104,17 @@
"""Return a list of output files that are outdated."""
raise NotImplementedError
+ def status_iterator(self, iterable, summary, colorfunc):
+ l = -1
+ for item in iterable:
+ if l == -1:
+ self.info(bold(summary), nonl=1)
+ l = 0
+ self.info(colorfunc(item) + ' ', nonl=1)
+ yield item
+ if l == 0:
+ self.info()
+
# build methods
def load_env(self):
@@ -131,12 +124,12 @@
return
if not self.freshenv:
try:
- self.msg('trying to load pickled env...', nonl=True)
+ self.info(bold('trying to load pickled env... '), nonl=True)
self.env = BuildEnvironment.frompickle(
path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.msg('done', nobold=True)
+ self.info('done')
except Exception, err:
- self.msg('failed: %s' % err, nobold=True)
+ self.info('failed: %s' % err)
self.env = BuildEnvironment(self.srcdir, self.doctreedir)
else:
self.env = BuildEnvironment(self.srcdir, self.doctreedir)
@@ -161,7 +154,7 @@
self.load_env()
to_build = self.get_outdated_files()
if not to_build:
- self.msg('no target files are out of date, exiting.')
+ self.info(bold('no target files are out of date, exiting.'))
return
if isinstance(to_build, str):
self.build([], to_build)
@@ -173,36 +166,32 @@
def build(self, filenames, summary=None):
if summary:
- self.msg('building [%s]:' % self.name, nonl=1)
- self.msg(summary, nobold=1)
+ self.info(bold('building [%s]: ' % self.name), nonl=1)
+ self.info(summary)
updated_filenames = []
# while reading, collect all warnings from docutils
warnings = []
self.env.set_warnfunc(warnings.append)
- self.msg('reading, updating environment:', nonl=1)
- iterator = self.env.update(
- self.config,
- hook=lambda doctree: self.events.emit('doctree-read', doctree))
- self.msg(iterator.next(), nonl=1, nobold=1)
- for filename in iterator:
- if not updated_filenames:
- self.msg('')
+ self.info(bold('updating environment: '), nonl=1)
+ iterator = self.env.update(self.config, self.app)
+ # the first item in the iterator is a summary message
+ self.info(iterator.next())
+ for filename in self.status_iterator(iterator, 'reading... ', purple):
updated_filenames.append(filename)
- self.msg(purple(filename), nonl=1, nobold=1)
- self.msg()
+ # nothing further to do, the environment has already done the reading
for warning in warnings:
self.warn(warning)
self.env.set_warnfunc(self.warn)
if updated_filenames:
# save the environment
- self.msg('pickling the env...', nonl=True)
+ self.info(bold('pickling the env... '), nonl=True)
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.msg('done', nobold=True)
+ self.info('done')
# global actions
- self.msg('checking consistency...')
+ self.info(bold('checking consistency...'))
self.env.check_consistency()
# another indirection to support methods which don't build files
@@ -210,9 +199,9 @@
self.write(filenames, updated_filenames)
# finish (write style files etc.)
- self.msg('finishing...')
+ self.info(bold('finishing... '))
self.finish()
- self.msg('done!')
+ self.info(bold('build succeeded.'))
def write(self, build_filenames, updated_filenames):
if build_filenames is None: # build_all
@@ -225,16 +214,15 @@
filenames.add(tocfilename)
filenames.add('contents.rst')
- self.msg('creating index...')
+ self.info(bold('creating index...'))
self.env.create_index(self)
self.prepare_writing(filenames)
# write target files
warnings = []
self.env.set_warnfunc(warnings.append)
- self.msg('writing output...')
- for filename in status_iterator(sorted(filenames), green,
- stream=self.status_stream):
+ for filename in self.status_iterator(sorted(filenames),
+ 'writing output... ', green):
doctree = self.env.get_and_resolve_doctree(filename, self)
self.write_file(filename, doctree)
for warning in warnings:
@@ -263,8 +251,8 @@
"""Load templates."""
self.init_templates()
if self.config.html_translator_class:
- self.translator_class = import_object(self.config.html_translator_class,
- 'html_translator_class setting')
+ self.translator_class = self.app.import_object(
+ self.config.html_translator_class, 'html_translator_class setting')
elif self.config.html_use_smartypants:
self.translator_class = SmartyPantsHTMLTranslator
else:
@@ -277,7 +265,7 @@
return publish_parts(
doc,
source_class=DocTreeInput,
- reader=doctree.Reader(),
+ reader=DoctreeReader(),
writer=HTMLWriter(self),
settings_overrides={'output_encoding': 'unicode'}
)
@@ -316,7 +304,7 @@
destination = StringOutput(encoding='utf-8')
doctree.settings = self.docsettings
- output = self.docwriter.write(doctree, destination)
+ self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
prev = next = None
@@ -360,14 +348,14 @@
self.handle_page(pagename, context)
def finish(self):
- self.msg('writing additional files...')
+ self.info(bold('writing additional files...'))
# the global general index
# the total count of lines for each index letter, used to distribute
# the entries into two columns
indexcounts = []
- for key, entries in self.env.index:
+ for _, entries in self.env.index:
indexcounts.append(sum(1 + len(subitems) for _, (_, subitems) in entries))
genindexcontext = dict(
@@ -434,7 +422,7 @@
self.handle_page('index', {'indextemplate': indextemplate}, 'index.html')
# copy style files
- self.msg('copying style files...')
+ self.info(bold('copying style files...'))
styledirname = path.join(path.dirname(__file__), 'style')
ensuredir(path.join(self.outdir, 'style'))
for filename in os.listdir(styledirname):
@@ -519,7 +507,7 @@
path.join(self.outdir, os_path(ctx['sourcename'])))
def handle_finish(self):
- self.msg('dumping search index...')
+ self.info(bold('dumping search index...'))
self.indexer.prune([fn[:-4] for fn in self.env.all_files])
f = open(path.join(self.outdir, 'searchindex.json'), 'w')
try:
@@ -576,7 +564,7 @@
def handle_page(self, pagename, context, templatename='page.html'):
context['current_page_name'] = pagename
- sidebarfile = self.confightml_sidebars.get(pagename, '')
+ sidebarfile = self.config.html_sidebars.get(pagename, '')
if sidebarfile:
context['customsidebar'] = path.join(self.srcdir, sidebarfile)
outfilename = path.join(self.outdir, os_path(pagename) + '.fpickle')
@@ -603,7 +591,7 @@
finally:
f.close()
- self.msg('dumping search index...')
+ self.info(bold('dumping search index...'))
self.indexer.prune(self.env.all_files)
f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb')
try:
@@ -698,7 +686,7 @@
doctree.settings.author = author
doctree.settings.filename = sourcename
doctree.settings.docclass = docclass
- output = docwriter.write(doctree, destination)
+ docwriter.write(doctree, destination)
print "done"
def assemble_doctree(self, indexfile, appendices):
@@ -746,7 +734,7 @@
return largetree
def finish(self):
- self.msg('copying TeX support files...')
+ self.info(bold('copying TeX support files...'))
styledirname = path.join(path.dirname(__file__), 'texinputs')
for filename in os.listdir(styledirname):
if not filename.startswith('.'):
@@ -780,7 +768,7 @@
libchanges = {}
apichanges = []
otherchanges = {}
- self.msg('writing summary file...')
+ self.info(bold('writing summary file...'))
for type, filename, lineno, module, descname, content in \
self.env.versionchanges[version]:
ttext = self.typemap[type]
@@ -841,7 +829,7 @@
break
return line
- self.msg('copying source files...')
+ self.info(bold('copying source files...'))
for filename in self.env.all_files:
f = open(path.join(self.srcdir, os_path(filename)))
lines = f.readlines()
@@ -868,7 +856,7 @@
pass
-builders = {
+builtin_builders = {
'html': StandaloneHTMLBuilder,
'web': WebHTMLBuilder,
'htmlhelp': HTMLHelpBuilder,
Modified: doctools/trunk/sphinx/config.py
==============================================================================
--- doctools/trunk/sphinx/config.py (original)
+++ doctools/trunk/sphinx/config.py Sun Jan 27 21:23:25 2008
@@ -10,29 +10,10 @@
"""
import os
-import sys
import types
from os import path
-class ConfigError(Exception):
- """Raised if something's wrong with the configuration."""
-
- def __init__(self, message, orig_exc=None):
- self.message = message
- self.orig_exc = orig_exc
-
- def __repr__(self):
- if self.orig_exc:
- return 'ConfigError(%r, %r)' % (self.message, self.orig_exc)
- return 'ConfigError(%r)' % self.message
-
- def __str__(self):
- if self.orig_exc:
- return '%s (exception: %s)' % (self.message, self.orig_exc)
- return self.message
-
-
class Config(object):
"""Configuration file abstraction."""
@@ -53,7 +34,6 @@
# general reading options
unused_files = ([], True),
- refcount_file = ('', True),
add_function_parentheses = (True, True),
add_module_names = (True, True),
@@ -77,6 +57,7 @@
)
def __init__(self, dirname, filename):
+ self.values = self.config_values.copy()
config = {}
olddir = os.getcwd()
try:
@@ -90,14 +71,10 @@
del config[key]
self.__dict__.update(config)
- def __getattr__(self, name):
- if name in self.config_values:
- defval = self.config_values[name][0]
- setattr(self, name, defval)
- return defval
- if name[0:1] == '_':
- return object.__getattr__(self, name)
- raise AttributeError('no configuration value named %r' % name)
+ def init_defaults(self):
+ for val in self.values:
+ if val not in self.__dict__:
+ self.__dict__[val] = self.values[val][0]
def __getitem__(self, name):
return getattr(self, name)
Modified: doctools/trunk/sphinx/directives.py
==============================================================================
--- doctools/trunk/sphinx/directives.py (original)
+++ doctools/trunk/sphinx/directives.py Sun Jan 27 21:23:25 2008
@@ -15,7 +15,7 @@
from os import path
from docutils import nodes
-from docutils.parsers.rst import directives, roles
+from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import admonitions
from sphinx import addnodes
@@ -273,21 +273,6 @@
return optname
-def add_refcount_annotation(env, node, name):
- """Add a reference count annotation. Return None."""
- entry = env.refcounts.get(name)
- if not entry:
- return
- elif entry.result_type not in ("PyObject*", "PyVarObject*"):
- return
- rc = 'Return value: '
- if entry.result_refs is None:
- rc += "Always NULL."
- else:
- rc += (entry.result_refs and "New" or "Borrowed") + " reference."
- node += addnodes.refcount(rc, rc)
-
-
def desc_directive(desctype, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
env = state.document.settings.env
@@ -361,8 +346,6 @@
fullname, fullname)
subnode = addnodes.desc_content()
- if desctype == 'cfunction':
- add_refcount_annotation(env, subnode, name)
# needed for automatic qualification of members
clsname_set = False
if desctype == 'class' and names:
@@ -407,8 +390,8 @@
'describe',
]
-for name in desctypes:
- directives.register_directive(name, desc_directive)
+for _name in desctypes:
+ directives.register_directive(_name, desc_directive)
# ------ versionadded/versionchanged -----------------------------------------------
Modified: doctools/trunk/sphinx/environment.py
==============================================================================
--- doctools/trunk/sphinx/environment.py (original)
+++ doctools/trunk/sphinx/environment.py Sun Jan 27 21:23:25 2008
@@ -44,7 +44,6 @@
from sphinx import addnodes
from sphinx.util import get_matching_files, os_path, SEP
-from sphinx.refcounting import Refcounts
default_settings = {
'embed_stylesheet': False,
@@ -128,7 +127,7 @@
class MyStandaloneReader(standalone.Reader):
"""
- Add our own Substitutions transform.
+ Add our own transforms.
"""
def get_transforms(self):
tf = standalone.Reader.get_transforms(self)
@@ -189,9 +188,6 @@
self.srcdir = srcdir
self.config = None
- # refcount data if present
- self.refcounts = {}
-
# the docutils settings for building
self.settings = default_settings.copy()
self.settings['env'] = self
@@ -314,7 +310,7 @@
return added, changed, removed
- def update(self, config, hook=None):
+ def update(self, config, app=None):
"""(Re-)read all files new or changed since last update. Yields a summary
and then filenames as it processes them. Store all environment filenames
in the canonical format (ie using SEP as a separator in place of
@@ -341,11 +337,6 @@
self.config = config
- # read the refcounts file
- if self.config.refcount_file:
- self.refcounts = Refcounts.fromfile(
- path.join(self.srcdir, self.config.refcount_file))
-
# clear all files no longer present
for filename in removed:
self.clear_file(filename)
@@ -353,14 +344,14 @@
# read all new and changed files
for filename in added + changed:
yield filename
- self.read_file(filename)
+ self.read_file(filename, app=app)
if 'contents.rst' not in self.all_files:
self._warnfunc('no master file contents.rst found')
# --------- SINGLE FILE BUILDING -------------------------------------------
- def read_file(self, filename, src_path=None, save_parsed=True, hook=None):
+ def read_file(self, filename, src_path=None, save_parsed=True, app=None):
"""Parse a file and add/update inventory entries for the doctree.
If srcpath is given, read from a different source file."""
# remove all inventory entries for that file
@@ -386,9 +377,8 @@
f.close()
self.all_files[filename] = (path.getmtime(src_path), md5sum)
- # run post-read hook
- if hook:
- hook(doctree)
+ if app:
+ app.emit('doctree-read', doctree)
# make it picklable
doctree.reporter = None
@@ -590,7 +580,7 @@
for includefile in includefiles:
try:
toc = self.tocs[includefile].deepcopy()
- except KeyError, err:
+ except KeyError:
# this is raised if the included file does not exist
self._warnfunc('%s: toctree contains ref to nonexisting '
'file %r' % (filename, includefile))
@@ -622,6 +612,9 @@
return doctree
+ descroles = frozenset(('data', 'exc', 'func', 'class', 'const', 'attr',
+ 'meth', 'cfunc', 'cdata', 'ctype', 'cmacro'))
+
def resolve_references(self, doctree, docfilename, builder):
for node in doctree.traverse(addnodes.pending_xref):
contnode = node[0].deepcopy()
@@ -702,7 +695,7 @@
(platform and '(%s) ' % platform),
synopsis, (deprecated and ' (deprecated)' or ''))
newnode.append(contnode)
- else:
+ elif typ in self.descroles:
# "descrefs"
modname = node['modname']
clsname = node['classname']
@@ -720,11 +713,16 @@
builder.get_relative_uri(docfilename, desc[0])
+ '#' + name)
newnode.append(contnode)
+ else:
+ raise RuntimeError('unknown xfileref node encountered: %s' % node)
except NoUri:
newnode = contnode
if newnode:
node.replace_self(newnode)
+ # allow custom references to be resolved
+ builder.app.emit('doctree-resolved', doctree, docfilename)
+
def create_index(self, builder, _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
"""Create the real index from the collected index entries."""
new = {}
Deleted: /doctools/trunk/sphinx/extension.py
==============================================================================
--- /doctools/trunk/sphinx/extension.py Sun Jan 27 21:23:25 2008
+++ (empty file)
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.extension
- ~~~~~~~~~~~~~~~~
-
- Gracefully adapted from the TextPress event system by Armin.
-
- :copyright: 2008 by Georg Brandl, Armin Ronacher.
- :license: BSD.
-"""
-
-from sphinx.config import ConfigError
-
-
-def import_object(objname, source=None):
- """Import an object from a 'module.name' string."""
- try:
- module, name = objname.rsplit('.', 1)
- except ValueError, err:
- raise ConfigError('Invalid full object name %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
- try:
- return getattr(__import__(module, None, None, [name]), name)
- except ImportError, err:
- raise ConfigError('Could not import %s' % module +
- (source and ' (needed for %s)' % source or ''), err)
- except AttributeError, err:
- raise ConfigError('Could not find %s' % objname +
- (source and ' (needed for %s)' % source or ''), err)
-
-
-# List of all known events. Maps name to arguments description.
-events = {
- 'builder-created' : 'builder instance',
- 'doctree-read' : 'the doctree before being pickled',
-}
-
-class EventManager(object):
- """
- Helper class that handles event listeners and events.
-
- This is *not* a public interface. Always use the emit_event()
- functions to access it or the connect_event() / disconnect_event()
- functions on the application.
- """
-
- def __init__(self):
- self.next_listener_id = 0
- self._listeners = {}
-
- def _validate(self, event):
- event = intern(event)
- if event not in events:
- raise RuntimeError('unknown event name: %s' % event)
-
- def connect(self, event, callback):
- self._validate(event)
- listener_id = self.next_listener_id
- if event not in self._listeners:
- self._listeners[event] = {listener_id: callback}
- else:
- self._listeners[event][listener_id] = callback
- self.next_listener_id += 1
- return listener_id
-
- def remove(self, listener_id):
- for event in self._listeners:
- event.pop(listener_id, None)
-
- def emit(self, event, *args):
- self._validate(event)
- if event in self._listeners:
- for listener_id, callback in self._listeners[event].iteritems():
- yield listener_id, callback(*args)
-
-
-class DummyEventManager(EventManager):
- def connect(self, event, callback):
- self._validate(event)
- def remove(self, listener_id):
- pass
- def emit(self, event, *args):
- self._validate(event)
Modified: doctools/trunk/sphinx/latexwriter.py
==============================================================================
--- doctools/trunk/sphinx/latexwriter.py (original)
+++ doctools/trunk/sphinx/latexwriter.py Sun Jan 27 21:23:25 2008
@@ -14,9 +14,8 @@
import re
import time
-import string
-from docutils import frontend, nodes, languages, writers, utils
+from docutils import nodes, writers
from sphinx import addnodes
from sphinx import highlighting
Deleted: /doctools/trunk/sphinx/refcounting.py
==============================================================================
--- /doctools/trunk/sphinx/refcounting.py Sun Jan 27 21:23:25 2008
+++ (empty file)
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.refcounting
- ~~~~~~~~~~~~~~~~~~
-
- Handle reference counting annotations, based on refcount.py
- and anno-api.py.
-
- :copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
-"""
-
-class RCEntry:
- def __init__(self, name):
- self.name = name
- self.args = []
- self.result_type = ''
- self.result_refs = None
-
-
-class Refcounts(dict):
- @classmethod
- def fromfile(cls, filename):
- d = cls()
- fp = open(filename, 'r')
- try:
- for line in fp:
- line = line.strip()
- if line[:1] in ("", "#"):
- # blank lines and comments
- continue
- parts = line.split(":", 4)
- if len(parts) != 5:
- raise ValueError("Wrong field count in %r" % line)
- function, type, arg, refcount, comment = parts
- # Get the entry, creating it if needed:
- try:
- entry = d[function]
- except KeyError:
- entry = d[function] = RCEntry(function)
- if not refcount or refcount == "null":
- refcount = None
- else:
- refcount = int(refcount)
- # Update the entry with the new parameter or the result information.
- if arg:
- entry.args.append((arg, type, refcount))
- else:
- entry.result_type = type
- entry.result_refs = refcount
- finally:
- fp.close()
- return d
Modified: doctools/trunk/sphinx/util/__init__.py
==============================================================================
--- doctools/trunk/sphinx/util/__init__.py (original)
+++ doctools/trunk/sphinx/util/__init__.py Sun Jan 27 21:23:25 2008
@@ -51,15 +51,6 @@
raise
-def status_iterator(iterable, colorfunc=lambda x: x, stream=sys.stdout):
- """Print out each item before yielding it."""
- for item in iterable:
- print >>stream, colorfunc(item),
- stream.flush()
- yield item
- print >>stream
-
-
def get_matching_files(dirname, pattern, exclude=()):
"""Get all files matching a pattern in a directory, recursively."""
# dirname is a normalized absolute path.
Modified: doctools/trunk/sphinx/util/console.py
==============================================================================
--- doctools/trunk/sphinx/util/console.py (original)
+++ doctools/trunk/sphinx/util/console.py Sun Jan 27 21:23:25 2008
@@ -11,6 +11,30 @@
codes = {}
+def get_terminal_width():
+ """Borrowed from the py lib."""
+ try:
+ import os, termios, fcntl, struct
+ call = fcntl.ioctl(0, termios.TIOCGWINSZ, "\000"*8)
+ height, width = struct.unpack("hhhh", call)[:2]
+ terminal_width = width
+ except (SystemExit, KeyboardInterrupt):
+ raise
+ except:
+ # FALLBACK
+ terminal_width = int(os.environ.get('COLUMNS', 80))-1
+ return terminal_width
+
+_tw = get_terminal_width()
+
+def print_and_backspace(text, func):
+ if not codes:
+ # if no coloring, don't output fancy backspaces
+ func(text)
+ else:
+ func(text.ljust(_tw) + _tw * "\b")
+
+
def nocolor():
codes.clear()
@@ -31,8 +55,8 @@
'blink': '05m',
}
-for name, value in _attrs.items():
- codes[name] = '\x1b[' + value
+for _name, _value in _attrs.items():
+ codes[_name] = '\x1b[' + _value
_colors = [
('black', 'darkgray'),
@@ -49,5 +73,5 @@
codes[dark] = '\x1b[%im' % (i+30)
codes[light] = '\x1b[%i;01m' % (i+30)
-for name in codes:
- create_color_func(name)
+for _name in codes:
+ create_color_func(_name)
Modified: doctools/trunk/sphinx/util/smartypants.py
==============================================================================
--- doctools/trunk/sphinx/util/smartypants.py (original)
+++ doctools/trunk/sphinx/util/smartypants.py Sun Jan 27 21:23:25 2008
@@ -151,7 +151,7 @@
(\s | s\b)
""" % (close_class,), re.VERBOSE)
-def educateQuotes(str):
+def educateQuotes(s):
"""
Parameter: String.
@@ -163,35 +163,33 @@
# Special case if the very first character is a quote
# followed by punctuation at a non-word-break. Close the quotes by brute force:
- str = single_quote_start_re.sub("’", str)
- str = double_quote_start_re.sub("”", str)
+ s = single_quote_start_re.sub("’", s)
+ s = double_quote_start_re.sub("”", s)
# Special case for double sets of quotes, e.g.:
# <p>He said, "'Quoted' words in a larger quote."</p>
- str = double_quote_sets_re.sub("“‘", str)
- str = single_quote_sets_re.sub("‘“", str)
+ s = double_quote_sets_re.sub("“‘", s)
+ s = single_quote_sets_re.sub("‘“", s)
# Special case for decade abbreviations (the '80s):
- str = decade_abbr_re.sub("’", str)
+ s = decade_abbr_re.sub("’", s)
- str = opening_single_quotes_regex.sub(r"\1‘", str)
- str = closing_single_quotes_regex.sub(r"\1’", str)
- str = closing_single_quotes_regex_2.sub(r"\1’\2", str)
+ s = opening_single_quotes_regex.sub(r"\1‘", s)
+ s = closing_single_quotes_regex.sub(r"\1’", s)
+ s = closing_single_quotes_regex_2.sub(r"\1’\2", s)
# Any remaining single quotes should be opening ones:
- str = str.replace("'", "‘")
+ s = s.replace("'", "‘")
- str = opening_double_quotes_regex.sub(r"\1“", str)
- str = closing_double_quotes_regex.sub(r"”", str)
- str = closing_double_quotes_regex_2.sub(r"\1”", str)
+ s = opening_double_quotes_regex.sub(r"\1“", s)
+ s = closing_double_quotes_regex.sub(r"”", s)
+ s = closing_double_quotes_regex_2.sub(r"\1”", s)
# Any remaining quotes should be opening ones.
- str = str.replace('"', "“")
+ return s.replace('"', "“")
- return str
-
-def educateBackticks(str):
+def educateBackticks(s):
"""
Parameter: String.
Returns: The string, with ``backticks'' -style double quotes
@@ -199,10 +197,10 @@
Example input: ``Isn't this fun?''
Example output: “Isn't this fun?”
"""
- return str.replace("``", "“").replace("''", "”")
+ return s.replace("``", "“").replace("''", "”")
-def educateSingleBackticks(str):
+def educateSingleBackticks(s):
"""
Parameter: String.
Returns: The string, with `backticks' -style single quotes
@@ -211,10 +209,10 @@
Example input: `Isn't this fun?'
Example output: ‘Isn’t this fun?’
"""
- return str.replace('`', "‘").replace("'", "’")
+ return s.replace('`', "‘").replace("'", "’")
-def educateDashesOldSchool(str):
+def educateDashesOldSchool(s):
"""
Parameter: String.
@@ -222,10 +220,10 @@
an en-dash HTML entity, and each "---" translated to
an em-dash HTML entity.
"""
- return str.replace('---', "—").replace('--', "–")
+ return s.replace('---', "—").replace('--', "–")
-def educateDashesOldSchoolInverted(str):
+def educateDashesOldSchoolInverted(s):
"""
Parameter: String.
@@ -240,11 +238,11 @@
the shortcut should be shorter to type. (Thanks to Aaron
Swartz for the idea.)
"""
- return str.replace('---', "–").replace('--', "—")
+ return s.replace('---', "–").replace('--', "—")
-def educateEllipses(str):
+def educateEllipses(s):
"""
Parameter: String.
Returns: The string, with each instance of "..." translated to
@@ -253,7 +251,7 @@
Example input: Huh...?
Example output: Huh…?
"""
- return str.replace('...', "…").replace('. . .', "…")
+ return s.replace('...', "…").replace('. . .', "…")
__author__ = "Chad Miller <smartypantspy at chad.org>"
More information about the Python-checkins
mailing list