[Zope-Checkins] CVS: Zope/lib/python/docutils - __init__.py:1.2.10.10 core.py:1.2.10.8 examples.py:1.1.4.4 frontend.py:1.2.10.9 io.py:1.2.10.8 nodes.py:1.2.10.8 statemachine.py:1.2.10.8 utils.py:1.2.10.8

Andreas Jung andreas at andreas-jung.com
Sun Oct 9 10:44:14 EDT 2005


Update of /cvs-repository/Zope/lib/python/docutils
In directory cvs.zope.org:/tmp/cvs-serv26422

Modified Files:
      Tag: Zope-2_7-branch
	__init__.py core.py examples.py frontend.py io.py nodes.py 
	statemachine.py utils.py 
Log Message:
upgrade to docutils 0.3.9


=== Zope/lib/python/docutils/__init__.py 1.2.10.9 => 1.2.10.10 ===
--- Zope/lib/python/docutils/__init__.py:1.2.10.9	Fri Jan  7 08:26:01 2005
+++ Zope/lib/python/docutils/__init__.py	Sun Oct  9 10:43:43 2005
@@ -51,7 +51,7 @@
 
 __docformat__ = 'reStructuredText'
 
-__version__ = '0.3.7'
+__version__ = '0.3.9'
 """``major.minor.micro`` version number.  The micro number is bumped for API
 changes, for new functionality, and for interim project releases.  The minor
 number is bumped whenever there is a significant project release.  The major


=== Zope/lib/python/docutils/core.py 1.2.10.7 => 1.2.10.8 ===
--- Zope/lib/python/docutils/core.py:1.2.10.7	Fri Jan  7 08:26:01 2005
+++ Zope/lib/python/docutils/core.py	Sun Oct  9 10:43:43 2005
@@ -197,6 +197,7 @@
             self.writer.assemble_parts()
         except Exception, error:
             if self.settings.traceback: # propagate exceptions?
+                self.debugging_dumps(document)                
                 raise
             self.report_Exception(error)
             exit = 1
@@ -210,6 +211,8 @@
         return output
 
     def debugging_dumps(self, document):
+        if not document:
+            return
         if self.settings.dump_settings:
             print >>sys.stderr, '\n::: Runtime settings:'
             print >>sys.stderr, pprint.pformat(self.settings.__dict__)


=== Zope/lib/python/docutils/examples.py 1.1.4.3 => 1.1.4.4 ===
--- Zope/lib/python/docutils/examples.py:1.1.4.3	Fri Jan  7 08:26:02 2005
+++ Zope/lib/python/docutils/examples.py	Sun Oct  9 10:43:43 2005
@@ -7,12 +7,13 @@
 """
 This module contains practical examples of Docutils client code.
 
