[Zope-Checkins] CVS: Zope/lib/python/docutils/parsers/rst/directives - __init__.py:1.2.10.8 admonitions.py:1.2.10.6 body.py:1.2.10.7 html.py:1.2.10.6 images.py:1.2.10.7 misc.py:1.2.10.7 parts.py:1.2.10.6 references.py:1.2.10.6 tables.py:1.1.2.3

Andreas Jung andreas at andreas-jung.com
Fri Jan 7 08:26:35 EST 2005


Update of /cvs-repository/Zope/lib/python/docutils/parsers/rst/directives
In directory cvs.zope.org:/tmp/cvs-serv7568/lib/python/docutils/parsers/rst/directives

Added Files:
      Tag: Zope-2_7-branch
	__init__.py admonitions.py body.py html.py images.py misc.py 
	parts.py references.py tables.py 
Log Message:
- updated Docutils to V 0.3.7
- moved Docutils back to lib/python
- removed sitecustomize.py



=== Zope/lib/python/docutils/parsers/rst/directives/__init__.py 1.2.10.7 => 1.2.10.8 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/__init__.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,381 @@
+# Author: David Goodger
+# Contact: goodger at python.org
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+This package contains directive implementation modules.
+
+The interface for directive functions is as follows::
+
+    def directive_fn(name, arguments, options, content, lineno,
+                     content_offset, block_text, state, state_machine):
+        code...
+
+    # Set function attributes:
+    directive_fn.arguments = ...
+    directive_fn.options = ...
+    direcitve_fn.content = ...
+
+Parameters:
+
+- ``name`` is the directive type or name (string).
+
+- ``arguments`` is a list of positional arguments (strings).
+
+- ``options`` is a dictionary mapping option names (strings) to values (type
+  depends on option conversion functions; see below).
+
+- ``content`` is a list of strings, the directive content.
+
+- ``lineno`` is the line number of the first line of the directive.
+
+- ``content_offset`` is the line offset of the first line of the content from
+  the beginning of the current input.  Used when initiating a nested parse.
+
+- ``block_text`` is a string containing the entire directive.  Include it as
+  the content of a literal block in a system message if there is a problem.
+
+- ``state`` is the state which called the directive function.
+
+- ``state_machine`` is the state machine which controls the state which called
+  the directive function.
+
+Function attributes, interpreted by the directive parser (which calls the
+directive function):
+
+- ``arguments``: A 3-tuple specifying the expected positional arguments, or
+  ``None`` if the directive has no arguments.  The 3 items in the tuple are
+  ``(required, optional, whitespace OK in last argument)``:
+
+  1. The number of required arguments.
+  2. The number of optional arguments.
+  3. A boolean, indicating if the final argument may contain whitespace.
+
+  Arguments are normally single whitespace-separated words.  The final
+  argument may contain whitespace if the third item in the argument spec tuple
+  is 1/True.  If the form of the arguments is more complex, specify only one
+  argument (either required or optional) and indicate that final whitespace is
+  OK; the client code must do any context-sensitive parsing.
+
+- ``options``: A dictionary, mapping known option names to conversion
+  functions such as `int` or `float`.  ``None`` or an empty dict implies no
+  options to parse.  Several directive option conversion functions are defined
+  in this module.
+
+  Option conversion functions take a single parameter, the option argument (a
+  string or ``None``), validate it and/or convert it to the appropriate form.
+  Conversion functions may raise ``ValueError`` and ``TypeError`` exceptions.
+
+- ``content``: A boolean; true if content is allowed.  Client code must handle
+  the case where content is required but not supplied (an empty content list
+  will be supplied).
+
+Directive functions return a list of nodes which will be inserted into the
+document tree at the point where the directive was encountered (can be an
+empty list).
+
+See `Creating reStructuredText Directives`_ for more information.
+
+.. _Creating reStructuredText Directives:
+   http://docutils.sourceforge.net/docs/howto/rst-directives.html
+"""
+
+__docformat__ = 'reStructuredText'
+
+import re
+import codecs
+from docutils import nodes
+from docutils.parsers.rst.languages import en as _fallback_language_module
+
+
+_directive_registry = {
+      'attention': ('admonitions', 'attention'),
+      'caution': ('admonitions', 'caution'),
+      'danger': ('admonitions', 'danger'),
+      'error': ('admonitions', 'error'),
+      'important': ('admonitions', 'important'),
+      'note': ('admonitions', 'note'),
+      'tip': ('admonitions', 'tip'),
+      'hint': ('admonitions', 'hint'),
+      'warning': ('admonitions', 'warning'),
+      'admonition': ('admonitions', 'admonition'),
+      'sidebar': ('body', 'sidebar'),
+      'topic': ('body', 'topic'),
+      'line-block': ('body', 'line_block'),
+      'parsed-literal': ('body', 'parsed_literal'),
+      'rubric': ('body', 'rubric'),
+      'epigraph': ('body', 'epigraph'),
+      'highlights': ('body', 'highlights'),
+      'pull-quote': ('body', 'pull_quote'),
+      'compound': ('body', 'compound'),
+      #'questions': ('body', 'question_list'),
+      'table': ('tables', 'table'),
+      'csv-table': ('tables', 'csv_table'),
+      'image': ('images', 'image'),
+      'figure': ('images', 'figure'),
+      'contents': ('parts', 'contents'),
+      'sectnum': ('parts', 'sectnum'),
+      #'footnotes': ('parts', 'footnotes'),
+      #'citations': ('parts', 'citations'),
+      'target-notes': ('references', 'target_notes'),
+      'meta': ('html', 'meta'),
+      #'imagemap': ('html', 'imagemap'),
+      'raw': ('misc', 'raw'),
+      'include': ('misc', 'include'),
+      'replace': ('misc', 'replace'),
+      'unicode': ('misc', 'unicode_directive'),
+      'class': ('misc', 'class_directive'),
+      'role': ('misc', 'role'),
+      'restructuredtext-test-directive': ('misc', 'directive_test_function'),}
+"""Mapping of directive name to (module name, function name).  The directive
+name is canonical & must be lowercase.  Language-dependent names are defined
+in the ``language`` subpackage."""
+
+_modules = {}
+"""Cache of imported directive modules."""
+
+_directives = {}
+"""Cache of imported directive functions."""
+
+def directive(directive_name, language_module, document):
+    """
+    Locate and return a directive function from its language-dependent name.
+    If not found in the current language, check English.  Return None if the
+    named directive cannot be found.
+    """
+    normname = directive_name.lower()
+    messages = []
+    msg_text = []
+    if _directives.has_key(normname):
+        return _directives[normname], messages
+    canonicalname = None
+    try:
+        canonicalname = language_module.directives[normname]
+    except AttributeError, error:
+        msg_text.append('Problem retrieving directive entry from language '
+                        'module %r: %s.' % (language_module, error))
+    except KeyError:
+        msg_text.append('No directive entry for "%s" in module "%s".'
+                        % (directive_name, language_module.__name__))
+    if not canonicalname:
+        try:
+            canonicalname = _fallback_language_module.directives[normname]
+            msg_text.append('Using English fallback for directive "%s".'
+                            % directive_name)
+        except KeyError:
+            msg_text.append('Trying "%s" as canonical directive name.'
+                            % directive_name)
+            # The canonical name should be an English name, but just in case:
+            canonicalname = normname
+    if msg_text:
+        message = document.reporter.info(
+            '\n'.join(msg_text), line=document.current_line)
+        messages.append(message)
+    try:
+        modulename, functionname = _directive_registry[canonicalname]
+    except KeyError:
+        messages.append(document.reporter.error(
+            'Directive "%s" not registered (canonical name "%s").'
+            % (directive_name, canonicalname), line=document.current_line))
+        return None, messages
+    if _modules.has_key(modulename):
+        module = _modules[modulename]
+    else:
+        try:
+            module = __import__(modulename, globals(), locals())
+        except ImportError, detail:
+            messages.append(document.reporter.error(
+                'Error importing directive module "%s" (directive "%s"):\n%s'
+                % (modulename, directive_name, detail),
+                line=document.current_line))
+            return None, messages
+    try:
+        function = getattr(module, functionname)
+        _directives[normname] = function
+    except AttributeError:
+        messages.append(document.reporter.error(
+            'No function "%s" in module "%s" (directive "%s").'
+            % (functionname, modulename, directive_name),
+            line=document.current_line))
+        return None, messages
+    return function, messages
+
+def register_directive(name, directive_function):
+    """
+    Register a nonstandard application-defined directive function.
+    Language lookups are not needed for such functions.
+    """
+    _directives[name] = directive_function
+
+def flag(argument):
+    """
+    Check for a valid flag option (no argument) and return ``None``.
+    (Directive option conversion function.)
+
+    Raise ``ValueError`` if an argument is found.
+    """
+    if argument and argument.strip():
+        raise ValueError('no argument is allowed; "%s" supplied' % argument)
+    else:
+        return None
+
+def unchanged_required(argument):
+    """
+    Return the argument text, unchanged.
+    (Directive option conversion function.)
+
+    Raise ``ValueError`` if no argument is found.
+    """
+    if argument is None:
+        raise ValueError('argument required but none supplied')
+    else:
+        return argument  # unchanged!
+
+def unchanged(argument):
+    """
+    Return the argument text, unchanged.
+    (Directive option conversion function.)
+
+    No argument implies empty string ("").
+    """
+    if argument is None:
+        return u''
+    else:
+        return argument  # unchanged!
+
+def path(argument):
+    """
+    Return the path argument unwrapped (with newlines removed).
+    (Directive option conversion function.)
+
+    Raise ``ValueError`` if no argument is found or if the path contains
+    internal whitespace.
+    """
+    if argument is None:
+        raise ValueError('argument required but none supplied')
+    else:
+        path = ''.join([s.strip() for s in argument.splitlines()])
+        if path.find(' ') == -1:
+            return path
+        else:
+            raise ValueError('path contains whitespace')
+
+def nonnegative_int(argument):
+    """
+    Check for a nonnegative integer argument; raise ``ValueError`` if not.
+    (Directive option conversion function.)
+    """
+    value = int(argument)
+    if value < 0:
+        raise ValueError('negative value; must be positive or zero')
+    return value
+
+def class_option(argument):
+    """
+    Convert the argument into an ID-compatible string and return it.
+    (Directive option conversion function.)
+
+    Raise ``ValueError`` if no argument is found.
+    """
+    if argument is None:
+        raise ValueError('argument required but none supplied')
+    names = argument.split()
+    class_names = []
+    for name in names:
+        class_name = nodes.make_id(name)
+        if not class_name:
+            raise ValueError('cannot make "%s" into a class name' % name)
+        class_names.append(class_name)
+    return ' '.join(class_names)
+
+unicode_pattern = re.compile(
+    r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE)
+
+def unicode_code(code):
+    r"""
+    Convert a Unicode character code to a Unicode character.
+
+    Codes may be decimal numbers, hexadecimal numbers (prefixed by ``0x``,
+    ``x``, ``\x``, ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style
+    numeric character entities (e.g. ``&#x262E;``).  Other text remains as-is.
+    """
+    try:
+        if code.isdigit():                  # decimal number
+            return unichr(int(code))
+        else:
+            match = unicode_pattern.match(code)
+            if match:                       # hex number
+                value = match.group(1) or match.group(2)
+                return unichr(int(value, 16))
+            else:                           # other text
+                return code
+    except OverflowError, detail:
+        raise ValueError('code too large (%s)' % detail)
+
+def single_char_or_unicode(argument):
+    char = unicode_code(argument)
+    if len(char) > 1:
+        raise ValueError('%r invalid; must be a single character or '
+                         'a Unicode code' % char)
+    return char
+
+def single_char_or_whitespace_or_unicode(argument):
+    if argument == 'tab':
+        char = '\t'
+    elif argument == 'space':
+        char = ' '
+    else:
+        char = single_char_or_unicode(argument)
+    return char
+
+def positive_int(argument):
+    value = int(argument)
+    if value < 1:
+        raise ValueError('negative or zero value; must be positive')
+    return value
+
+def positive_int_list(argument):
+    if ',' in argument:
+        entries = argument.split(',')
+    else:
+        entries = argument.split()
+    return [positive_int(entry) for entry in entries]
+
+def encoding(argument):
+    try:
+        codecs.lookup(argument)
+    except LookupError:
+        raise ValueError('unknown encoding: "%s"' % argument)
+    return argument
+
+def choice(argument, values):
+    """
+    Directive option utility function, supplied to enable options whose
+    argument must be a member of a finite set of possible values (must be
+    lower case).  A custom conversion function must be written to use it.  For
+    example::
+
+        from docutils.parsers.rst import directives
+
+        def yesno(argument):
+            return directives.choice(argument, ('yes', 'no'))
+
+    Raise ``ValueError`` if no argument is found or if the argument's value is
+    not valid (not an entry in the supplied list).
+    """
+    try:
+        value = argument.lower().strip()
+    except AttributeError:
+        raise ValueError('must supply an argument; choose from %s'
+                         % format_values(values))
+    if value in values:
+        return value
+    else:
+        raise ValueError('"%s" unknown; choose from %s'
+                         % (argument, format_values(values)))
+
+def format_values(values):
+    return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]),
+                            values[-1])


=== Zope/lib/python/docutils/parsers/rst/directives/admonitions.py 1.2.10.5 => 1.2.10.6 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/admonitions.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,90 @@
+# Author: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Admonition directives.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+from docutils.parsers.rst import states, directives
+from docutils import nodes
+
+
+def make_admonition(node_class, name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+    if not content:
+        error = state_machine.reporter.error(
+            'The "%s" admonition is empty; content required.' % (name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    text = '\n'.join(content)
+    admonition_node = node_class(text)
+    if arguments:
+        title_text = arguments[0]
+        textnodes, messages = state.inline_text(title_text, lineno)
+        admonition_node += nodes.title(title_text, '', *textnodes)
+        admonition_node += messages
+        if options.has_key('class'):
+            class_value = options['class']
+        else:
+            class_value = 'admonition-' + nodes.make_id(title_text)
+        admonition_node.set_class(class_value)
+    state.nested_parse(content, content_offset, admonition_node)
+    return [admonition_node]
+
+def admonition(*args):
+    return make_admonition(nodes.admonition, *args)
+
+admonition.arguments = (1, 0, 1)
+admonition.options = {'class': directives.class_option}
+admonition.content = 1
+
+def attention(*args):
+    return make_admonition(nodes.attention, *args)
+
+attention.content = 1
+
+def caution(*args):
+    return make_admonition(nodes.caution, *args)
+
+caution.content = 1
+
+def danger(*args):
+    return make_admonition(nodes.danger, *args)
+
+danger.content = 1
+
+def error(*args):
+    return make_admonition(nodes.error, *args)
+
+error.content = 1
+
+def hint(*args):
+    return make_admonition(nodes.hint, *args)
+
+hint.content = 1
+
+def important(*args):
+    return make_admonition(nodes.important, *args)
+
+important.content = 1
+
+def note(*args):
+    return make_admonition(nodes.note, *args)
+
+note.content = 1
+
+def tip(*args):
+    return make_admonition(nodes.tip, *args)
+
+tip.content = 1
+
+def warning(*args):
+    return make_admonition(nodes.warning, *args)
+
+warning.content = 1


=== Zope/lib/python/docutils/parsers/rst/directives/body.py 1.2.10.6 => 1.2.10.7 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/body.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,163 @@
+# Author: David Goodger
+# Contact: goodger at python.org
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for additional body elements.
+
+See `docutils.parsers.rst.directives` for API details.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+from docutils import nodes
+from docutils.parsers.rst import directives
+
+              
+def topic(name, arguments, options, content, lineno,
+          content_offset, block_text, state, state_machine,
+          node_class=nodes.topic):
+    if not state_machine.match_titles:
+        error = state_machine.reporter.error(
+              'The "%s" directive may not be used within topics, sidebars, '
+              'or body elements.' % name,
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    if not content:
+        warning = state_machine.reporter.warning(
+            'Content block expected for the "%s" directive; none found.'
+            % name, nodes.literal_block(block_text, block_text),
+            line=lineno)
+        return [warning]
+    title_text = arguments[0]
+    textnodes, messages = state.inline_text(title_text, lineno)
+    titles = [nodes.title(title_text, '', *textnodes)]
+    # sidebar uses this code
+    if options.has_key('subtitle'):
+        textnodes, more_messages = state.inline_text(options['subtitle'],
+                                                     lineno)
+        titles.append(nodes.subtitle(options['subtitle'], '', *textnodes))
+        messages.extend(more_messages)
+    text = '\n'.join(content)
+    node = node_class(text, *(titles + messages))
+    if options.has_key('class'):
+        node.set_class(options['class'])
+    if text:
+        state.nested_parse(content, content_offset, node)
+    return [node]
+
+topic.arguments = (1, 0, 1)
+topic.options = {'class': directives.class_option}
+topic.content = 1
+
+def sidebar(name, arguments, options, content, lineno,
+            content_offset, block_text, state, state_machine):
+    return topic(name, arguments, options, content, lineno,
+                 content_offset, block_text, state, state_machine,
+                 node_class=nodes.sidebar)
+
+sidebar.arguments = (1, 0, 1)
+sidebar.options = {'subtitle': directives.unchanged_required,
+                   'class': directives.class_option}
+sidebar.content = 1
+
+def line_block(name, arguments, options, content, lineno,
+               content_offset, block_text, state, state_machine):
+    if not content:
+        warning = state_machine.reporter.warning(
+            'Content block expected for the "%s" directive; none found.'
+            % name, nodes.literal_block(block_text, block_text), line=lineno)
+        return [warning]
+    block = nodes.line_block()
+    node_list = [block]
+    for line_text in content:
+        text_nodes, messages = state.inline_text(line_text.strip(),
+                                                 lineno + content_offset)
+        line = nodes.line(line_text, '', *text_nodes)
+        if line_text.strip():
+            line.indent = len(line_text) - len(line_text.lstrip())
+        block += line
+        node_list.extend(messages)
+        content_offset += 1
+    state.nest_line_block_lines(block)
+    return node_list
+
+line_block.options = {'class': directives.class_option}
+line_block.content = 1
+
+def parsed_literal(name, arguments, options, content, lineno,
+                   content_offset, block_text, state, state_machine):
+    return block(name, arguments, options, content, lineno,
+                 content_offset, block_text, state, state_machine,
+                 node_class=nodes.literal_block)
+
+parsed_literal.options = {'class': directives.class_option}
+parsed_literal.content = 1
+
+def block(name, arguments, options, content, lineno,
+          content_offset, block_text, state, state_machine, node_class):
+    if not content:
+        warning = state_machine.reporter.warning(
+            'Content block expected for the "%s" directive; none found.'
+            % name, nodes.literal_block(block_text, block_text), line=lineno)
+        return [warning]
+    text = '\n'.join(content)
+    text_nodes, messages = state.inline_text(text, lineno)
+    node = node_class(text, '', *text_nodes, **options)
+    node.line = content_offset + 1
+    return [node] + messages
+
+def rubric(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    rubric_text = arguments[0]
+    textnodes, messages = state.inline_text(rubric_text, lineno)
+    rubric = nodes.rubric(rubric_text, '', *textnodes, **options)
+    return [rubric] + messages
+
+rubric.arguments = (1, 0, 1)
+rubric.options = {'class': directives.class_option}
+
+def epigraph(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    block_quote, messages = state.block_quote(content, content_offset)
+    block_quote.set_class('epigraph')
+    return [block_quote] + messages
+
+epigraph.content = 1
+
+def highlights(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    block_quote, messages = state.block_quote(content, content_offset)
+    block_quote.set_class('highlights')
+    return [block_quote] + messages
+
+highlights.content = 1
+
+def pull_quote(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    block_quote, messages = state.block_quote(content, content_offset)
+    block_quote.set_class('pull-quote')
+    return [block_quote] + messages
+
+pull_quote.content = 1
+
+def compound(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    text = '\n'.join(content)
+    if not text:
+        error = state_machine.reporter.error(
+            'The "%s" directive is empty; content required.' % name,
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    node = nodes.compound(text)
+    if options.has_key('class'):
+        node.set_class(options['class'])
+    state.nested_parse(content, content_offset, node)
+    return [node]
+
+compound.options = {'class': directives.class_option}
+compound.content = 1


=== Zope/lib/python/docutils/parsers/rst/directives/html.py 1.2.10.5 => 1.2.10.6 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/html.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,96 @@
+# Author: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for typically HTML-specific constructs.
+"""
+
+__docformat__ = 'reStructuredText'
+
+import sys
+from docutils import nodes, utils
+from docutils.parsers.rst import states
+from docutils.transforms import components
+
+
+def meta(name, arguments, options, content, lineno,
+         content_offset, block_text, state, state_machine):
+    node = nodes.Element()
+    if content:
+        new_line_offset, blank_finish = state.nested_list_parse(
+              content, content_offset, node, initial_state='MetaBody',
+              blank_finish=1, state_machine_kwargs=metaSMkwargs)
+        if (new_line_offset - content_offset) != len(content):
+            # incomplete parse of block?
+            error = state_machine.reporter.error(
+                'Invalid meta directive.',
+                nodes.literal_block(block_text, block_text), line=lineno)
+            node += error
+    else:
+        error = state_machine.reporter.error(
+            'Empty meta directive.',
+            nodes.literal_block(block_text, block_text), line=lineno)
+        node += error
+    return node.get_children()
+
+meta.content = 1
+
+def imagemap(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    return []
+
+
+class MetaBody(states.SpecializedBody):
+
+    class meta(nodes.Special, nodes.PreBibliographic, nodes.Element):
+        """HTML-specific "meta" element."""
+        pass
+
+    def field_marker(self, match, context, next_state):
+        """Meta element."""
+        node, blank_finish = self.parsemeta(match)
+        self.parent += node
+        return [], next_state, []
+
+    def parsemeta(self, match):
+        name = self.parse_field_marker(match)
+        indented, indent, line_offset, blank_finish = \
+              self.state_machine.get_first_known_indented(match.end())
+        node = self.meta()
+        pending = nodes.pending(components.Filter,
+                                {'component': 'writer',
+                                 'format': 'html',
+                                 'nodes': [node]})
+        node['content'] = ' '.join(indented)
+        if not indented:
+            line = self.state_machine.line
+            msg = self.reporter.info(
+                  'No content for meta tag "%s".' % name,
+                  nodes.literal_block(line, line),
+                  line=self.state_machine.abs_line_number())
+            return msg, blank_finish
+        tokens = name.split()
+        try:
+            attname, val = utils.extract_name_value(tokens[0])[0]
+            node[attname.lower()] = val
+        except utils.NameValueError:
+            node['name'] = tokens[0]
+        for token in tokens[1:]:
+            try:
+                attname, val = utils.extract_name_value(token)[0]
+                node[attname.lower()] = val
+            except utils.NameValueError, detail:
+                line = self.state_machine.line
+                msg = self.reporter.error(
+                      'Error parsing meta tag attribute "%s": %s.'
+                      % (token, detail), nodes.literal_block(line, line),
+                      line=self.state_machine.abs_line_number())
+                return msg, blank_finish
+        self.document.note_pending(pending)
+        return pending, blank_finish
+
+
+metaSMkwargs = {'state_classes': (MetaBody,)}


=== Zope/lib/python/docutils/parsers/rst/directives/images.py 1.2.10.6 => 1.2.10.7 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/images.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,122 @@
+# Author: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for figures and simple images.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+from docutils import nodes, utils
+from docutils.parsers.rst import directives, states
+from docutils.nodes import whitespace_normalize_name
+
+try:
+    import Image                        # PIL
+except ImportError:
+    Image = None
+
+align_values = ('top', 'middle', 'bottom', 'left', 'center', 'right')
+
+def align(argument):
+    return directives.choice(argument, align_values)
+
+def image(name, arguments, options, content, lineno,
+          content_offset, block_text, state, state_machine):
+    messages = []
+    reference = ''.join(arguments[0].split('\n'))
+    if reference.find(' ') != -1:
+        error = state_machine.reporter.error(
+              'Image URI contains whitespace.',
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    options['uri'] = reference
+    reference_node = None
+    if options.has_key('target'):
+        block = states.escape2null(options['target']).splitlines()
+        block = [line for line in block]
+        target_type, data = state.parse_target(block, block_text, lineno)
+        if target_type == 'refuri':
+            reference_node = nodes.reference(refuri=data)
+        elif target_type == 'refname':
+            reference_node = nodes.reference(
+                refname=data, name=whitespace_normalize_name(options['target']))
+            state.document.note_refname(reference_node)
+        else:                           # malformed target
+            messages.append(data)       # data is a system message
+        del options['target']
+    image_node = nodes.image(block_text, **options)
+    if reference_node:
+        reference_node += image_node
+        return messages + [reference_node]
+    else:
+        return messages + [image_node]
+
+image.arguments = (1, 0, 1)
+image.options = {'alt': directives.unchanged,
+                 'height': directives.nonnegative_int,
+                 'width': directives.nonnegative_int,
+                 'scale': directives.nonnegative_int,
+                 'align': align,
+                 'target': directives.unchanged_required,
+                 'class': directives.class_option}
+
+def figure(name, arguments, options, content, lineno,
+           content_offset, block_text, state, state_machine):
+    figwidth = options.setdefault('figwidth')
+    figclass = options.setdefault('figclass')
+    del options['figwidth']
+    del options['figclass']
+    (image_node,) = image(name, arguments, options, content, lineno,
+                         content_offset, block_text, state, state_machine)
+    if isinstance(image_node, nodes.system_message):
+        return [image_node]
+    figure_node = nodes.figure('', image_node)
+    if figwidth == 'image':
+        if Image:
+            # PIL doesn't like Unicode paths:
+            try:
+                i = Image.open(str(image_node['uri']))
+            except (IOError, UnicodeError):
+                pass
+            else:
+                state.document.settings.record_dependencies.add(reference)
+                figure_node['width'] = i.size[0]
+    elif figwidth is not None:
+        figure_node['width'] = figwidth
+    if figclass:
+        figure_node.set_class(figclass)
+    if content:
+        node = nodes.Element()          # anonymous container for parsing
+        state.nested_parse(content, content_offset, node)
+        first_node = node[0]
+        if isinstance(first_node, nodes.paragraph):
+            caption = nodes.caption(first_node.rawsource, '',
+                                    *first_node.children)
+            figure_node += caption
+        elif not (isinstance(first_node, nodes.comment)
+                  and len(first_node) == 0):
+            error = state_machine.reporter.error(
+                  'Figure caption must be a paragraph or empty comment.',
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [figure_node, error]
+        if len(node) > 1:
+            figure_node += nodes.legend('', *node[1:])
+    return [figure_node]
+
+def figwidth_value(argument):
+    if argument.lower() == 'image':
+        return 'image'
+    else:
+        return directives.nonnegative_int(argument)
+
+figure.arguments = (1, 0, 1)
+figure.options = {'figwidth': figwidth_value,
+                  'figclass': directives.class_option}
+figure.options.update(image.options)
+figure.content = 1


=== Zope/lib/python/docutils/parsers/rst/directives/misc.py 1.2.10.6 => 1.2.10.7 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/misc.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,322 @@
+# Authors: David Goodger, Dethe Elza
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""Miscellaneous directives."""
+
+__docformat__ = 'reStructuredText'
+
+import sys
+import os.path
+import re
+from docutils import io, nodes, statemachine, utils
+from docutils.parsers.rst import directives, roles, states
+from docutils.transforms import misc
+
+try:
+    import urllib2
+except ImportError:
+    urllib2 = None
+
+
+def include(name, arguments, options, content, lineno,
+            content_offset, block_text, state, state_machine):
+    """Include a reST file as part of the content of this reST file."""
+    source = state_machine.input_lines.source(
+        lineno - state_machine.input_offset - 1)
+    source_dir = os.path.dirname(os.path.abspath(source))
+    path = ''.join(arguments[0].splitlines())
+    if path.find(' ') != -1:
+        error = state_machine.reporter.error(
+              '"%s" directive path contains whitespace.' % name,
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    path = os.path.normpath(os.path.join(source_dir, path))
+    path = utils.relative_path(None, path)
+    encoding = options.get('encoding', state.document.settings.input_encoding)
+    try:
+        state.document.settings.record_dependencies.add(path)
+        include_file = io.FileInput(
+            source_path=path, encoding=encoding,
+            error_handler=state.document.settings.input_encoding_error_handler,
+            handle_io_errors=None)
+    except IOError, error:
+        severe = state_machine.reporter.severe(
+              'Problems with "%s" directive path:\n%s: %s.'
+              % (name, error.__class__.__name__, error),
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [severe]
+    include_text = include_file.read()
+    if options.has_key('literal'):
+        literal_block = nodes.literal_block(include_text, include_text,
+                                            source=path)
+        literal_block.line = 1
+        return literal_block
+    else:
+        include_lines = statemachine.string2lines(include_text,
+                                                  convert_whitespace=1)
+        state_machine.insert_input(include_lines, path)
+        return []
+
+include.arguments = (1, 0, 1)
+include.options = {'literal': directives.flag,
+                   'encoding': directives.encoding}
+
+def raw(name, arguments, options, content, lineno,
+        content_offset, block_text, state, state_machine):
+    """
+    Pass through content unchanged
+
+    Content is included in output based on type argument
+
+    Content may be included inline (content section of directive) or
+    imported from a file or url.
+    """
+    attributes = {'format': ' '.join(arguments[0].lower().split())}
+    encoding = options.get('encoding', state.document.settings.input_encoding)
+    if content:
+        if options.has_key('file') or options.has_key('url'):
+            error = state_machine.reporter.error(
+                  '"%s" directive may not both specify an external file and '
+                  'have content.' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [error]
+        text = '\n'.join(content)
+    elif options.has_key('file'):
+        if options.has_key('url'):
+            error = state_machine.reporter.error(
+                  'The "file" and "url" options may not be simultaneously '
+                  'specified for the "%s" directive.' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [error]
+        source_dir = os.path.dirname(
+            os.path.abspath(state.document.current_source))
+        path = os.path.normpath(os.path.join(source_dir, options['file']))
+        path = utils.relative_path(None, path)
+        try:
+            state.document.settings.record_dependencies.add(path)
+            raw_file = io.FileInput(
+                source_path=path, encoding=encoding,
+                error_handler=state.document.settings.input_encoding_error_handler,
+                handle_io_errors=None)
+        except IOError, error:
+            severe = state_machine.reporter.severe(
+                  'Problems with "%s" directive path:\n%s.' % (name, error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [severe]
+        text = raw_file.read()
+        attributes['source'] = path
+    elif options.has_key('url'):
+        if not urllib2:
+            severe = state_machine.reporter.severe(
+                  'Problems with the "%s" directive and its "url" option: '
+                  'unable to access the required functionality (from the '
+                  '"urllib2" module).' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [severe]
+        source = options['url']
+        try:
+            raw_text = urllib2.urlopen(source).read()
+        except (urllib2.URLError, IOError, OSError), error:
+            severe = state_machine.reporter.severe(
+                  'Problems with "%s" directive URL "%s":\n%s.'
+                  % (name, options['url'], error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            return [severe]
+        raw_file = io.StringInput(
+            source=raw_text, source_path=source, encoding=encoding,
+            error_handler=state.document.settings.input_encoding_error_handler)
+        text = raw_file.read()
+        attributes['source'] = source
+    else:
+        error = state_machine.reporter.warning(
+            'The "%s" directive requires content; none supplied.' % (name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    raw_node = nodes.raw('', text, **attributes)
+    return [raw_node]
+
+raw.arguments = (1, 0, 1)
+raw.options = {'file': directives.path,
+               'url': directives.path,
+               'encoding': directives.encoding}
+raw.content = 1
+
+def replace(name, arguments, options, content, lineno,
+            content_offset, block_text, state, state_machine):
+    if not isinstance(state, states.SubstitutionDef):
+        error = state_machine.reporter.error(
+            'Invalid context: the "%s" directive can only be used within a '
+            'substitution definition.' % (name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    text = '\n'.join(content)
+    element = nodes.Element(text)
+    if text:
+        state.nested_parse(content, content_offset, element)
+        if len(element) != 1 or not isinstance(element[0], nodes.paragraph):
+            messages = []
+            for node in element:
+                if isinstance(node, nodes.system_message):
+                    if node.has_key('backrefs'):
+                        del node['backrefs']
+                    messages.append(node)
+            error = state_machine.reporter.error(
+                'Error in "%s" directive: may contain a single paragraph '
+                'only.' % (name), line=lineno)
+            messages.append(error)
+            return messages
+        else:
+            return element[0].children
+    else:
+        error = state_machine.reporter.error(
+            'The "%s" directive is empty; content required.' % (name),
+            line=lineno)
+        return [error]
+
+replace.content = 1
+
+def unicode_directive(name, arguments, options, content, lineno,
+                      content_offset, block_text, state, state_machine):
+    r"""
+    Convert Unicode character codes (numbers) to characters.  Codes may be
+    decimal numbers, hexadecimal numbers (prefixed by ``0x``, ``x``, ``\x``,
+    ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style numeric character
+    entities (e.g. ``&#x262E;``).  Text following ".." is a comment and is
+    ignored.  Spaces are ignored, and any other text remains as-is.
+    """
+    if not isinstance(state, states.SubstitutionDef):
+        error = state_machine.reporter.error(
+            'Invalid context: the "%s" directive can only be used within a '
+            'substitution definition.' % (name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    substitution_definition = state_machine.node
+    if options.has_key('trim'):
+        substitution_definition.attributes['ltrim'] = 1
+        substitution_definition.attributes['rtrim'] = 1
+    if options.has_key('ltrim'):
+        substitution_definition.attributes['ltrim'] = 1
+    if options.has_key('rtrim'):
+        substitution_definition.attributes['rtrim'] = 1
+    codes = unicode_comment_pattern.split(arguments[0])[0].split()
+    element = nodes.Element()
+    for code in codes:
+        try:
+            decoded = directives.unicode_code(code)
+        except ValueError, err:
+            error = state_machine.reporter.error(
+                'Invalid character code: %s\n%s: %s'
+                % (code, err.__class__.__name__, err),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            return [error]
+        element += nodes.Text(decoded)
+    return element.children
+
+unicode_directive.arguments = (1, 0, 1)
+unicode_directive.options = {'trim': directives.flag,
+                             'ltrim': directives.flag,
+                             'rtrim': directives.flag}
+unicode_comment_pattern = re.compile(r'( |\n|^)\.\. ')
+
+def class_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+    """
+    Set a "class" attribute on the next element.
+    A "pending" element is inserted, and a transform does the work later.
+    """
+    try:
+        class_value = directives.class_option(arguments[0])
+    except ValueError:
+        error = state_machine.reporter.error(
+            'Invalid class attribute value for "%s" directive: "%s".'
+            % (name, arguments[0]),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    pending = nodes.pending(misc.ClassAttribute,
+                            {'class': class_value, 'directive': name},
+                            block_text)
+    state_machine.document.note_pending(pending)
+    return [pending]
+
+class_directive.arguments = (1, 0, 1)
+class_directive.content = 1
+
+role_arg_pat = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
+                          % ((states.Inliner.simplename,) * 2))
+def role(name, arguments, options, content, lineno,
+         content_offset, block_text, state, state_machine):
+    """Dynamically create and register a custom interpreted text role."""
+    if content_offset > lineno or not content:
+        error = state_machine.reporter.error(
+            '"%s" directive requires arguments on the first line.'
+            % name, nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    args = content[0]
+    match = role_arg_pat.match(args)
+    if not match:
+        error = state_machine.reporter.error(
+            '"%s" directive arguments not valid role names: "%s".'
+            % (name, args), nodes.literal_block(block_text, block_text),
+            line=lineno)
+        return [error]
+    new_role_name = match.group(1)
+    base_role_name = match.group(3)
+    messages = []
+    if base_role_name:
+        base_role, messages = roles.role(
+            base_role_name, state_machine.language, lineno, state.reporter)
+        if base_role is None:
+            error = state.reporter.error(
+                'Unknown interpreted text role "%s".' % base_role_name,
+                nodes.literal_block(block_text, block_text), line=lineno)
+            return messages + [error]
+    else:
+        base_role = roles.generic_custom_role
+    assert not hasattr(base_role, 'arguments'), (
+        'Supplemental directive arguments for "%s" directive not supported'
+        '(specified by "%r" role).' % (name, base_role))
+    try:
+        (arguments, options, content, content_offset) = (
+            state.parse_directive_block(content[1:], content_offset, base_role,
+                                        option_presets={}))
+    except states.MarkupError, detail:
+        error = state_machine.reporter.error(
+            'Error in "%s" directive:\n%s.' % (name, detail),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return messages + [error]
+    if not options.has_key('class'):
+        try:
+            options['class'] = directives.class_option(new_role_name)
+        except ValueError, detail:
+            error = state_machine.reporter.error(
+                'Invalid argument for "%s" directive:\n%s.'
+                % (name, detail),
+                nodes.literal_block(block_text, block_text), line=lineno)
+            return messages + [error]
+    role = roles.CustomRole(new_role_name, base_role, options, content)
+    roles.register_local_role(new_role_name, role)
+    return messages
+
+role.content = 1
+
+def directive_test_function(name, arguments, options, content, lineno,
+                            content_offset, block_text, state, state_machine):
+    """This directive is useful only for testing purposes."""
+    if content:
+        text = '\n'.join(content)
+        info = state_machine.reporter.info(
+            'Directive processed. Type="%s", arguments=%r, options=%r, '
+            'content:' % (name, arguments, options),
+            nodes.literal_block(text, text), line=lineno)
+    else:
+        info = state_machine.reporter.info(
+            'Directive processed. Type="%s", arguments=%r, options=%r, '
+            'content: None' % (name, arguments, options), line=lineno)
+    return [info]
+
+directive_test_function.arguments = (0, 1, 1)
+directive_test_function.options = {'option': directives.unchanged_required}
+directive_test_function.content = 1


=== Zope/lib/python/docutils/parsers/rst/directives/parts.py 1.2.10.5 => 1.2.10.6 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/parts.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,84 @@
+# Author: David Goodger, Dmitry Jemerov
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for document parts.
+"""
+
+__docformat__ = 'reStructuredText'
+
+from docutils import nodes, languages
+from docutils.transforms import parts
+from docutils.parsers.rst import directives
+
+
+backlinks_values = ('top', 'entry', 'none')
+
+def backlinks(arg):
+    value = directives.choice(arg, backlinks_values)
+    if value == 'none':
+        return None
+    else:
+        return value
+
+def contents(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    """Table of contents."""
+    document = state_machine.document
+    language = languages.get_language(document.settings.language_code)
+
+    if arguments:
+        title_text = arguments[0]
+        text_nodes, messages = state.inline_text(title_text, lineno)
+        title = nodes.title(title_text, '', *text_nodes)
+    else:
+        messages = []
+        if options.has_key('local'):
+            title = None
+        else:
+            title = nodes.title('', language.labels['contents'])
+
+    topic = nodes.topic(CLASS='contents')
+
+    cls = options.get('class')
+    if cls:
+        topic.set_class(cls)
+
+    if title:
+        name = title.astext()
+        topic += title
+    else:
+        name = language.labels['contents']
+
+    name = nodes.fully_normalize_name(name)
+    if not document.has_name(name):
+        topic['name'] = name
+    document.note_implicit_target(topic)
+
+    pending = nodes.pending(parts.Contents, rawsource=block_text)
+    pending.details.update(options)
+    document.note_pending(pending)
+    topic += pending
+    return [topic] + messages
+
+contents.arguments = (0, 1, 1)
+contents.options = {'depth': directives.nonnegative_int,
+                    'local': directives.flag,
+                    'backlinks': backlinks,
+                    'class': directives.class_option}
+
+def sectnum(name, arguments, options, content, lineno,
+            content_offset, block_text, state, state_machine):
+    """Automatic section numbering."""
+    pending = nodes.pending(parts.SectNum)
+    pending.details.update(options)
+    state_machine.document.note_pending(pending)
+    return [pending]
+
+sectnum.options = {'depth': int,
+                   'start': int,
+                   'prefix': directives.unchanged_required,
+                   'suffix': directives.unchanged_required}


=== Zope/lib/python/docutils/parsers/rst/directives/references.py 1.2.10.5 => 1.2.10.6 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/references.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,23 @@
+# Author: David Goodger, Dmitry Jemerov
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for references and targets.
+"""
+
+__docformat__ = 'reStructuredText'
+
+from docutils import nodes
+from docutils.transforms import references
+
+
+def target_notes(name, arguments, options, content, lineno,
+                 content_offset, block_text, state, state_machine):
+    """Target footnote generation."""
+    pending = nodes.pending(references.TargetNotes)
+    state_machine.document.note_pending(pending)
+    nodelist = [pending]
+    return nodelist


=== Zope/lib/python/docutils/parsers/rst/directives/tables.py 1.1.2.2 => 1.1.2.3 ===
--- /dev/null	Fri Jan  7 08:26:35 2005
+++ Zope/lib/python/docutils/parsers/rst/directives/tables.py	Fri Jan  7 08:26:04 2005
@@ -0,0 +1,306 @@
+# Authors: David Goodger, David Priest
+# Contact: goodger at python.org
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Directives for table elements.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+import os.path
+from docutils import io, nodes, statemachine, utils
+from docutils.utils import SystemMessagePropagation
+from docutils.parsers.rst import directives
+
+try:
+    import csv                          # new in Python 2.3
+except ImportError:
+    csv = None
+
+try:
+    import urllib2
+except ImportError:
+    urllib2 = None
+
+try:
+    True
+except NameError:                       # Python 2.2 & 2.1 compatibility
+    True = not 0
+    False = not 1
+
+
+def table(name, arguments, options, content, lineno,
+          content_offset, block_text, state, state_machine):
+    if not content:
+        warning = state_machine.reporter.warning(
+            'Content block expected for the "%s" directive; none found.'
+            % name, nodes.literal_block(block_text, block_text),
+            line=lineno)
+        return [warning]
+    title, messages = make_title(arguments, state, lineno)
+    node = nodes.Element()          # anonymous container for parsing
+    text = '\n'.join(content)
+    state.nested_parse(content, content_offset, node)
+    if len(node) != 1 or not isinstance(node[0], nodes.table):
+        error = state_machine.reporter.error(
+            'Error parsing content block for the "%s" directive: '
+            'exactly one table expected.'
+            % name, nodes.literal_block(block_text, block_text),
+            line=lineno)
+        return [error]
+    table_node = node[0]
+    if options.has_key('class'):
+        table_node.set_class(options['class'])
+    if title:
+        table_node.insert(0, title)
+    return [table_node] + messages
+
+table.arguments = (0, 1, 1)
+table.options = {'class': directives.class_option}
+table.content = 1
+
+def make_title(arguments, state, lineno):
+    if arguments:
+        title_text = arguments[0]
+        text_nodes, messages = state.inline_text(title_text, lineno)
+        title = nodes.title(title_text, '', *text_nodes)
+    else:
+        title = None
+        messages = []
+    return title, messages
+
+
+if csv:
+    class DocutilsDialect(csv.Dialect):
+
+        """CSV dialect for `csv_table` directive function."""
+
+        delimiter = ','
+        quotechar = '"'
+        doublequote = True
+        skipinitialspace = True
+        lineterminator = '\n'
+        quoting = csv.QUOTE_MINIMAL
+
+        def __init__(self, options):
+            if options.has_key('delim'):
+                self.delimiter = str(options['delim'])
+            if options.has_key('keepspace'):
+                self.skipinitialspace = False
+            if options.has_key('quote'):
+                self.quotechar = str(options['quote'])
+            if options.has_key('escape'):
+                self.doublequote = False
+                self.escapechar = str(options['escape'])
+            csv.Dialect.__init__(self)
+
+
+    class HeaderDialect(csv.Dialect):
+
+        """CSV dialect to use for the "header" option data."""
+
+        delimiter = ','
+        quotechar = '"'
+        escapechar = '\\'
+        doublequote = False
+        skipinitialspace = True
+        lineterminator = '\n'
+        quoting = csv.QUOTE_MINIMAL
+
+
+def csv_table(name, arguments, options, content, lineno,
+             content_offset, block_text, state, state_machine):
+    try:
+        check_requirements(name, lineno, block_text, state_machine)
+        title, messages = make_title(arguments, state, lineno)
+        csv_data, source = get_csv_data(
+            name, options, content, lineno, block_text, state, state_machine)
+        table_head, max_header_cols = process_header_option(
+            options, state_machine, lineno)
+        rows, max_cols = parse_csv_data_into_rows(
+            csv_data, DocutilsDialect(options), source, options)
+        max_cols = max(max_cols, max_header_cols)
+        header_rows = options.get('header-rows', 0) # default 0
+        check_table_dimensions(
+            rows, header_rows, name, lineno, block_text, state_machine)
+        table_head.extend(rows[:header_rows])
+        table_body = rows[header_rows:]
+        col_widths = get_column_widths(
+            max_cols, name, options, lineno, block_text, state_machine)
+        extend_short_rows_with_empty_cells(max_cols, (table_head, table_body))
+    except SystemMessagePropagation, detail:
+        return [detail.args[0]]
+    except csv.Error, detail:
+        error = state_machine.reporter.error(
+            'Error with CSV data in "%s" directive:\n%s' % (name, detail),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        return [error]
+    table = (col_widths, table_head, table_body)
+    table_node = state.build_table(table, content_offset)
+    if options.has_key('class'):
+        table_node.set_class(options['class'])
+    if title:
+        table_node.insert(0, title)
+    return [table_node] + messages
+
+csv_table.arguments = (0, 1, 1)
+csv_table.options = {'header-rows': directives.nonnegative_int,
+                     'header': directives.unchanged,
+                     'widths': directives.positive_int_list,
+                     'file': directives.path,
+                     'url': directives.path,
+                     'encoding': directives.encoding,
+                     'class': directives.class_option,
+                     # field delimiter char
+                     'delim': directives.single_char_or_whitespace_or_unicode,
+                     # treat whitespace after delimiter as significant
+                     'keepspace': directives.flag,
+                     # text field quote/unquote char:
+                     'quote': directives.single_char_or_unicode,
+                     # char used to escape delim & quote as-needed:
+                     'escape': directives.single_char_or_unicode,}
+csv_table.content = 1
+
+def check_requirements(name, lineno, block_text, state_machine):
+    if not csv:
+        error = state_machine.reporter.error(
+            'The "%s" directive is not compatible with this version of '
+            'Python (%s).  Requires the "csv" module, new in Python 2.3.'
+            % (name, sys.version.split()[0]),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+
+def get_csv_data(name, options, content, lineno, block_text,
+                 state, state_machine):
+    """
+    CSV data can come from the directive content, from an external file, or
+    from a URL reference.
+    """
+    encoding = options.get('encoding', state.document.settings.input_encoding)
+    if content:                         # CSV data is from directive content
+        if options.has_key('file') or options.has_key('url'):
+            error = state_machine.reporter.error(
+                  '"%s" directive may not both specify an external file and '
+                  'have content.' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
+        source = content.source(0)
+        csv_data = content
+    elif options.has_key('file'):       # CSV data is from an external file
+        if options.has_key('url'):
+            error = state_machine.reporter.error(
+                  'The "file" and "url" options may not be simultaneously '
+                  'specified for the "%s" directive.' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
+        source_dir = os.path.dirname(
+            os.path.abspath(state.document.current_source))
+        source = os.path.normpath(os.path.join(source_dir, options['file']))
+        source = utils.relative_path(None, source)
+        try:
+            state.document.settings.record_dependencies.add(source)
+            csv_file = io.FileInput(
+                source_path=source, encoding=encoding,
+                error_handler=state.document.settings.input_encoding_error_handler,
+                handle_io_errors=None)
+            csv_data = csv_file.read().splitlines()
+        except IOError, error:
+            severe = state_machine.reporter.severe(
+                  'Problems with "%s" directive path:\n%s.' % (name, error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(severe)
+    elif options.has_key('url'):        # CSV data is from a URL
+        if not urllib2:
+            severe = state_machine.reporter.severe(
+                  'Problems with the "%s" directive and its "url" option: '
+                  'unable to access the required functionality (from the '
+                  '"urllib2" module).' % name,
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(severe)
+        source = options['url']
+        try:
+            csv_text = urllib2.urlopen(source).read()
+        except (urllib2.URLError, IOError, OSError, ValueError), error:
+            severe = state_machine.reporter.severe(
+                  'Problems with "%s" directive URL "%s":\n%s.'
+                  % (name, options['url'], error),
+                  nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(severe)
+        csv_file = io.StringInput(
+            source=csv_text, source_path=source, encoding=encoding,
+            error_handler=state.document.settings.input_encoding_error_handler)
+        csv_data = csv_file.read().splitlines()
+    else:
+        error = state_machine.reporter.warning(
+            'The "%s" directive requires content; none supplied.' % (name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+    return csv_data, source
+
+def process_header_option(options, state_machine, lineno):
+    source = state_machine.get_source(lineno - 1)
+    table_head = []
+    max_header_cols = 0
+    if options.has_key('header'):       # separate table header in option
+        rows, max_header_cols = parse_csv_data_into_rows(
+            options['header'].split('\n'), HeaderDialect(), source, options)
+        table_head.extend(rows)
+    return table_head, max_header_cols
+
+def parse_csv_data_into_rows(csv_data, dialect, source, options):
+    # csv.py doesn't do Unicode; encode temporarily as UTF-8
+    csv_reader = csv.reader([line.encode('utf-8') for line in csv_data],
+                            dialect=dialect)
+    rows = []
+    max_cols = 0
+    for row in csv_reader:
+        row_data = []
+        for cell in row:
+            # decode UTF-8 back to Unicode
+            cell_text = unicode(cell, 'utf-8')
+            cell_data = (0, 0, 0, statemachine.StringList(
+                cell_text.splitlines(), source=source))
+            row_data.append(cell_data)
+        rows.append(row_data)
+        max_cols = max(max_cols, len(row))
+    return rows, max_cols
+
+def check_table_dimensions(rows, header_rows, name, lineno, block_text,
+                           state_machine):
+    if len(rows) < header_rows:
+        error = state_machine.reporter.error(
+            '%s header row(s) specified but only %s row(s) of data supplied '
+            '("%s" directive).' % (header_rows, len(rows), name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+    elif len(rows) == header_rows > 0:
+        error = state_machine.reporter.error(
+            'Insufficient data supplied (%s row(s)); no data remaining for '
+            'table body, required by "%s" directive.' % (len(rows), name),
+            nodes.literal_block(block_text, block_text), line=lineno)
+        raise SystemMessagePropagation(error)
+
+def get_column_widths(max_cols, name, options, lineno, block_text,
+                      state_machine):
+    if options.has_key('widths'):
+        col_widths = options['widths']
+        if len(col_widths) != max_cols:
+            error = state_machine.reporter.error(
+              '"%s" widths do not match the number of columns in table (%s).'
+              % (name, max_cols),
+              nodes.literal_block(block_text, block_text), line=lineno)
+            raise SystemMessagePropagation(error)
+    else:
+        col_widths = [100 / max_cols] * max_cols
+    return col_widths
+
+def extend_short_rows_with_empty_cells(columns, parts):
+    for part in parts:
+        for row in part:
+            if len(row) < columns:
+                row.extend([(0, 0, 0, [])] * (columns - len(row)))



More information about the Zope-Checkins mailing list