[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("&#8217;", str)
-    str = double_quote_start_re.sub("&#8221;", str)
+    s = single_quote_start_re.sub("&#8217;", s)
+    s = double_quote_start_re.sub("&#8221;", 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("&#8220;&#8216;", str)
-    str = single_quote_sets_re.sub("&#8216;&#8220;", str)
+    s = double_quote_sets_re.sub("&#8220;&#8216;", s)
+    s = single_quote_sets_re.sub("&#8216;&#8220;", s)
 
     # Special case for decade abbreviations (the '80s):
-    str = decade_abbr_re.sub("&#8217;", str)
+    s = decade_abbr_re.sub("&#8217;", s)
 
-    str = opening_single_quotes_regex.sub(r"\1&#8216;", str)
-    str = closing_single_quotes_regex.sub(r"\1&#8217;", str)
-    str = closing_single_quotes_regex_2.sub(r"\1&#8217;\2", str)
+    s = opening_single_quotes_regex.sub(r"\1&#8216;", s)
+    s = closing_single_quotes_regex.sub(r"\1&#8217;", s)
+    s = closing_single_quotes_regex_2.sub(r"\1&#8217;\2", s)
 
     # Any remaining single quotes should be opening ones:
-    str = str.replace("'", "&#8216;")
+    s = s.replace("'", "&#8216;")
 
-    str = opening_double_quotes_regex.sub(r"\1&#8220;", str)
-    str = closing_double_quotes_regex.sub(r"&#8221;", str)
-    str = closing_double_quotes_regex_2.sub(r"\1&#8221;", str)
+    s = opening_double_quotes_regex.sub(r"\1&#8220;", s)
+    s = closing_double_quotes_regex.sub(r"&#8221;", s)
+    s = closing_double_quotes_regex_2.sub(r"\1&#8221;", s)
 
     # Any remaining quotes should be opening ones.
-    str = str.replace('"', "&#8220;")
+    return s.replace('"', "&#8220;")
 
-    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: &#8220;Isn't this fun?&#8221;
     """
-    return str.replace("``", "&#8220;").replace("''", "&#8221;")
+    return s.replace("``", "&#8220;").replace("''", "&#8221;")
 
 
-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: &#8216;Isn&#8217;t this fun?&#8217;
     """
-    return str.replace('`', "&#8216;").replace("'", "&#8217;")
+    return s.replace('`', "&#8216;").replace("'", "&#8217;")
 
 
-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('---', "&#8212;").replace('--', "&#8211;")
+    return s.replace('---', "&#8212;").replace('--', "&#8211;")
 
 
-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('---', "&#8211;").replace('--', "&#8212;")
+    return s.replace('---', "&#8211;").replace('--', "&#8212;")
 
 
 
-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&#8230;?
     """
-    return str.replace('...', "&#8230;").replace('. . .', "&#8230;")
+    return s.replace('...', "&#8230;").replace('. . .', "&#8230;")
 
 
 __author__ = "Chad Miller <smartypantspy at chad.org>"


More information about the Python-checkins mailing list