-Importing this module is not recommended; its contents are subject to change
-in future Docutils releases.  Instead, it is recommended that you copy and
-paste the parts you need into your own code, modifying as necessary.
+Importing this module from client code is not recommended; its contents are
+subject to change in future Docutils releases.  Instead, it is recommended
+that you copy and paste the parts you need into your own code, modifying as
+necessary.
 """
 
-from docutils import core
+from docutils import core, io
 
 
 def html_parts(input_string, source_path=None, destination_path=None,
@@ -72,3 +73,23 @@
     if output_encoding != 'unicode':
         fragment = fragment.encode(output_encoding)
     return fragment
+
+def internals(input_string, source_path=None, destination_path=None,
+              input_encoding='unicode'):
+    """
+    Return the document tree and publisher, for exploring Docutils internals.
+
+    Parameters: see `html_parts()`.
+    """
+    overrides = {'input_encoding': input_encoding}
+    output, pub = core.publish_programmatically(
+        source_class=io.StringInput, source=input_string,
+        source_path=source_path,
+        destination_class=io.NullOutput, destination=None,
+        destination_path=destination_path,
+        reader=None, reader_name='standalone',
+        parser=None, parser_name='restructuredtext',
+        writer=None, writer_name='null',
+        settings=None, settings_spec=None, settings_overrides=overrides,
+        config_section=None, enable_exit_status=None)
+    return pub.writer.document, pub


=== Zope/lib/python/docutils/frontend.py 1.2.10.8 => 1.2.10.9 ===
--- Zope/lib/python/docutils/frontend.py:1.2.10.8	Fri Jan  7 08:26:02 2005
+++ Zope/lib/python/docutils/frontend.py	Sun Oct  9 10:43:43 2005
@@ -124,6 +124,13 @@
                    None, sys.exc_info()[2])
     return value
 
+def validate_nonnegative_int(setting, value, option_parser,
+                             config_parser=None, config_section=None):
+    value = int(value)
+    if value < 0:
+        raise ValueError('negative value; must be positive or zero')
+    return value
+
 def validate_threshold(setting, value, option_parser,
                        config_parser=None, config_section=None):
     try:
@@ -333,10 +340,10 @@
                                'validator': validate_threshold}),
          ('Report all system messages, info-level and higher.  (Same as '
           '"--report=info".)',
-          ['--verbose', '-v'], {'action': 'store_const', 'const': 'info',
+          ['--verbose', '-v'], {'action': 'store_const', 'const': 1,
                                 'dest': 'report_level'}),
          ('Do not report any system messages.  (Same as "--report=none".)',
-          ['--quiet', '-q'], {'action': 'store_const', 'const': 'none',
+          ['--quiet', '-q'], {'action': 'store_const', 'const': 5,
                               'dest': 'report_level'}),
          ('Set the threshold (<level>) at or above which system messages are '
           'converted to exceptions, halting execution immediately by '
@@ -429,6 +436,9 @@
           ['--version', '-V'], {'action': 'version'}),
          ('Show this help message and exit.',
           ['--help', '-h'], {'action': 'help'}),
+         # Typically not useful for non-programmatical use.
+         (SUPPRESS_HELP, ['--id-prefix'], {'default': ''}),
+         (SUPPRESS_HELP, ['--auto-id-prefix'], {'default': 'id'}),
          # Hidden options, for development use only:
          (SUPPRESS_HELP, ['--dump-settings'], {'action': 'store_true'}),
          (SUPPRESS_HELP, ['--dump-internals'], {'action': 'store_true'}),


=== Zope/lib/python/docutils/io.py 1.2.10.7 => 1.2.10.8 ===
--- Zope/lib/python/docutils/io.py:1.2.10.7	Fri Jan  7 08:26:02 2005
+++ Zope/lib/python/docutils/io.py	Sun Oct  9 10:43:43 2005
@@ -70,32 +70,42 @@
         if (self.encoding and self.encoding.lower() == 'unicode'
             or isinstance(data, UnicodeType)):
             return data
-        encodings = [self.encoding, 'utf-8']
-        try:
-            encodings.append(locale.nl_langinfo(locale.CODESET))
-        except:
-            pass
-        try:
-            encodings.append(locale.getlocale()[1])
-        except:
-            pass
-        try:
-            encodings.append(locale.getdefaultlocale()[1])
-        except:
-            pass
-        encodings.append('latin-1')
+        encodings = [self.encoding]
+        if not self.encoding:
+            # Apply heuristics only if no encoding is explicitly given.
+            encodings.append('utf-8')
+            try:
+                encodings.append(locale.nl_langinfo(locale.CODESET))
+            except:
+                pass
+            try:
+                encodings.append(locale.getlocale()[1])
+            except:
+                pass
+            try:
+                encodings.append(locale.getdefaultlocale()[1])
+            except:
+                pass
+            encodings.append('latin-1')
+        error = None
+        error_details = ''
         for enc in encodings:
             if not enc:
                 continue
             try:
                 decoded = unicode(data, enc, self.error_handler)
                 self.successful_encoding = enc
-                return decoded
-            except (UnicodeError, LookupError):
+                # Return decoded, removing BOMs.
+                return decoded.replace(u'\ufeff', u'')
+            except (UnicodeError, LookupError), error:
                 pass
+        if error is not None:
+            error_details = '\n(%s: %s)' % (error.__class__.__name__, error)
         raise UnicodeError(
-            'Unable to decode input data.  Tried the following encodings: %s.'
-            % ', '.join([repr(enc) for enc in encodings if enc]))
+            'Unable to decode input data.  Tried the following encodings: '
+            '%s.%s'
+            % (', '.join([repr(enc) for enc in encodings if enc]),
+               error_details))
 
 
 class Output(TransformSpec):


=== Zope/lib/python/docutils/nodes.py 1.2.10.7 => 1.2.10.8 ===
--- Zope/lib/python/docutils/nodes.py:1.2.10.7	Fri Jan  7 08:26:02 2005
+++ Zope/lib/python/docutils/nodes.py	Sun Oct  9 10:43:43 2005
@@ -26,6 +26,8 @@
 import sys
 import os
 import re
+import copy
+import warnings
 import xml.dom.minidom
 from types import IntType, SliceType, StringType, UnicodeType, \
      TupleType, ListType
@@ -103,7 +105,7 @@
         or replaced occurs after the current node, the old node will
         still be traversed, and any new nodes will not.
 
-        Within ``visit`` methods (and ``depart`` methods for 
+        Within ``visit`` methods (and ``depart`` methods for
         `walkabout()`), `TreePruningException` subclasses may be raised
         (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
 
@@ -111,15 +113,15 @@
         ``visit`` implementation for each `Node` subclass encountered.
         """
         visitor.document.reporter.debug(
-            'calling dispatch_visit for %s' % self.__class__.__name__,
-            category='nodes.Node.walk')
+            'docutils.nodes.Node.walk calling dispatch_visit for %s'
+            % self.__class__.__name__)
         try:
             visitor.dispatch_visit(self)
         except (SkipChildren, SkipNode):
             return
         except SkipDeparture:           # not applicable; ignore
             pass
