[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. ``☮``). 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