[Zope-Checkins] CVS: Zope/lib/python/docutils/parsers/rst/directives - tables.py:1.1.2.1 __init__.py:1.2.10.6 body.py:1.2.10.5 images.py:1.2.10.5 misc.py:1.2.10.5

Christian 'Tiran' Heimes heimes at faho.rwth-aachen.de
Mon Jul 26 13:38:39 EDT 2004


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

Modified Files:
      Tag: Zope-2_7-branch
	__init__.py body.py images.py misc.py 
Added Files:
      Tag: Zope-2_7-branch
	tables.py 
Log Message:
Updated docutils including a fix for 1426:  System locale breaks reStructuredText horribly
Added rest-language-code to zope.conf schema. it's used instead of the locales


=== Added File Zope/lib/python/docutils/parsers/rst/directives/tables.py ===
# Authors: David Goodger, David Priest
# Contact: goodger at python.org
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/07/26 17:38:09 $
# Copyright: This module has been placed in the public domain.

"""
Directives for table elements.
"""

__docformat__ = 'reStructuredText'


import sys
import os.path
from docutils import 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,
                     '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.
    """
    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:
            csv_file = open(source, 'rb')
            try:
                csv_data = csv_file.read().splitlines()
            finally:
                csv_file.close()
        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_data = urllib2.urlopen(source).read().splitlines()
        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)
    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_reader = csv.reader(csv_data, dialect=dialect)
    rows = []
    max_cols = 0
    for row in csv_reader:
        row_data = []
        for cell in row:
            cell_data = (0, 0, 0, statemachine.StringList(cell.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)))


=== Zope/lib/python/docutils/parsers/rst/directives/__init__.py 1.2.10.5 => 1.2.10.6 ===
--- Zope/lib/python/docutils/parsers/rst/directives/__init__.py:1.2.10.5	Thu May 13 12:19:58 2004
+++ Zope/lib/python/docutils/parsers/rst/directives/__init__.py	Mon Jul 26 13:38:09 2004
@@ -1,5 +1,5 @@
 # Author: David Goodger
-# Contact: goodger at users.sourceforge.net
+# Contact: goodger at python.org
 # Revision: $Revision$
 # Date: $Date$
 # Copyright: This module has been placed in the public domain.
@@ -20,11 +20,12 @@
 
 Parameters:
 
-- ``name`` is the directive type or name.
+- ``name`` is the directive type or name (string).
 
-- ``arguments`` is a list of positional arguments.
+- ``arguments`` is a list of positional arguments (strings).
 
-- ``options`` is a dictionary mapping option names to values.
+- ``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.
 
@@ -63,6 +64,10 @@
   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).
@@ -74,11 +79,12 @@
 See `Creating reStructuredText Directives`_ for more information.
 
 .. _Creating reStructuredText Directives:
-   http://docutils.sourceforge.net/spec/howto/rst-directives.html
+   http://docutils.sourceforge.net/docs/howto/rst-directives.html
 """
 
 __docformat__ = 'reStructuredText'
 
+import re
 from docutils import nodes
 from docutils.parsers.rst.languages import en as _fallback_language_module
 
@@ -102,8 +108,9 @@
       'epigraph': ('body', 'epigraph'),
       'highlights': ('body', 'highlights'),
       'pull-quote': ('body', 'pull_quote'),
-      'table': ('body', 'table'),
       #'questions': ('body', 'question_list'),
+      'table': ('tables', 'table'),
+      'csv-table': ('tables', 'csv_table'),
       'image': ('images', 'image'),
       'figure': ('images', 'figure'),
       'contents': ('parts', 'contents'),
@@ -193,12 +200,12 @@
         return None, messages
     return function, messages
 
-def register_directive(name, directive):
+def register_directive(name, directive_function):
     """
     Register a nonstandard application-defined directive function.
     Language lookups are not needed for such functions.
     """
-    _directives[name] = directive
+    _directives[name] = directive_function
 
 def flag(argument):
     """
@@ -277,6 +284,60 @@
     if not class_name:
         raise ValueError('cannot make "%s" into a class name' % argument)
     return class_name
+
+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 format_values(values):
     return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]),


=== Zope/lib/python/docutils/parsers/rst/directives/body.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/parsers/rst/directives/body.py:1.2.10.4	Thu May 13 12:19:58 2004
+++ Zope/lib/python/docutils/parsers/rst/directives/body.py	Mon Jul 26 13:38:09 2004
@@ -1,11 +1,13 @@
 # Author: David Goodger
-# Contact: goodger at users.sourceforge.net
+# 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'
@@ -15,7 +17,7 @@
 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):
@@ -74,6 +76,7 @@
     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
 
 line_block.options = {'class': directives.class_option}
@@ -121,38 +124,3 @@
     return [block_quote] + messages
 
 pull_quote.content = 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]
-    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
-    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]
-
-table.arguments = (0, 1, 1)
-table.options = {'class': directives.class_option}
-table.content = 1


=== Zope/lib/python/docutils/parsers/rst/directives/images.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/parsers/rst/directives/images.py:1.2.10.4	Thu May 13 12:19:58 2004
+++ Zope/lib/python/docutils/parsers/rst/directives/images.py	Mon Jul 26 13:38:09 2004
@@ -28,6 +28,7 @@
 
 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(
@@ -35,23 +36,26 @@
               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':
-            node_list = nodes.reference(refuri=data)
+            reference_node = nodes.reference(refuri=data)
         elif target_type == 'refname':
-            node_list = nodes.reference(
+            reference_node = nodes.reference(
                 refname=data, name=whitespace_normalize_name(options['target']))
-            state.document.note_refname(node_list)
+            state.document.note_refname(reference_node)
         else:                           # malformed target
-            node_list = [data]          # data is a system message
+            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:
-        node_list = []
-    node_list.append(nodes.image(block_text, **options))
-    return node_list
+        return messages + [image_node]
 
 image.arguments = (1, 0, 1)
 image.options = {'alt': directives.unchanged,


=== Zope/lib/python/docutils/parsers/rst/directives/misc.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/parsers/rst/directives/misc.py:1.2.10.4	Thu May 13 12:19:59 2004
+++ Zope/lib/python/docutils/parsers/rst/directives/misc.py	Mon Jul 26 13:38:09 2004
@@ -117,7 +117,7 @@
                   nodes.literal_block(block_text, block_text), line=lineno)
             return [severe]
         text = raw_file.read()
-        raw_file.close()        
+        raw_file.close()
         attributes['source'] = options['file']
     else:
         error = state_machine.reporter.warning(
@@ -185,29 +185,19 @@
     element = nodes.Element()
     for code in codes:
         try:
-            if code.isdigit():
-                element += nodes.Text(unichr(int(code)))
-            else:
-                match = unicode_pattern.match(code)
-                if match:
-                    value = match.group(1) or match.group(2)
-                    element += nodes.Text(unichr(int(value, 16)))
-                else:
-                    element += nodes.Text(code)
-        except (ValueError, OverflowError), err:
+            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_pattern = re.compile(
-    r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE)
 unicode_comment_pattern = re.compile(r'( |\n|^).. ')
 
-
 def class_directive(name, arguments, options, content, lineno,
                        content_offset, block_text, state, state_machine):
     """"""
@@ -259,7 +249,7 @@
             return messages + [error]
     else:
         base_role = roles.generic_custom_role
-    assert not hasattr(base_role, 'arguments'), ( 
+    assert not hasattr(base_role, 'arguments'), (
         'Supplemental directive arguments for "%s" directive not supported'
         '(specified by "%r" role).' % (name, base_role))
     try:



More information about the Zope-Checkins mailing list