-        children = self.get_children()
+        children = self.children
         try:
             for child in children[:]:
                 child.walk(visitor)
@@ -138,8 +140,8 @@
         """
         call_depart = 1
         visitor.document.reporter.debug(
-            'calling dispatch_visit for %s' % self.__class__.__name__,
-            category='nodes.Node.walkabout')
+            'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
+            % self.__class__.__name__)
         try:
             try:
                 visitor.dispatch_visit(self)
@@ -147,7 +149,7 @@
                 return
             except SkipDeparture:
                 call_depart = 0
-            children = self.get_children()
+            children = self.children
             try:
                 for child in children[:]:
                     child.walkabout(visitor)
@@ -157,10 +159,83 @@
             pass
         if call_depart:
             visitor.document.reporter.debug(
-                'calling dispatch_departure for %s' % self.__class__.__name__,
-                category='nodes.Node.walkabout')
+                'docutils.nodes.Node.walkabout calling dispatch_departure '
+                'for %s' % self.__class__.__name__)
             visitor.dispatch_departure(self)
 
+    def traverse(self, condition=None,
+                 include_self=1, descend=1, siblings=0, ascend=0):
+        """
+        Return an iterable containing
+
+        * self (if include_self is true)
+        * all descendants in tree traversal order (if descend is true)
+        * all siblings (if siblings is true) and their descendants (if
+          also descend is true)
+        * the siblings of the parent (if ascend is true) and their
+          descendants (if also descend is true), and so on
+
+        If ascend is true, assume siblings to be true as well.
+
+        For example, given the following tree::
+
+            <paragraph>
+                <emphasis>      <--- emphasis.traverse() and
+                    <strong>    <--- strong.traverse() are called.
+                        Foo
+                    Bar
+                <reference name="Baz" refid="baz">
+                    Baz
+
+        Then list(emphasis.traverse()) equals ::
+
+            [<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
+
+        and list(strong.traverse(ascend=1)) equals ::
+
+            [<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
+        """
+        r = []
+        if ascend:
+            siblings=1
+        if include_self and (condition is None or condition(self)):
+            r.append(self)
+        if descend and len(self.children):
+            for child in self:
+                r.extend(child.traverse(
+                    include_self=1, descend=1, siblings=0, ascend=0,
+                    condition=condition))
+        if siblings or ascend:
+            node = self
+            while node.parent:
+                index = node.parent.index(node)
+                for sibling in node.parent[index+1:]:
+                    r.extend(sibling.traverse(include_self=1, descend=descend,
+                                              siblings=0, ascend=0,
+                                              condition=condition))
+                if not ascend:
+                    break
+                else:
+                    node = node.parent
+        return r
+
+
+    def next_node(self, condition=None,
+                  include_self=0, descend=1, siblings=0, ascend=0):
+        """
+        Return the first node in the iterable returned by traverse(),
+        or None if the iterable is empty.
+
+        Parameter list is the same as of traverse.  Note that
+        include_self defaults to 0, though.
+        """
+        iterable = self.traverse(condition=condition,
+                                 include_self=include_self, descend=descend,
+                                 siblings=siblings, ascend=ascend)
+        try:
+            return iterable[0]
+        except IndexError:
+            return None
 
 class Text(Node, UserString):
 
@@ -172,6 +247,9 @@
 
     tagname = '#text'
 
+    children = ()
+    """Text nodes have no children, and cannot have children."""
+
     def __init__(self, data, rawsource=''):
         UserString.__init__(self, data)
 
@@ -209,10 +287,6 @@
             result.append(indent + line + '\n')
         return ''.join(result)
 
-    def get_children(self):
-        """Text nodes have no children. Return []."""
-        return []
-
 
 class Element(Node):
 
@@ -225,6 +299,12 @@
 
         element['att'] = 'value'
 
+    There are two special attributes: 'ids' and 'names'.  Both are
+    lists of unique identifiers, and names serve as human interfaces
+    to IDs.  Names are case- and whitespace-normalized (see the
+    fully_normalize_name() function), and IDs conform to the regular
+    expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
+
     Elements also emulate lists for child nodes (element nodes and/or text
     nodes), indexing by integer.  To get the first child node, use::
 
@@ -245,6 +325,10 @@
     This is equivalent to ``element.extend([node1, node2])``.
     """
 
+    attr_defaults = {'ids': [], 'classes': [], 'names': [],
+                     'dupnames': [], 'backrefs': []}
+    """Default attributes."""
+
     tagname = None
     """The element generic identifier. If None, it is set as an instance
     attribute to the name of the class."""
@@ -261,7 +345,7 @@
 
         self.extend(children)           # maintain parent info
 
-        self.attributes = {}
+        self.attributes = copy.deepcopy(self.attr_defaults)
         """Dictionary of attribute {name: value}."""
 
         for att, value in attributes.items():
@@ -272,7 +356,7 @@
 
     def _dom_node(self, domroot):
         element = domroot.createElement(self.tagname)
-        for attribute, value in self.attributes.items():
+        for attribute, value in self.attlist():
             if isinstance(value, ListType):
                 value = ' '.join(['%s' % v for v in value])
             element.setAttribute(attribute, '%s' % value)
@@ -287,16 +371,16 @@
             if len(data) > 60:
                 data = data[:56] + ' ...'
                 break
-        if self.hasattr('name'):
+        if self['names']:
             return '<%s "%s": %s>' % (self.__class__.__name__,
-                                      self.attributes['name'], data)
+                                      '; '.join(self['names']), data)
         else:
             return '<%s: %s>' % (self.__class__.__name__, data)
 
     def shortrepr(self):
-        if self.hasattr('name'):
+        if self['names']:
             return '<%s "%s"...>' % (self.__class__.__name__,
-                                      self.attributes['name'])
+                                     '; '.join(self['names']))
         else:
             return '<%s...>' % self.tagname
 
@@ -382,20 +466,24 @@
     def __iadd__(self, other):
         """Append a node or a list of nodes to `self.children`."""
         if isinstance(other, Node):
-            self.setup_child(other)
-            self.children.append(other)
+            self.append(other)
         elif other is not None:
-            for node in other:
-                self.setup_child(node)
-            self.children.extend(other)
+            self.extend(other)
         return self
 
     def astext(self):
         return self.child_text_separator.join(
               [child.astext() for child in self.children])
 
+    def non_default_attributes(self):
+        atts = {}
+        for key, value in self.attributes.items():
+            if self.is_not_default(key):
+                atts[key] = value
+        return atts
+
     def attlist(self):
-        attlist = self.attributes.items()
+        attlist = self.non_default_attributes().items()
         attlist.sort()
         return attlist
 
@@ -420,8 +508,7 @@
 
     def extend(self, item):
         for node in item:
-            self.setup_child(node)
-        self.children.extend(item)
+            self.append(node)
 
     def insert(self, index, item):
         if isinstance(item, Node):
@@ -439,6 +526,15 @@
     def index(self, item):
         return self.children.index(item)
 
+    def is_not_default(self, key):
+        try:
+            return self[key] != self.attr_defaults[key]
+        except KeyError:
+            return 1
+
+    def clear(self):
+        self.children = []
+
     def replace(self, old, new):
         """Replace one child `Node` with another child or children."""
         index = self.index(old)
@@ -482,12 +578,10 @@
         if not isinstance(childclass, TupleType):
             childclass = (childclass,)
         for index in range(start, min(len(self), end)):
-            match = 0
             for c in childclass:
                 if isinstance(self.children[index], c):
-                    match = 1
                     break
-            if not match:
+            else:
                 return index
         return None
 
@@ -496,17 +590,33 @@
                        [child.pformat(indent, level+1)
                         for child in self.children])
 
-    def get_children(self):
-        """Return this element's children."""
-        return self.children
-
     def copy(self):
         return self.__class__(**self.attributes)
 
     def set_class(self, name):
-        """Add a new name to the "class" attribute."""
-        self.attributes['class'] = (self.attributes.get('class', '') + ' '
-                                    + name.lower()).strip()
+        """Add a new class to the "classes" attribute."""
+        warnings.warn('docutils.nodes.Element.set_class deprecated; '
+                      "append to Element['classes'] list attribute directly",
+                      DeprecationWarning, stacklevel=2)
+        assert ' ' not in name
+        self['classes'].append(name.lower())
+
+    def note_referenced_by(self, name=None, id=None):
+        """Note that this Element has been referenced by its name
+        `name` or id `id`."""
+        self.referenced = 1
+        # Element.expect_referenced_by_* dictionaries map names or ids
+        # to nodes whose ``referenced`` attribute is set to true as
+        # soon as this node is referenced by the given name or id.
+        # Needed for target propagation.
+        by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
+        by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
+        if by_name:
+            assert name is not None
+            by_name.referenced = 1
+        if by_id:
+            assert id is not None
+            by_id.referenced = 1
 
 
 class TextElement(Element):
@@ -514,7 +624,7 @@
     """
     An element which directly contains text.
 
-    Its children are all `Text` or `TextElement` subclass nodes.  You can
+    Its children are all `Text` or `Inline` subclass nodes.  You can
     check whether an element's context is inline simply by checking whether
     its immediate parent is a `TextElement` instance (including subclasses).
     This is handy for nodes like `image` that can appear both inline and as
@@ -557,7 +667,7 @@
 class BackLinkable:
 
     def add_backref(self, refid):
-        self.setdefault('backrefs', []).append(refid)
+        self['backrefs'].append(refid)
 
 
 # ====================
@@ -568,15 +678,12 @@
 
 class Titular: pass
 
-class PreDecorative:
-    """Category of Node which may occur before Decorative Nodes."""
-
-class PreBibliographic(PreDecorative):
+class PreBibliographic:
     """Category of Node which may occur before Bibliographic Nodes."""
 
-class Bibliographic(PreDecorative): pass
+class Bibliographic: pass
 
-class Decorative: pass
+class Decorative(PreBibliographic): pass
 
 class Structural: pass
 
@@ -584,7 +691,8 @@
 
 class General(Body): pass
 
-class Sequential(Body): pass
+class Sequential(Body):
+    """List-like elements."""
 
 class Admonition(Body): pass
 
@@ -604,9 +712,6 @@
 
     referenced = 0
 
-    indirect_reference_name = None
-    """Holds the whitespace_normalized_name (contains mixed case) of a target"""
-
 class Labeled:
     """Contains a `label` as its first element."""
 
@@ -717,6 +822,9 @@
         self.transformer = docutils.transforms.Transformer(self)
         """Storage for transforms to be applied to this document."""
 
+        self.decoration = None
+        """Document's `decoration` node."""
+
         self.document = self
 
     def asdom(self, dom=xml.dom.minidom):
@@ -726,21 +834,23 @@
         return domroot
 
     def set_id(self, node, msgnode=None):
-        if node.has_key('id'):
-            id = node['id']
+        for id in node['ids']:
             if self.ids.has_key(id) and self.ids[id] is not node:
                 msg = self.reporter.severe('Duplicate ID: "%s".' % id)
                 if msgnode != None:
                     msgnode += msg
-        else:
-            if node.has_key('name'):
-                id = make_id(node['name'])
+        if not node['ids']:
+            for name in node['names']:
+                id = self.settings.id_prefix + make_id(name)
+                if id and not self.ids.has_key(id):
+                    break
             else:
                 id = ''
-            while not id or self.ids.has_key(id):
-                id = 'id%s' % self.id_start
-                self.id_start += 1
-            node['id'] = id
+                while not id or self.ids.has_key(id):
+                    id = (self.settings.id_prefix +
+                          self.settings.auto_id_prefix + str(self.id_start))
+                    self.id_start += 1
+            node['ids'].append(id)
         self.ids[id] = node
         return id
 
@@ -775,8 +885,7 @@
            both old and new targets are external and refer to identical URIs.
            The new target is invalidated regardless.
         """
-        if node.has_key('name'):
-            name = node['name']
+        for name in node['names']:
             if self.nameids.has_key(name):
                 self.set_duplicate_name_id(node, id, name, msgnode, explicit)
             else:
@@ -794,30 +903,30 @@
                     old_node = self.ids[old_id]
                     if node.has_key('refuri'):
                         refuri = node['refuri']
-                        if old_node.has_key('name') \
+                        if old_node['names'] \
                                and old_node.has_key('refuri') \
                                and old_node['refuri'] == refuri:
                             level = 1   # just inform if refuri's identical
                     if level > 1:
-                        dupname(old_node)
+                        dupname(old_node, name)
                         self.nameids[name] = None
                 msg = self.reporter.system_message(
                     level, 'Duplicate explicit target name: "%s".' % name,
                     backrefs=[id], base_node=node)
                 if msgnode != None:
                     msgnode += msg
-                dupname(node)
+                dupname(node, name)
             else:
                 self.nameids[name] = id
                 if old_id is not None:
                     old_node = self.ids[old_id]
-                    dupname(old_node)
+                    dupname(old_node, name)
         else:
             if old_id is not None and not old_explicit:
                 self.nameids[name] = None
                 old_node = self.ids[old_id]
-                dupname(old_node)
-            dupname(node)
+                dupname(old_node, name)
+            dupname(node, name)
         if not explicit or (not old_explicit and old_id is not None):
             msg = self.reporter.info(
                 'Duplicate implicit target name: "%s".' % name,
@@ -851,7 +960,7 @@
 
     def note_indirect_target(self, target):
         self.indirect_targets.append(target)
-        if target.has_key('name'):
+        if target['names']:
             self.note_refname(target)
 
     def note_anonymous_target(self, target):
@@ -895,7 +1004,8 @@
         self.note_refname(ref)
 
     def note_substitution_def(self, subdef, def_name, msgnode=None):
-        name = subdef['name'] = whitespace_normalize_name(def_name)
+        name = whitespace_normalize_name(def_name)
+        subdef['names'].append(name)
         if self.substitution_defs.has_key(name):
             msg = self.reporter.error(
                   'Duplicate substitution definition name: "%s".' % name,
@@ -903,7 +1013,7 @@
             if msgnode != None:
                 msgnode += msg
             oldnode = self.substitution_defs[name]
-            dupname(oldnode)
+            dupname(oldnode, name)
         # keep only the last definition:
         self.substitution_defs[name] = subdef
         # case-insensitive mapping:
@@ -933,6 +1043,16 @@
         return self.__class__(self.settings, self.reporter,
                               **self.attributes)
 
+    def get_decoration(self):
+        if not self.decoration:
+            self.decoration = decoration()
+            index = self.first_child_not_matching_class(Titular)
+            if index is None:
+                self.append(self.decoration)
+            else:
+                self.insert(index, self.decoration)
+        return self.decoration
+
 
 # ================
 #  Title Elements
@@ -964,7 +1084,19 @@
 #  Decorative Elements
 # =====================
 
-class decoration(Decorative, Element): pass
+class decoration(Decorative, Element):
+
+    def get_header(self):
+        if not len(self.children) or not isinstance(self.children[0], header):
+            self.insert(0, header())
+        return self.children[0]
+
+    def get_footer(self):
+        if not len(self.children) or not isinstance(self.children[-1], footer):
+            self.append(footer())
+        return self.children[-1]
+
+
 class header(Decorative, Element): pass
 class footer(Decorative, Element): pass
 
@@ -1061,7 +1193,7 @@
 class line_block(General, Element): pass
 
 
-class line(General, TextElement):
+class line(Part, TextElement):
 
     indent = None
 
@@ -1081,8 +1213,8 @@
 class comment(Special, Invisible, FixedTextElement): pass
 class substitution_definition(Special, Invisible, TextElement): pass
 class target(Special, Invisible, Inline, TextElement, Targetable): pass
-class footnote(General, Element, Labeled, BackLinkable): pass
-class citation(General, Element, Labeled, BackLinkable): pass
+class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
+class citation(General, BackLinkable, Element, Labeled, Targetable): pass
 class label(Part, TextElement): pass
 class figure(General, Element): pass
 class caption(Part, TextElement): pass
@@ -1096,7 +1228,7 @@
 class entry(Part, Element): pass
 
 
-class system_message(Special, PreBibliographic, Element, BackLinkable):
+class system_message(Special, BackLinkable, PreBibliographic, Element):
 
     def __init__(self, message=None, *children, **attributes):
         if message:
@@ -1210,7 +1342,7 @@
 class subscript(Inline, TextElement): pass
 
 
-class image(General, Inline, TextElement):
+class image(General, Inline, Element):
 
     def astext(self):
         return self.get('alt', '')
@@ -1306,8 +1438,8 @@
         node_name = node.__class__.__name__
         method = getattr(self, 'visit_' + node_name, self.unknown_visit)
         self.document.reporter.debug(
-            'calling %s for %s' % (method.__name__, node_name),
-            category='nodes.NodeVisitor.dispatch_visit')
+            'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
+            % (method.__name__, node_name))
         return method(node)
 
     def dispatch_departure(self, node):
@@ -1319,8 +1451,8 @@
         node_name = node.__class__.__name__
         method = getattr(self, 'depart_' + node_name, self.unknown_departure)
         self.document.reporter.debug(
-            'calling %s for %s' % (method.__name__, node_name),
-            category='nodes.NodeVisitor.dispatch_departure')
+            'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
+            % (method.__name__, node_name))
         return method(node)
 
     def unknown_visit(self, node):
@@ -1357,6 +1489,7 @@
     subclasses), subclass `NodeVisitor` instead.
     """
 
+
 class GenericNodeVisitor(NodeVisitor):
 
     """
@@ -1398,10 +1531,11 @@
         setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
         setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
         setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
-        setattr(SparseNodeVisitor, 'depart' + _name, _nop)
+        setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
 
 _add_node_class_names(node_class_names)
 
+
 class TreeCopyVisitor(GenericNodeVisitor):
 
     """
@@ -1534,9 +1668,12 @@
 _non_id_chars = re.compile('[^a-z0-9]+')
 _non_id_at_ends = re.compile('^[-0-9]+|-+$')
 
-def dupname(node):
-    node['dupname'] = node['name']
-    del node['name']
+def dupname(node, name):
+    node['dupnames'].append(name)
+    node['names'].remove(name)
+    # Assume that this method is referenced, even though it isn't; we
+    # don't want to throw unnecessary system_messages.
+    node.referenced = 1
 
 def fully_normalize_name(name):
     """Return a case- and whitespace-normalized name."""


=== Zope/lib/python/docutils/statemachine.py 1.2.10.7 => 1.2.10.8 ===


=== Zope/lib/python/docutils/utils.py 1.2.10.7 => 1.2.10.8 ===
--- Zope/lib/python/docutils/utils.py:1.2.10.7	Fri Jan  7 08:26:02 2005
+++ Zope/lib/python/docutils/utils.py	Sun Oct  9 10:43:43 2005
@@ -13,6 +13,7 @@
 import sys
 import os
 import os.path
+import warnings
 from types import StringType, UnicodeType
 from docutils import ApplicationError, DataError
 from docutils import frontend, nodes
@@ -39,27 +40,14 @@
     There is typically one Reporter object per process.  A Reporter object is
     instantiated with thresholds for reporting (generating warnings) and
     halting processing (raising exceptions), a switch to turn debug output on
-    or off, and an I/O stream for warnings.  These are stored in the default
-    reporting category, '' (zero-length string).
+    or off, and an I/O stream for warnings.  These are stored as instance
+    attributes.
 
-    Multiple reporting categories [#]_ may be set, each with its own reporting
-    and halting thresholds, debugging switch, and warning stream
-    (collectively a `ConditionSet`).  Categories are hierarchical dotted-name
-    strings that look like attribute references: 'spam', 'spam.eggs',
-    'neeeow.wum.ping'.  The 'spam' category is the ancestor of
-    'spam.bacon.eggs'.  Unset categories inherit stored conditions from their
-    closest ancestor category that has been set.
-
-    When a system message is generated, the stored conditions from its
-    category (or ancestor if unset) are retrieved.  The system message level
-    is compared to the thresholds stored in the category, and a warning or
-    error is generated as appropriate.  Debug messages are produced iff the
-    stored debug switch is on.  Message output is sent to the stored warning
-    stream if not set to ''.
-
-    The default category is '' (empty string).  By convention, Writers should
-    retrieve reporting conditions from the 'writer' category (which, unless
-    explicitly set, defaults to the conditions of the default category).
+    When a system message is generated, its level is compared to the stored
+    thresholds, and a warning or error is generated as appropriate.  Debug
+    messages are produced iff the stored debug switch is on, independently of
+    other thresholds.  Message output is sent to the stored warning stream if
+    not set to ''.
 
     The Reporter class also employs a modified form of the "Observer" pattern
     [GoF95]_ to track system messages generated.  The `attach_observer` method
@@ -67,9 +55,6 @@
     accepts system messages.  The observer can be removed with
     `detach_observer`, and another added in its place.
 
-    .. [#] The concept of "categories" was inspired by the log4j project:
-       http://jakarta.apache.org/log4j/.
-
     .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
        Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
        1995.
@@ -81,10 +66,7 @@
     def __init__(self, source, report_level, halt_level, stream=None,
                  debug=0, encoding='ascii', error_handler='replace'):
         """
-        Initialize the `ConditionSet` forthe `Reporter`'s default category.
-
         :Parameters:
-
             - `source`: The path to or description of the source data.
             - `report_level`: The level at or above which warning output will
               be sent to `stream`.
@@ -101,6 +83,23 @@
         self.source = source
         """The path to or description of the source data."""
 
+        self.encoding = encoding
+        """The character encoding for the stderr output."""
+
+        self.error_handler = error_handler
+        """The character encoding error handler."""
+
+        self.debug_flag = debug
+        """Show debug (level=0) system messages?"""
+
+        self.report_level = report_level
+        """The level at or above which warning output will be sent
+        to `self.stream`."""
+
+        self.halt_level = halt_level
+        """The level at or above which `SystemMessage` exceptions
+        will be raised, halting execution."""
+
         if stream is None:
             stream = sys.stderr
         elif type(stream) in (StringType, UnicodeType):
@@ -111,15 +110,8 @@
                 elif type(stream) == UnicodeType:
                     stream = open(stream.encode(), 'w')
 
-        self.encoding = encoding
-        """The character encoding for the stderr output."""
-
-        self.error_handler = error_handler
-        """The character encoding error handler."""
-
-        self.categories = {'': ConditionSet(debug, report_level, halt_level,
-                                            stream)}
-        """Mapping of category names to conditions. Default category is ''."""
+        self.stream = stream
+        """Where warning output is sent."""
 
         self.observers = []
         """List of bound methods or functions to call with each system_message
@@ -130,23 +122,15 @@
 
     def set_conditions(self, category, report_level, halt_level,
                        stream=None, debug=0):
+        warnings.warn('docutils.utils.Reporter.set_conditions deprecated; '
+                      'set attributes via configuration settings or directly',
+                      DeprecationWarning, stacklevel=2)
+        self.report_level = report_level
+        self.halt_level = halt_level
         if stream is None:
             stream = sys.stderr
-        self.categories[category] = ConditionSet(debug, report_level,
-                                                 halt_level, stream)
-
-    def unset_conditions(self, category):
-        if category and self.categories.has_key(category):
-            del self.categories[category]
-
-    __delitem__ = unset_conditions
-
-    def get_conditions(self, category):
-        while not self.categories.has_key(category):
-            category = category[:category.rfind('.') + 1][:-1]
-        return self.categories[category]
-
-    __getitem__ = get_conditions
+        self.stream = stream
+        self.debug = debug
 
     def attach_observer(self, observer):
         """
@@ -169,9 +153,6 @@
         Raise an exception or generate a warning if appropriate.
         """
         attributes = kwargs.copy()
-        category = kwargs.get('category', '')
-        if kwargs.has_key('category'):
-            del attributes['category']
         if kwargs.has_key('base_node'):
             source, line = get_source_line(kwargs['base_node'])
             del attributes['base_node']
@@ -183,16 +164,13 @@
         msg = nodes.system_message(message, level=level,
                                    type=self.levels[level],
                                    *children, **attributes)
-        debug, report_level, halt_level, stream = self[category].astuple()
-        if (level >= report_level or debug and level == 0) and stream:
+        if self.stream and (level >= self.report_level
+                            or self.debug_flag and level == 0):
             msgtext = msg.astext().encode(self.encoding, self.error_handler)
-            if category:
-                print >>stream, msgtext, '[%s]' % category
-            else:
-                print >>stream, msgtext
-        if level >= halt_level:
+            print >>self.stream, msgtext
+        if level >= self.halt_level:
             raise SystemMessage(msg, level)
-        if level > 0 or debug:
+        if level > 0 or self.debug_flag:
             self.notify_observers(msg)
         self.max_level = max(level, self.max_level)
         return msg
@@ -203,7 +181,8 @@
         effect on the processing. Level-0 system messages are handled
         separately from the others.
         """
-        return self.system_message(0, *args, **kwargs)
+        if self.debug_flag:
+            return self.system_message(0, *args, **kwargs)
 
     def info(self, *args, **kwargs):
         """
@@ -235,25 +214,6 @@
         return self.system_message(4, *args, **kwargs)
 
 
-class ConditionSet:
-
-    """
-    A set of two thresholds (`report_level` & `halt_level`), a switch
-    (`debug`), and an I/O stream (`stream`), corresponding to one `Reporter`
-    category.
-    """
-
-    def __init__(self, debug, report_level, halt_level, stream):
-        self.debug = debug
-        self.report_level = report_level
-        self.halt_level = halt_level
-        self.stream = stream
-
-    def astuple(self):
-        return (self.debug, self.report_level, self.halt_level,
-                self.stream)
-
-
 class ExtensionOptionError(DataError): pass
 class BadOptionError(ExtensionOptionError): pass
 class BadOptionDataError(ExtensionOptionError): pass
@@ -346,7 +306,7 @@
             options[name] = convertor(value)
         except (ValueError, TypeError), detail:
             raise detail.__class__('(option: "%s"; value: %r)\n%s'
-                                   % (name, value, detail))
+                                   % (name, value, ' '.join(detail.args)))
     return options
 
 



More information about the Zope-Checkins mailing list