Commit d7e93e24 authored by Andreas Jung's avatar Andreas Jung

update to docutils 0.3.9

parent 635843bd
# Author: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.2.10.9 $
# Date: $Date: 2005/01/07 13:26:01 $
# Revision: $Revision: 3374 $
# Date: $Date: 2005-05-26 23:21:48 +0200 (Thu, 26 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -51,7 +51,7 @@ Subpackages:
__docformat__ = 'reStructuredText'
__version__ = '0.3.7'
__version__ = '0.3.9'
"""``major.minor.micro`` version number. The micro number is bumped for API
changes, for new functionality, and for interim project releases. The minor
number is bumped whenever there is a significant project release. The major
......
# Authors: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:01 $
# Revision: $Revision: 2987 $
# Date: $Date: 2005-02-26 19:17:59 +0100 (Sat, 26 Feb 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -197,6 +197,7 @@ class Publisher:
self.writer.assemble_parts()
except Exception, error:
if self.settings.traceback: # propagate exceptions?
self.debugging_dumps(document)
raise
self.report_Exception(error)
exit = 1
......@@ -210,6 +211,8 @@ class Publisher:
return output
def debugging_dumps(self, document):
if not document:
return
if self.settings.dump_settings:
print >>sys.stderr, '\n::: Runtime settings:'
print >>sys.stderr, pprint.pformat(self.settings.__dict__)
......
# Authors: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.1.4.3 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 3247 $
# Date: $Date: 2005-04-23 21:23:57 +0200 (Sat, 23 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
This module contains practical examples of Docutils client code.
Importing this module is not recommended; its contents are subject to change
in future Docutils releases. Instead, it is recommended that you copy and
paste the parts you need into your own code, modifying as necessary.
Importing this module from client code is not recommended; its contents are
subject to change in future Docutils releases. Instead, it is recommended
that you copy and paste the parts you need into your own code, modifying as
necessary.
"""
from docutils import core
from docutils import core, io
def html_parts(input_string, source_path=None, destination_path=None,
......@@ -72,3 +73,23 @@ def html_fragment(input_string, source_path=None, destination_path=None,
if output_encoding != 'unicode':
fragment = fragment.encode(output_encoding)
return fragment
def internals(input_string, source_path=None, destination_path=None,
input_encoding='unicode'):
"""
Return the document tree and publisher, for exploring Docutils internals.
Parameters: see `html_parts()`.
"""
overrides = {'input_encoding': input_encoding}
output, pub = core.publish_programmatically(
source_class=io.StringInput, source=input_string,
source_path=source_path,
destination_class=io.NullOutput, destination=None,
destination_path=destination_path,
reader=None, reader_name='standalone',
parser=None, parser_name='restructuredtext',
writer=None, writer_name='null',
settings=None, settings_spec=None, settings_overrides=overrides,
config_section=None, enable_exit_status=None)
return pub.writer.document, pub
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.8 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 3358 $
# Date: $Date: 2005-05-21 02:00:25 +0200 (Sat, 21 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -124,6 +124,13 @@ def validate_boolean(setting, value, option_parser,
None, sys.exc_info()[2])
return value
def validate_nonnegative_int(setting, value, option_parser,
config_parser=None, config_section=None):
value = int(value)
if value < 0:
raise ValueError('negative value; must be positive or zero')
return value
def validate_threshold(setting, value, option_parser,
config_parser=None, config_section=None):
try:
......@@ -333,10 +340,10 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
'validator': validate_threshold}),
('Report all system messages, info-level and higher. (Same as '
'"--report=info".)',
['--verbose', '-v'], {'action': 'store_const', 'const': 'info',
['--verbose', '-v'], {'action': 'store_const', 'const': 1,
'dest': 'report_level'}),
('Do not report any system messages. (Same as "--report=none".)',
['--quiet', '-q'], {'action': 'store_const', 'const': 'none',
['--quiet', '-q'], {'action': 'store_const', 'const': 5,
'dest': 'report_level'}),
('Set the threshold (<level>) at or above which system messages are '
'converted to exceptions, halting execution immediately by '
......@@ -429,6 +436,9 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
['--version', '-V'], {'action': 'version'}),
('Show this help message and exit.',
['--help', '-h'], {'action': 'help'}),
# Typically not useful for non-programmatical use.
(SUPPRESS_HELP, ['--id-prefix'], {'default': ''}),
(SUPPRESS_HELP, ['--auto-id-prefix'], {'default': 'id'}),
# Hidden options, for development use only:
(SUPPRESS_HELP, ['--dump-settings'], {'action': 'store_true'}),
(SUPPRESS_HELP, ['--dump-internals'], {'action': 'store_true'}),
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 3138 $
# Date: $Date: 2005-03-27 17:05:34 +0200 (Sun, 27 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -70,32 +70,42 @@ class Input(TransformSpec):
if (self.encoding and self.encoding.lower() == 'unicode'
or isinstance(data, UnicodeType)):
return data
encodings = [self.encoding, 'utf-8']
try:
encodings.append(locale.nl_langinfo(locale.CODESET))
except:
pass
try:
encodings.append(locale.getlocale()[1])
except:
pass
try:
encodings.append(locale.getdefaultlocale()[1])
except:
pass
encodings.append('latin-1')
encodings = [self.encoding]
if not self.encoding:
# Apply heuristics only if no encoding is explicitly given.
encodings.append('utf-8')
try:
encodings.append(locale.nl_langinfo(locale.CODESET))
except:
pass
try:
encodings.append(locale.getlocale()[1])
except:
pass
try:
encodings.append(locale.getdefaultlocale()[1])
except:
pass
encodings.append('latin-1')
error = None
error_details = ''
for enc in encodings:
if not enc:
continue
try:
decoded = unicode(data, enc, self.error_handler)
self.successful_encoding = enc
return decoded
except (UnicodeError, LookupError):
# Return decoded, removing BOMs.
return decoded.replace(u'\ufeff', u'')
except (UnicodeError, LookupError), error:
pass
if error is not None:
error_details = '\n(%s: %s)' % (error.__class__.__name__, error)
raise UnicodeError(
'Unable to decode input data. Tried the following encodings: %s.'
% ', '.join([repr(enc) for enc in encodings if enc]))
'Unable to decode input data. Tried the following encodings: '
'%s.%s'
% (', '.join([repr(enc) for enc in encodings if enc]),
error_details))
class Output(TransformSpec):
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# Internationalization details are documented in
......
# Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Marek Blaha
# Contact: mb@dat.cz
# Revision: $Revision: 1.1.4.4 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Authors: David Goodger; Gunnar Schwant
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1.2.5 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn
# Contact: mghsm@uol.com.ar
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Asko Soukka
# Contact: asko.soukka@iki.fi
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2609 $
# Date: $Date: 2004-09-13 21:25:33 +0200 (Mon, 13 Sep 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Stefane Fermigier
# Contact: sf@fermigier.com
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Nicola Larosa
# Contact: docutils@tekNico.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2944 $
# Date: $Date: 2005-01-20 13:11:50 +0100 (Thu, 20 Jan 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -45,7 +45,7 @@ bibliographic_fields = {
'autori': 'authors',
'organizzazione': 'organization',
'indirizzo': 'address',
'contatti': 'contact',
'contatto': 'contact',
'versione': 'version',
'revisione': 'revision',
'status': 'status',
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.1.4.4 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2333 $
# Date: $Date: 2004-06-20 22:51:22 +0200 (Sun, 20 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Roman Suzi
# Contact: rnd@onego.ru
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2999 $
# Date: $Date: 2005-03-03 20:35:02 +0100 (Thu, 03 Mar 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -46,21 +46,21 @@ labels = {
"""Mapping of node class name to label text."""
bibliographic_fields = {
u'\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f': u'abstract',
u'\u0410\u0434\u0440\u0435\u0441': u'address',
u'\u0410\u0432\u0442\u043e\u0440': u'author',
u'\u0410\u0432\u0442\u043e\u0440\u044b': u'authors',
u'\u041a\u043e\u043d\u0442\u0430\u043a\u0442': u'contact',
u'\u041f\u0440\u0430\u0432\u0430 \u043a\u043e\u043f\u0438\u0440\u043e'
u'\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f': u'abstract',
u'\u0430\u0434\u0440\u0435\u0441': u'address',
u'\u0430\u0432\u0442\u043e\u0440': u'author',
u'\u0430\u0432\u0442\u043e\u0440\u044b': u'authors',
u'\u043a\u043e\u043d\u0442\u0430\u043a\u0442': u'contact',
u'\u043f\u0440\u0430\u0432\u0430 \u043a\u043e\u043f\u0438\u0440\u043e'
u'\u0432\u0430\u043d\u0438\u044f': u'copyright',
u'\u0414\u0430\u0442\u0430': u'date',
u'\u041f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435':
u'\u0434\u0430\u0442\u0430': u'date',
u'\u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435':
u'dedication',
u'\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f':
u'\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f':
u'organization',
u'\u0420\u0435\u0434\u0430\u043a\u0446\u0438\u044f': u'revision',
u'\u0421\u0442\u0430\u0442\u0443\u0441': u'status',
u'\u0412\u0435\u0440\u0441\u0438\u044f': u'version'}
u'\u0440\u0435\u0434\u0430\u043a\u0446\u0438\u044f': u'revision',
u'\u0441\u0442\u0430\u0442\u0443\u0441': u'status',
u'\u0432\u0435\u0440\u0441\u0438\u044f': u'version'}
"""Russian (lowcased) to canonical name mapping for bibliographic fields."""
author_separators = [';', ',']
......
# :Author: Miroslav Vasko
# :Contact: zemiak@zoznam.sk
# :Revision: $Revision: 1.2.10.7 $
# :Date: $Date: 2005/01/07 13:26:02 $
# :Revision: $Revision: 2224 $
# :Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# :Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Joe YS Jaw
# Contact: joeysj@users.sourceforge.net
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2608 $
# Date: $Date: 2004-09-13 21:09:56 +0200 (Mon, 13 Sep 2004) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 3358 $
# Date: $Date: 2005-05-21 02:00:25 +0200 (Sat, 21 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -26,6 +26,8 @@ __docformat__ = 'reStructuredText'
import sys
import os
import re
import copy
import warnings
import xml.dom.minidom
from types import IntType, SliceType, StringType, UnicodeType, \
TupleType, ListType
......@@ -103,7 +105,7 @@ class Node:
or replaced occurs after the current node, the old node will
still be traversed, and any new nodes will not.
Within ``visit`` methods (and ``depart`` methods for
Within ``visit`` methods (and ``depart`` methods for
`walkabout()`), `TreePruningException` subclasses may be raised
(`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`).
......@@ -111,15 +113,15 @@ class Node:
``visit`` implementation for each `Node` subclass encountered.
"""
visitor.document.reporter.debug(
'calling dispatch_visit for %s' % self.__class__.__name__,
category='nodes.Node.walk')
'docutils.nodes.Node.walk calling dispatch_visit for %s'
% self.__class__.__name__)
try:
visitor.dispatch_visit(self)
except (SkipChildren, SkipNode):
return
except SkipDeparture: # not applicable; ignore
pass
children = self.get_children()
children = self.children
try:
for child in children[:]:
child.walk(visitor)
......@@ -138,8 +140,8 @@ class Node:
"""
call_depart = 1
visitor.document.reporter.debug(
'calling dispatch_visit for %s' % self.__class__.__name__,
category='nodes.Node.walkabout')
'docutils.nodes.Node.walkabout calling dispatch_visit for %s'
% self.__class__.__name__)
try:
try:
visitor.dispatch_visit(self)
......@@ -147,7 +149,7 @@ class Node:
return
except SkipDeparture:
call_depart = 0
children = self.get_children()
children = self.children
try:
for child in children[:]:
child.walkabout(visitor)
......@@ -157,10 +159,83 @@ class Node:
pass
if call_depart:
visitor.document.reporter.debug(
'calling dispatch_departure for %s' % self.__class__.__name__,
category='nodes.Node.walkabout')
'docutils.nodes.Node.walkabout calling dispatch_departure '
'for %s' % self.__class__.__name__)
visitor.dispatch_departure(self)
def traverse(self, condition=None,
include_self=1, descend=1, siblings=0, ascend=0):
"""
Return an iterable containing
* self (if include_self is true)
* all descendants in tree traversal order (if descend is true)
* all siblings (if siblings is true) and their descendants (if
also descend is true)
* the siblings of the parent (if ascend is true) and their
descendants (if also descend is true), and so on
If ascend is true, assume siblings to be true as well.
For example, given the following tree::
<paragraph>
<emphasis> <--- emphasis.traverse() and
<strong> <--- strong.traverse() are called.
Foo
Bar
<reference name="Baz" refid="baz">
Baz
Then list(emphasis.traverse()) equals ::
[<emphasis>, <strong>, <#text: Foo>, <#text: Bar>]
and list(strong.traverse(ascend=1)) equals ::
[<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
"""
r = []
if ascend:
siblings=1
if include_self and (condition is None or condition(self)):
r.append(self)
if descend and len(self.children):
for child in self:
r.extend(child.traverse(
include_self=1, descend=1, siblings=0, ascend=0,
condition=condition))
if siblings or ascend:
node = self
while node.parent:
index = node.parent.index(node)
for sibling in node.parent[index+1:]:
r.extend(sibling.traverse(include_self=1, descend=descend,
siblings=0, ascend=0,
condition=condition))
if not ascend:
break
else:
node = node.parent
return r
def next_node(self, condition=None,
include_self=0, descend=1, siblings=0, ascend=0):
"""
Return the first node in the iterable returned by traverse(),
or None if the iterable is empty.
Parameter list is the same as of traverse. Note that
include_self defaults to 0, though.
"""
iterable = self.traverse(condition=condition,
include_self=include_self, descend=descend,
siblings=siblings, ascend=ascend)
try:
return iterable[0]
except IndexError:
return None
class Text(Node, UserString):
......@@ -172,6 +247,9 @@ class Text(Node, UserString):
tagname = '#text'
children = ()
"""Text nodes have no children, and cannot have children."""
def __init__(self, data, rawsource=''):
UserString.__init__(self, data)
......@@ -209,10 +287,6 @@ class Text(Node, UserString):
result.append(indent + line + '\n')
return ''.join(result)
def get_children(self):
"""Text nodes have no children. Return []."""
return []
class Element(Node):
......@@ -225,6 +299,12 @@ class Element(Node):
element['att'] = 'value'
There are two special attributes: 'ids' and 'names'. Both are
lists of unique identifiers, and names serve as human interfaces
to IDs. Names are case- and whitespace-normalized (see the
fully_normalize_name() function), and IDs conform to the regular
expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function).
Elements also emulate lists for child nodes (element nodes and/or text
nodes), indexing by integer. To get the first child node, use::
......@@ -245,6 +325,10 @@ class Element(Node):
This is equivalent to ``element.extend([node1, node2])``.
"""
attr_defaults = {'ids': [], 'classes': [], 'names': [],
'dupnames': [], 'backrefs': []}
"""Default attributes."""
tagname = None
"""The element generic identifier. If None, it is set as an instance
attribute to the name of the class."""
......@@ -261,7 +345,7 @@ class Element(Node):
self.extend(children) # maintain parent info
self.attributes = {}
self.attributes = copy.deepcopy(self.attr_defaults)
"""Dictionary of attribute {name: value}."""
for att, value in attributes.items():
......@@ -272,7 +356,7 @@ class Element(Node):
def _dom_node(self, domroot):
element = domroot.createElement(self.tagname)
for attribute, value in self.attributes.items():
for attribute, value in self.attlist():
if isinstance(value, ListType):
value = ' '.join(['%s' % v for v in value])
element.setAttribute(attribute, '%s' % value)
......@@ -287,16 +371,16 @@ class Element(Node):
if len(data) > 60:
data = data[:56] + ' ...'
break
if self.hasattr('name'):
if self['names']:
return '<%s "%s": %s>' % (self.__class__.__name__,
self.attributes['name'], data)
'; '.join(self['names']), data)
else:
return '<%s: %s>' % (self.__class__.__name__, data)
def shortrepr(self):
if self.hasattr('name'):
if self['names']:
return '<%s "%s"...>' % (self.__class__.__name__,
self.attributes['name'])
'; '.join(self['names']))
else:
return '<%s...>' % self.tagname
......@@ -382,20 +466,24 @@ class Element(Node):
def __iadd__(self, other):
"""Append a node or a list of nodes to `self.children`."""
if isinstance(other, Node):
self.setup_child(other)
self.children.append(other)
self.append(other)
elif other is not None:
for node in other:
self.setup_child(node)
self.children.extend(other)
self.extend(other)
return self
def astext(self):
return self.child_text_separator.join(
[child.astext() for child in self.children])
def non_default_attributes(self):
atts = {}
for key, value in self.attributes.items():
if self.is_not_default(key):
atts[key] = value
return atts
def attlist(self):
attlist = self.attributes.items()
attlist = self.non_default_attributes().items()
attlist.sort()
return attlist
......@@ -420,8 +508,7 @@ class Element(Node):
def extend(self, item):
for node in item:
self.setup_child(node)
self.children.extend(item)
self.append(node)
def insert(self, index, item):
if isinstance(item, Node):
......@@ -439,6 +526,15 @@ class Element(Node):
def index(self, item):
return self.children.index(item)
def is_not_default(self, key):
try:
return self[key] != self.attr_defaults[key]
except KeyError:
return 1
def clear(self):
self.children = []
def replace(self, old, new):
"""Replace one child `Node` with another child or children."""
index = self.index(old)
......@@ -482,12 +578,10 @@ class Element(Node):
if not isinstance(childclass, TupleType):
childclass = (childclass,)
for index in range(start, min(len(self), end)):
match = 0
for c in childclass:
if isinstance(self.children[index], c):
match = 1
break
if not match:
else:
return index
return None
......@@ -496,17 +590,33 @@ class Element(Node):
[child.pformat(indent, level+1)
for child in self.children])
def get_children(self):
"""Return this element's children."""
return self.children
def copy(self):
return self.__class__(**self.attributes)
def set_class(self, name):
"""Add a new name to the "class" attribute."""
self.attributes['class'] = (self.attributes.get('class', '') + ' '
+ name.lower()).strip()
"""Add a new class to the "classes" attribute."""
warnings.warn('docutils.nodes.Element.set_class deprecated; '
"append to Element['classes'] list attribute directly",
DeprecationWarning, stacklevel=2)
assert ' ' not in name
self['classes'].append(name.lower())
def note_referenced_by(self, name=None, id=None):
"""Note that this Element has been referenced by its name
`name` or id `id`."""
self.referenced = 1
# Element.expect_referenced_by_* dictionaries map names or ids
# to nodes whose ``referenced`` attribute is set to true as
# soon as this node is referenced by the given name or id.
# Needed for target propagation.
by_name = getattr(self, 'expect_referenced_by_name', {}).get(name)
by_id = getattr(self, 'expect_referenced_by_id', {}).get(id)
if by_name:
assert name is not None
by_name.referenced = 1
if by_id:
assert id is not None
by_id.referenced = 1
class TextElement(Element):
......@@ -514,7 +624,7 @@ class TextElement(Element):
"""
An element which directly contains text.
Its children are all `Text` or `TextElement` subclass nodes. You can
Its children are all `Text` or `Inline` subclass nodes. You can
check whether an element's context is inline simply by checking whether
its immediate parent is a `TextElement` instance (including subclasses).
This is handy for nodes like `image` that can appear both inline and as
......@@ -557,7 +667,7 @@ class Resolvable:
class BackLinkable:
def add_backref(self, refid):
self.setdefault('backrefs', []).append(refid)
self['backrefs'].append(refid)
# ====================
......@@ -568,15 +678,12 @@ class Root: pass
class Titular: pass
class PreDecorative:
"""Category of Node which may occur before Decorative Nodes."""
class PreBibliographic(PreDecorative):
class PreBibliographic:
"""Category of Node which may occur before Bibliographic Nodes."""
class Bibliographic(PreDecorative): pass
class Bibliographic: pass
class Decorative: pass
class Decorative(PreBibliographic): pass
class Structural: pass
......@@ -584,7 +691,8 @@ class Body: pass
class General(Body): pass
class Sequential(Body): pass
class Sequential(Body):
"""List-like elements."""
class Admonition(Body): pass
......@@ -604,9 +712,6 @@ class Targetable(Resolvable):
referenced = 0
indirect_reference_name = None
"""Holds the whitespace_normalized_name (contains mixed case) of a target"""
class Labeled:
"""Contains a `label` as its first element."""
......@@ -717,6 +822,9 @@ class document(Root, Structural, Element):
self.transformer = docutils.transforms.Transformer(self)
"""Storage for transforms to be applied to this document."""
self.decoration = None
"""Document's `decoration` node."""
self.document = self
def asdom(self, dom=xml.dom.minidom):
......@@ -726,21 +834,23 @@ class document(Root, Structural, Element):
return domroot
def set_id(self, node, msgnode=None):
if node.has_key('id'):
id = node['id']
for id in node['ids']:
if self.ids.has_key(id) and self.ids[id] is not node:
msg = self.reporter.severe('Duplicate ID: "%s".' % id)
if msgnode != None:
msgnode += msg
else:
if node.has_key('name'):
id = make_id(node['name'])
if not node['ids']:
for name in node['names']:
id = self.settings.id_prefix + make_id(name)
if id and not self.ids.has_key(id):
break
else:
id = ''
while not id or self.ids.has_key(id):
id = 'id%s' % self.id_start
self.id_start += 1
node['id'] = id
while not id or self.ids.has_key(id):
id = (self.settings.id_prefix +
self.settings.auto_id_prefix + str(self.id_start))
self.id_start += 1
node['ids'].append(id)
self.ids[id] = node
return id
......@@ -775,8 +885,7 @@ class document(Root, Structural, Element):
both old and new targets are external and refer to identical URIs.
The new target is invalidated regardless.
"""
if node.has_key('name'):
name = node['name']
for name in node['names']:
if self.nameids.has_key(name):
self.set_duplicate_name_id(node, id, name, msgnode, explicit)
else:
......@@ -794,30 +903,30 @@ class document(Root, Structural, Element):
old_node = self.ids[old_id]
if node.has_key('refuri'):
refuri = node['refuri']
if old_node.has_key('name') \
if old_node['names'] \
and old_node.has_key('refuri') \
and old_node['refuri'] == refuri:
level = 1 # just inform if refuri's identical
if level > 1:
dupname(old_node)
dupname(old_node, name)
self.nameids[name] = None
msg = self.reporter.system_message(
level, 'Duplicate explicit target name: "%s".' % name,
backrefs=[id], base_node=node)
if msgnode != None:
msgnode += msg
dupname(node)
dupname(node, name)
else:
self.nameids[name] = id
if old_id is not None:
old_node = self.ids[old_id]
dupname(old_node)
dupname(old_node, name)
else:
if old_id is not None and not old_explicit:
self.nameids[name] = None
old_node = self.ids[old_id]
dupname(old_node)
dupname(node)
dupname(old_node, name)
dupname(node, name)
if not explicit or (not old_explicit and old_id is not None):
msg = self.reporter.info(
'Duplicate implicit target name: "%s".' % name,
......@@ -851,7 +960,7 @@ class document(Root, Structural, Element):
def note_indirect_target(self, target):
self.indirect_targets.append(target)
if target.has_key('name'):
if target['names']:
self.note_refname(target)
def note_anonymous_target(self, target):
......@@ -895,7 +1004,8 @@ class document(Root, Structural, Element):
self.note_refname(ref)
def note_substitution_def(self, subdef, def_name, msgnode=None):
name = subdef['name'] = whitespace_normalize_name(def_name)
name = whitespace_normalize_name(def_name)
subdef['names'].append(name)
if self.substitution_defs.has_key(name):
msg = self.reporter.error(
'Duplicate substitution definition name: "%s".' % name,
......@@ -903,7 +1013,7 @@ class document(Root, Structural, Element):
if msgnode != None:
msgnode += msg
oldnode = self.substitution_defs[name]
dupname(oldnode)
dupname(oldnode, name)
# keep only the last definition:
self.substitution_defs[name] = subdef
# case-insensitive mapping:
......@@ -933,6 +1043,16 @@ class document(Root, Structural, Element):
return self.__class__(self.settings, self.reporter,
**self.attributes)
def get_decoration(self):
if not self.decoration:
self.decoration = decoration()
index = self.first_child_not_matching_class(Titular)
if index is None:
self.append(self.decoration)
else:
self.insert(index, self.decoration)
return self.decoration
# ================
# Title Elements
......@@ -964,7 +1084,19 @@ class copyright(Bibliographic, TextElement): pass
# Decorative Elements
# =====================
class decoration(Decorative, Element): pass
class decoration(Decorative, Element):
def get_header(self):
if not len(self.children) or not isinstance(self.children[0], header):
self.insert(0, header())
return self.children[0]
def get_footer(self):
if not len(self.children) or not isinstance(self.children[-1], footer):
self.append(footer())
return self.children[-1]
class header(Decorative, Element): pass
class footer(Decorative, Element): pass
......@@ -1061,7 +1193,7 @@ class doctest_block(General, FixedTextElement): pass
class line_block(General, Element): pass
class line(General, TextElement):
class line(Part, TextElement):
indent = None
......@@ -1081,8 +1213,8 @@ class admonition(Admonition, Element): pass
class comment(Special, Invisible, FixedTextElement): pass
class substitution_definition(Special, Invisible, TextElement): pass
class target(Special, Invisible, Inline, TextElement, Targetable): pass
class footnote(General, Element, Labeled, BackLinkable): pass
class citation(General, Element, Labeled, BackLinkable): pass
class footnote(General, BackLinkable, Element, Labeled, Targetable): pass
class citation(General, BackLinkable, Element, Labeled, Targetable): pass
class label(Part, TextElement): pass
class figure(General, Element): pass
class caption(Part, TextElement): pass
......@@ -1096,7 +1228,7 @@ class row(Part, Element): pass
class entry(Part, Element): pass
class system_message(Special, PreBibliographic, Element, BackLinkable):
class system_message(Special, BackLinkable, PreBibliographic, Element):
def __init__(self, message=None, *children, **attributes):
if message:
......@@ -1210,7 +1342,7 @@ class superscript(Inline, TextElement): pass
class subscript(Inline, TextElement): pass
class image(General, Inline, TextElement):
class image(General, Inline, Element):
def astext(self):
return self.get('alt', '')
......@@ -1306,8 +1438,8 @@ class NodeVisitor:
node_name = node.__class__.__name__
method = getattr(self, 'visit_' + node_name, self.unknown_visit)
self.document.reporter.debug(
'calling %s for %s' % (method.__name__, node_name),
category='nodes.NodeVisitor.dispatch_visit')
'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s'
% (method.__name__, node_name))
return method(node)
def dispatch_departure(self, node):
......@@ -1319,8 +1451,8 @@ class NodeVisitor:
node_name = node.__class__.__name__
method = getattr(self, 'depart_' + node_name, self.unknown_departure)
self.document.reporter.debug(
'calling %s for %s' % (method.__name__, node_name),
category='nodes.NodeVisitor.dispatch_departure')
'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s'
% (method.__name__, node_name))
return method(node)
def unknown_visit(self, node):
......@@ -1357,6 +1489,7 @@ class SparseNodeVisitor(NodeVisitor):
subclasses), subclass `NodeVisitor` instead.
"""
class GenericNodeVisitor(NodeVisitor):
"""
......@@ -1398,10 +1531,11 @@ def _add_node_class_names(names):
setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
setattr(SparseNodeVisitor, 'depart' + _name, _nop)
setattr(SparseNodeVisitor, 'depart_' + _name, _nop)
_add_node_class_names(node_class_names)
class TreeCopyVisitor(GenericNodeVisitor):
"""
......@@ -1534,9 +1668,12 @@ def make_id(string):
_non_id_chars = re.compile('[^a-z0-9]+')
_non_id_at_ends = re.compile('^[-0-9]+|-+$')
def dupname(node):
node['dupname'] = node['name']
del node['name']
def dupname(node, name):
node['dupnames'].append(name)
node['names'].remove(name)
# Assume that this method is referenced, even though it isn't; we
# don't want to throw unnecessary system_messages.
node.referenced = 1
def fully_normalize_name(name):
"""Return a case- and whitespace-normalized name."""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:03 $
# Revision: $Revision: 1645 $
# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:03 $
# Revision: $Revision: 3171 $
# Date: $Date: 2005-04-05 17:26:16 +0200 (Tue, 05 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -112,7 +112,23 @@ class Parser(docutils.parsers.Parser):
('Leave spaces before footnote references.',
['--leave-footnote-reference-space'],
{'action': 'store_false', 'dest': 'trim_footnote_reference_space',
'validator': frontend.validate_boolean}),))
'validator': frontend.validate_boolean}),
('Disable directives that insert the contents of external file '
'("include" & "raw"); replaced with a "warning" system message.',
['--no-file-insertion'],
{'action': 'store_false', 'default': 1,
'dest': 'file_insertion_enabled'}),
('Enable directives that insert the contents of external file '
'("include" & "raw"). Enabled by default.',
['--file-insertion-enabled'],
{'action': 'store_true', 'dest': 'file_insertion_enabled'}),
('Disable the "raw" directives; replaced with a "warning" '
'system message.',
['--no-raw'],
{'action': 'store_false', 'default': 1, 'dest': 'raw_enabled'}),
('Enable the "raw" directive. Enabled by default.',
['--raw-enabled'],
{'action': 'store_true', 'dest': 'raw_enabled'}),))
config_section = 'restructuredtext parser'
config_section_dependencies = ('parsers',)
......@@ -128,11 +144,10 @@ class Parser(docutils.parsers.Parser):
def parse(self, inputstring, document):
"""Parse `inputstring` and populate `document`, a document tree."""
self.setup_parse(inputstring, document)
debug = document.reporter[''].debug
self.statemachine = states.RSTStateMachine(
state_classes=self.state_classes,
initial_state=self.initial_state,
debug=debug)
debug=document.reporter.debug_flag)
inputlines = docutils.statemachine.string2lines(
inputstring, tab_width=document.settings.tab_width,
convert_whitespace=1)
......
# Author: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.2.10.8 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -113,10 +113,13 @@ _directive_registry = {
#'questions': ('body', 'question_list'),
'table': ('tables', 'table'),
'csv-table': ('tables', 'csv_table'),
'list-table': ('tables', 'list_table'),
'image': ('images', 'image'),
'figure': ('images', 'figure'),
'contents': ('parts', 'contents'),
'sectnum': ('parts', 'sectnum'),
'header': ('parts', 'header'),
'footer': ('parts', 'footer'),
#'footnotes': ('parts', 'footnotes'),
#'citations': ('parts', 'citations'),
'target-notes': ('references', 'target_notes'),
......@@ -250,17 +253,26 @@ 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.
Raise ``ValueError`` if no argument is found.
"""
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')
return path
def uri(argument):
"""
Return the URI argument with whitespace removed.
(Directive option conversion function.)
Raise ``ValueError`` if no argument is found.
"""
if argument is None:
raise ValueError('argument required but none supplied')
else:
uri = ''.join(argument.split())
return uri
def nonnegative_int(argument):
"""
......@@ -274,7 +286,7 @@ def nonnegative_int(argument):
def class_option(argument):
"""
Convert the argument into an ID-compatible string and return it.
Convert the argument into a list of ID-compatible strings and return it.
(Directive option conversion function.)
Raise ``ValueError`` if no argument is found.
......@@ -288,7 +300,7 @@ def class_option(argument):
if not class_name:
raise ValueError('cannot make "%s" into a class name' % name)
class_names.append(class_name)
return ' '.join(class_names)
return class_names
unicode_pattern = re.compile(
r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE)
......@@ -296,10 +308,13 @@ unicode_pattern = re.compile(
def unicode_code(code):
r"""
Convert a Unicode character code to a Unicode character.
(Directive option conversion function.)
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.
Raise ValueError for illegal Unicode code values.
"""
try:
if code.isdigit(): # decimal number
......@@ -315,6 +330,10 @@ def unicode_code(code):
raise ValueError('code too large (%s)' % detail)
def single_char_or_unicode(argument):
"""
A single character is returned as-is. Unicode characters codes are
converted as in `unicode_code`. (Directive option conversion function.)
"""
char = unicode_code(argument)
if len(char) > 1:
raise ValueError('%r invalid; must be a single character or '
......@@ -322,6 +341,10 @@ def single_char_or_unicode(argument):
return char
def single_char_or_whitespace_or_unicode(argument):
"""
As with `single_char_or_unicode`, but "tab" and "space" are also supported.
(Directive option conversion function.)
"""
if argument == 'tab':
char = '\t'
elif argument == 'space':
......@@ -331,12 +354,23 @@ def single_char_or_whitespace_or_unicode(argument):
return char
def positive_int(argument):
"""
Converts the argument into an integer. Raises ValueError for negative,
zero, or non-integer values. (Directive option conversion function.)
"""
value = int(argument)
if value < 1:
raise ValueError('negative or zero value; must be positive')
return value
def positive_int_list(argument):
"""
Converts a space- or comma-separated list of values into a Python list
of integers.
(Directive option conversion function.)
Raises ValueError for non-positive-integer values.
"""
if ',' in argument:
entries = argument.split(',')
else:
......@@ -344,6 +378,12 @@ def positive_int_list(argument):
return [positive_int(entry) for entry in entries]
def encoding(argument):
"""
Verfies the encoding argument by lookup.
(Directive option conversion function.)
Raises ValueError for unknown encodings.
"""
try:
codecs.lookup(argument)
except LookupError:
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3155 $
# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -30,10 +30,10 @@ def make_admonition(node_class, name, arguments, options, content, lineno,
admonition_node += nodes.title(title_text, '', *textnodes)
admonition_node += messages
if options.has_key('class'):
class_value = options['class']
classes = options['class']
else:
class_value = 'admonition-' + nodes.make_id(title_text)
admonition_node.set_class(class_value)
classes = ['admonition-' + nodes.make_id(title_text)]
admonition_node['classes'] += classes
state.nested_parse(content, content_offset, admonition_node)
return [admonition_node]
......
# Author: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3206 $
# Date: $Date: 2005-04-12 01:16:11 +0200 (Tue, 12 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -16,14 +16,16 @@ __docformat__ = 'reStructuredText'
import sys
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst.roles import set_classes
def topic(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine,
node_class=nodes.topic):
if not state_machine.match_titles:
if not (state_machine.match_titles
or isinstance(state_machine.node, nodes.sidebar)):
error = state_machine.reporter.error(
'The "%s" directive may not be used within topics, sidebars, '
'The "%s" directive may not be used within topics '
'or body elements.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
......@@ -44,8 +46,7 @@ def topic(name, arguments, options, content, lineno,
messages.extend(more_messages)
text = '\n'.join(content)
node = node_class(text, *(titles + messages))
if options.has_key('class'):
node.set_class(options['class'])
node['classes'] += options.get('class', [])
if text:
state.nested_parse(content, content_offset, node)
return [node]
......@@ -56,6 +57,11 @@ topic.content = 1
def sidebar(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
if isinstance(state_machine.node, nodes.sidebar):
error = state_machine.reporter.error(
'The "%s" directive may not be used within a sidebar element.'
% name, nodes.literal_block(block_text, block_text), line=lineno)
return [error]
return topic(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine,
node_class=nodes.sidebar)
......@@ -72,7 +78,7 @@ def line_block(name, arguments, options, content, lineno,
'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()
block = nodes.line_block(classes=options.get('class', []))
node_list = [block]
for line_text in content:
text_nodes, messages = state.inline_text(line_text.strip(),
......@@ -91,6 +97,7 @@ line_block.content = 1
def parsed_literal(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
set_classes(options)
return block(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine,
node_class=nodes.literal_block)
......@@ -124,7 +131,7 @@ 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')
block_quote['classes'].append('epigraph')
return [block_quote] + messages
epigraph.content = 1
......@@ -132,7 +139,7 @@ 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')
block_quote['classes'].append('highlights')
return [block_quote] + messages
highlights.content = 1
......@@ -140,7 +147,7 @@ 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')
block_quote['classes'].append('pull-quote')
return [block_quote] + messages
pull_quote.content = 1
......@@ -154,8 +161,7 @@ def compound(name, arguments, options, content, lineno,
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'])
node['classes'] += options.get('class', [])
state.nested_parse(content, content_offset, node)
return [node]
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3038 $
# Date: $Date: 2005-03-14 17:16:57 +0100 (Mon, 14 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -34,7 +34,7 @@ def meta(name, arguments, options, content, lineno,
'Empty meta directive.',
nodes.literal_block(block_text, block_text), line=lineno)
node += error
return node.get_children()
return node.children
meta.content = 1
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3347 $
# Date: $Date: 2005-05-18 20:17:33 +0200 (Wed, 18 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -14,27 +14,43 @@ __docformat__ = 'reStructuredText'
import sys
from docutils import nodes, utils
from docutils.parsers.rst import directives, states
from docutils.nodes import whitespace_normalize_name
from docutils.nodes import fully_normalize_name
from docutils.parsers.rst.roles import set_classes
try:
import Image # PIL
except ImportError:
Image = None
align_values = ('top', 'middle', 'bottom', 'left', 'center', 'right')
align_h_values = ('left', 'center', 'right')
align_v_values = ('top', 'middle', 'bottom')
align_values = align_v_values + align_h_values
def align(argument):
return directives.choice(argument, align_values)
def image(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
if options.has_key('align'):
# check for align_v values only
if isinstance(state, states.SubstitutionDef):
if options['align'] not in align_v_values:
error = state_machine.reporter.error(
'Error in "%s" directive: "%s" is not a valid value for '
'the "align" option within a substitution definition. '
'Valid values for "align" are: "%s".'
% (name, options['align'], '", "'.join(align_v_values)),
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
elif options['align'] not in align_h_values:
error = state_machine.reporter.error(
'Error in "%s" directive: "%s" is not a valid value for '
'the "align" option. Valid values for "align" are: "%s".'
% (name, options['align'], '", "'.join(align_h_values)),
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
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]
reference = directives.uri(arguments[0])
options['uri'] = reference
reference_node = None
if options.has_key('target'):
......@@ -44,12 +60,13 @@ def image(name, arguments, options, content, 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']))
reference_node = nodes.reference(refname=data,
name=fully_normalize_name(options['target']))
state.document.note_refname(reference_node)
else: # malformed target
messages.append(data) # data is a system message
del options['target']
set_classes(options)
image_node = nodes.image(block_text, **options)
if reference_node:
reference_node += image_node
......@@ -66,31 +83,38 @@ image.options = {'alt': directives.unchanged,
'target': directives.unchanged_required,
'class': directives.class_option}
def figure_align(argument):
return directives.choice(argument, align_h_values)
def figure(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
figwidth = options.setdefault('figwidth')
figclass = options.setdefault('figclass')
figclasses = options.setdefault('figclass')
align = options.setdefault('align')
del options['figwidth']
del options['figclass']
del options['align']
(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:
if Image and state.document.settings.file_insertion_enabled:
# 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)
state.document.settings.record_dependencies.add(image_node['uri'])
figure_node['width'] = i.size[0]
elif figwidth is not None:
figure_node['width'] = figwidth
if figclass:
figure_node.set_class(figclass)
if figclasses:
figure_node['classes'] += figclasses
if align:
figure_node['align'] = align
if content:
node = nodes.Element() # anonymous container for parsing
state.nested_parse(content, content_offset, node)
......@@ -119,4 +143,5 @@ figure.arguments = (1, 0, 1)
figure.options = {'figwidth': figwidth_value,
'figclass': directives.class_option}
figure.options.update(image.options)
figure.options['align'] = figure_align
figure.content = 1
# Authors: David Goodger, Dethe Elza
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3129 $
# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""Miscellaneous directives."""
......@@ -24,15 +24,15 @@ except ImportError:
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."""
if not state.document.settings.file_insertion_enabled:
warning = state_machine.reporter.warning(
'"%s" directive disabled.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [warning]
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 = directives.path(arguments[0])
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)
......@@ -48,7 +48,14 @@ def include(name, arguments, options, content, lineno,
% (name, error.__class__.__name__, error),
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
include_text = include_file.read()
try:
include_text = include_file.read()
except UnicodeError, error:
severe = state_machine.reporter.severe(
'Problem with "%s" directive:\n%s: %s'
% (name, error.__class__.__name__, error),
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
if options.has_key('literal'):
literal_block = nodes.literal_block(include_text, include_text,
source=path)
......@@ -74,6 +81,14 @@ def raw(name, arguments, options, content, lineno,
Content may be included inline (content section of directive) or
imported from a file or url.
"""
print 2
if ( not state.document.settings.raw_enabled
or (not state.document.settings.file_insertion_enabled
and (options.has_key('file') or options.has_key('url'))) ):
warning = state_machine.reporter.warning(
'"%s" directive disabled.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [warning]
attributes = {'format': ' '.join(arguments[0].lower().split())}
encoding = options.get('encoding', state.document.settings.input_encoding)
if content:
......@@ -106,7 +121,14 @@ def raw(name, arguments, options, content, lineno,
'Problems with "%s" directive path:\n%s.' % (name, error),
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
text = raw_file.read()
try:
text = raw_file.read()
except UnicodeError, error:
severe = state_machine.reporter.severe(
'Problem with "%s" directive:\n%s: %s'
% (name, error.__class__.__name__, error),
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
attributes['source'] = path
elif options.has_key('url'):
if not urllib2:
......@@ -128,7 +150,14 @@ def raw(name, arguments, options, content, lineno,
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()
try:
text = raw_file.read()
except UnicodeError, error:
severe = state_machine.reporter.severe(
'Problem with "%s" directive:\n%s: %s'
% (name, error.__class__.__name__, error),
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
attributes['source'] = source
else:
error = state_machine.reporter.warning(
......@@ -140,7 +169,7 @@ def raw(name, arguments, options, content, lineno,
raw.arguments = (1, 0, 1)
raw.options = {'file': directives.path,
'url': directives.path,
'url': directives.uri,
'encoding': directives.encoding}
raw.content = 1
......@@ -160,8 +189,7 @@ def replace(name, arguments, options, content, lineno,
messages = []
for node in element:
if isinstance(node, nodes.system_message):
if node.has_key('backrefs'):
del node['backrefs']
node['backrefs'] = []
messages.append(node)
error = state_machine.reporter.error(
'Error in "%s" directive: may contain a single paragraph '
......
# Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3199 $
# Date: $Date: 2005-04-09 03:32:29 +0200 (Sat, 09 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -26,10 +26,24 @@ def backlinks(arg):
def contents(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""Table of contents."""
"""
Table of contents.
The table of contents is generated in two passes: initial parse and
transform. During the initial parse, a 'pending' element is generated
which acts as a placeholder, storing the TOC title and any options
internally. At a later stage in the processing, the 'pending' element is
replaced by a 'topic' element, a title and the table of contents proper.
"""
if not (state_machine.match_titles
or isinstance(state_machine.node, nodes.sidebar)):
error = state_machine.reporter.error(
'The "%s" directive may not be used within topics '
'or body elements.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
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)
......@@ -40,24 +54,17 @@ def contents(name, arguments, options, content, lineno,
title = None
else:
title = nodes.title('', language.labels['contents'])
topic = nodes.topic(CLASS='contents')
cls = options.get('class')
if cls:
topic.set_class(cls)
topic = nodes.topic(classes=['contents'])
topic['classes'] += options.get('class', [])
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
topic['names'].append(name)
document.note_implicit_target(topic)
pending = nodes.pending(parts.Contents, rawsource=block_text)
pending.details.update(options)
document.note_pending(pending)
......@@ -82,3 +89,36 @@ sectnum.options = {'depth': int,
'start': int,
'prefix': directives.unchanged_required,
'suffix': directives.unchanged_required}
def header_footer(node, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""Contents of document header or footer."""
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)
node.append(nodes.paragraph(
'', 'Problem with the "%s" directive: no content supplied.' % name))
return [warning]
text = '\n'.join(content)
state.nested_parse(content, content_offset, node)
return []
def header(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
decoration = state_machine.document.get_decoration()
node = decoration.get_header()
return header_footer(node, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine)
header.content = 1
def footer(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
decoration = state_machine.document.get_decoration()
node = decoration.get_footer()
return header_footer(node, name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine)
footer.content = 1
# Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 856 $
# Date: $Date: 2002-10-24 03:01:53 +0200 (Thu, 24 Oct 2002) $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, David Priest
# Contact: goodger@python.org
# Revision: $Revision: 1.1.2.3 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3165 $
# Date: $Date: 2005-04-05 04:55:06 +0200 (Tue, 05 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -44,7 +44,6 @@ def table(name, arguments, options, content, 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(
......@@ -54,8 +53,7 @@ def table(name, arguments, options, content, lineno,
line=lineno)
return [error]
table_node = node[0]
if options.has_key('class'):
table_node.set_class(options['class'])
table_node['classes'] += options.get('class', [])
if title:
table_node.insert(0, title)
return [table_node] + messages
......@@ -116,6 +114,12 @@ if csv:
def csv_table(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
try:
if ( not state.document.settings.file_insertion_enabled
and (options.has_key('file') or options.has_key('url')) ):
warning = state_machine.reporter.warning(
'"%s" directive disabled.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [warning]
check_requirements(name, lineno, block_text, state_machine)
title, messages = make_title(arguments, state, lineno)
csv_data, source = get_csv_data(
......@@ -126,8 +130,10 @@ def csv_table(name, arguments, options, content, lineno,
csv_data, DocutilsDialect(options), source, options)
max_cols = max(max_cols, max_header_cols)
header_rows = options.get('header-rows', 0) # default 0
stub_columns = options.get('stub-columns', 0) # default 0
check_table_dimensions(
rows, header_rows, name, lineno, block_text, state_machine)
rows, header_rows, stub_columns, name, lineno,
block_text, state_machine)
table_head.extend(rows[:header_rows])
table_body = rows[header_rows:]
col_widths = get_column_widths(
......@@ -141,19 +147,19 @@ def csv_table(name, arguments, options, content, lineno,
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'])
table_node = state.build_table(table, content_offset, stub_columns)
table_node['classes'] += options.get('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,
'stub-columns': directives.nonnegative_int,
'header': directives.unchanged,
'widths': directives.positive_int_list,
'file': directives.path,
'url': directives.path,
'url': directives.uri,
'encoding': directives.encoding,
'class': directives.class_option,
# field delimiter char
......@@ -206,7 +212,8 @@ def get_csv_data(name, options, content, lineno, block_text,
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,
error_handler
=state.document.settings.input_encoding_error_handler,
handle_io_errors=None)
csv_data = csv_file.read().splitlines()
except IOError, error:
......@@ -270,20 +277,34 @@ def parse_csv_data_into_rows(csv_data, dialect, source, options):
max_cols = max(max_cols, len(row))
return rows, max_cols
def check_table_dimensions(rows, header_rows, name, lineno, block_text,
state_machine):
def check_table_dimensions(rows, header_rows, stub_columns, 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:
if 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)
for row in rows:
if len(row) < stub_columns:
error = state_machine.reporter.error(
'%s stub column(s) specified but only %s columns(s) of data '
'supplied ("%s" directive).' % (stub_columns, len(row), name),
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
if len(row) == stub_columns > 0:
error = state_machine.reporter.error(
'Insufficient data supplied (%s columns(s)); no data remaining '
'for table body, required by "%s" directive.'
% (len(row), 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):
......@@ -295,8 +316,13 @@ def get_column_widths(max_cols, name, options, lineno, block_text,
% (name, max_cols),
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
else:
elif max_cols:
col_widths = [100 / max_cols] * max_cols
else:
error = state_machine.reporter.error(
'No table data detected in CSV file.',
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
return col_widths
def extend_short_rows_with_empty_cells(columns, parts):
......@@ -304,3 +330,112 @@ def extend_short_rows_with_empty_cells(columns, parts):
for row in part:
if len(row) < columns:
row.extend([(0, 0, 0, [])] * (columns - len(row)))
def list_table(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""
Implement tables whose data is encoded as a uniform two-level bullet list.
For further ideas, see
http://docutils.sf.net/docs/dev/rst/alternatives.html#list-driven-tables
"""
if not content:
error = state_machine.reporter.error(
'The "%s" directive is empty; content required.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
title, messages = make_title(arguments, state, lineno)
node = nodes.Element() # anonymous container for parsing
state.nested_parse(content, content_offset, node)
try:
num_cols, col_widths = check_list_content(
node, name, options, content, lineno, block_text, state_machine)
table_data = [[item.children for item in row_list[0]]
for row_list in node[0]]
header_rows = options.get('header-rows', 0) # default 0
stub_columns = options.get('stub-columns', 0) # default 0
check_table_dimensions(
table_data, header_rows, stub_columns, name, lineno,
block_text, state_machine)
except SystemMessagePropagation, detail:
return [detail.args[0]]
table_node = build_table_from_list(table_data, col_widths,
header_rows, stub_columns)
table_node['classes'] += options.get('class', [])
if title:
table_node.insert(0, title)
return [table_node] + messages
list_table.arguments = (0, 1, 1)
list_table.options = {'header-rows': directives.nonnegative_int,
'stub-columns': directives.nonnegative_int,
'widths': directives.positive_int_list,
'class': directives.class_option}
list_table.content = 1
def check_list_content(node, name, options, content, lineno, block_text,
state_machine):
if len(node) != 1 or not isinstance(node[0], nodes.bullet_list):
error = state_machine.reporter.error(
'Error parsing content block for the "%s" directive: '
'exactly one bullet list expected.' % name,
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
list_node = node[0]
# Check for a uniform two-level bullet list:
for item_index in range(len(list_node)):
item = list_node[item_index]
if len(item) != 1 or not isinstance(item[0], nodes.bullet_list):
error = state_machine.reporter.error(
'Error parsing content block for the "%s" directive: '
'two-level bullet list expected, but row %s does not contain '
'a second-level bullet list.' % (name, item_index + 1),
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
elif item_index:
if len(item[0]) != num_cols:
error = state_machine.reporter.error(
'Error parsing content block for the "%s" directive: '
'uniform two-level bullet list expected, but row %s does '
'not contain the same number of items as row 1 (%s vs %s).'
% (name, item_index + 1, len(item[0]), num_cols),
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
else:
num_cols = len(item[0])
col_widths = get_column_widths(
num_cols, name, options, lineno, block_text, state_machine)
if len(col_widths) != num_cols:
error = state_machine.reporter.error(
'Error parsing "widths" option of the "%s" directive: '
'number of columns does not match the table data (%s vs %s).'
% (name, len(col_widths), num_cols),
nodes.literal_block(block_text, block_text), line=lineno)
raise SystemMessagePropagation(error)
return num_cols, col_widths
def build_table_from_list(table_data, col_widths, header_rows, stub_columns):
table = nodes.table()
tgroup = nodes.tgroup(cols=len(col_widths))
table += tgroup
for col_width in col_widths:
colspec = nodes.colspec(colwidth=col_width)
if stub_columns:
colspec.attributes['stub'] = 1
stub_columns -= 1
tgroup += colspec
rows = []
for row in table_data:
row_node = nodes.row()
for cell in row:
entry = nodes.entry()
entry += cell
row_node += entry
rows.append(row_node)
if header_rows:
thead = nodes.thead()
thead.extend(rows[:header_rows])
tgroup += thead
tbody = nodes.tbody()
tbody.extend(rows[header_rows:])
tgroup += tbody
return table
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 2224 $
# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
# Internationalization details are documented in
......
# Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -42,6 +42,7 @@ directives = {
#'faq': 'questions',
'table (translation required)': 'table',
'csv-table (translation required)': 'csv-table',
'list-table (translation required)': 'list-table',
'meta': 'meta',
#'beeldkaart': 'imagemap',
'beeld': 'image',
......@@ -55,6 +56,8 @@ directives = {
'inhoud': 'contents',
'sectnum': 'sectnum',
'section-numbering': 'sectnum',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#'voetnote': 'footnotes',
#'aanhalings': 'citations',
'teikennotas': 'target-notes',
......
# Author: Marek Blaha
# Contact: mb@dat.cz
# Revision: $Revision: 1.1.4.4 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -43,6 +43,7 @@ directives = {
#'faq': 'questions',
u'table (translation required)': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
u'meta (translation required)': 'meta',
#'imagemap': 'imagemap',
u'image (translation required)': 'image', # obrazek
......@@ -56,6 +57,8 @@ directives = {
u'obsah': 'contents',
u'sectnum (translation required)': 'sectnum',
u'section-numbering (translation required)': 'sectnum',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#'footnotes': 'footnotes',
#'citations': 'citations',
u'target-notes (translation required)': 'target-notes',
......
# Authors: Engelbert Gruber; Felix Wiemann
# Contact: grubert@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -42,6 +42,7 @@ directives = {
#'fragen': 'questions',
'tabelle': 'table',
'csv-tabelle': 'csv-table',
'list-table (translation required)': 'list-table',
'meta': 'meta',
#'imagemap': 'imagemap',
'bild': 'image',
......@@ -59,6 +60,8 @@ directives = {
'kapitel-nummerierung': 'sectnum',
'abschnitts-nummerierung': 'sectnum',
u'linkziel-fu\xdfnoten': 'target-notes',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#u'fu\xdfnoten': 'footnotes',
#'zitate': 'citations',
}
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -41,6 +41,7 @@ directives = {
#'questions': 'questions',
'table': 'table',
'csv-table': 'csv-table',
'list-table': 'list-table',
#'qa': 'questions',
#'faq': 'questions',
'meta': 'meta',
......@@ -56,6 +57,8 @@ directives = {
'contents': 'contents',
'sectnum': 'sectnum',
'section-numbering': 'sectnum',
'header': 'header',
'footer': 'footer',
#'footnotes': 'footnotes',
#'citations': 'citations',
'target-notes': 'target-notes',
......
# Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1.2.5 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3189 $
# Date: $Date: 2005-04-08 05:05:45 +0200 (Fri, 08 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -48,6 +48,7 @@ directives = {
u'tabelo': 'table',
u'tabelo-vdk': 'csv-table', # "valoroj disigitaj per komoj"
u'tabelo-csv': 'csv-table',
u'tabelo-lista': 'list-table',
u'meta': 'meta',
#'imagemap': 'imagemap',
u'bildo': 'image',
......@@ -62,6 +63,8 @@ directives = {
u'enhavo': 'contents',
u'seknum': 'sectnum',
u'sekcia-numerado': 'sectnum',
u'kapsekcio': 'header',
u'piedsekcio': 'footer',
#'footnotes': 'footnotes',
#'citations': 'citations',
u'celaj-notoj': 'target-notes',
......
# -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn
# Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3190 $
# Date: $Date: 2005-04-08 05:06:12 +0200 (Fri, 08 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -50,6 +50,7 @@ directives = {
u'tabla': 'table',
u'tabla-vsc': 'csv-table',
u'tabla-csv': 'csv-table',
u'tabla-lista': 'list-table',
u'meta': 'meta',
#'imagemap': 'imagemap',
u'imagen': 'image',
......@@ -67,6 +68,8 @@ directives = {
u'numeracion-seccion': 'sectnum',
u'numeraci\u00f3n-secci\u00f3n': 'sectnum',
u'notas-destino': 'target-notes',
u'cabecera': 'header',
u'pie': 'footer',
#'footnotes': 'footnotes',
#'citations': 'citations',
u'restructuredtext-test-directive': 'restructuredtext-test-directive'}
......
# Author: Asko Soukka
# Contact: asko.soukka@iki.fi
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -39,6 +39,7 @@ directives = {
u'lainaus': u'pull-quote',
u'taulukko': u'table',
u'csv-taulukko': u'csv-table',
u'list-table (translation required)': 'list-table',
u'compound (translation required)': 'compound',
#u'kysymykset': u'questions',
u'meta': u'meta',
......@@ -53,6 +54,8 @@ directives = {
u'rooli': u'role',
u'sis\u00e4llys': u'contents',
u'kappale': u'sectnum',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#u'alaviitteet': u'footnotes',
#u'viitaukset': u'citations',
u'target-notes (translation required)': u'target-notes'}
......
# Authors: David Goodger; William Dode
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -44,6 +44,7 @@ directives = {
#u'faq': 'questions',
u'tableau': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
u'm\u00E9ta': 'meta',
#u'imagemap (translation required)': 'imagemap',
u'image': 'image',
......@@ -60,6 +61,8 @@ directives = {
u'sectnum': 'sectnum',
u'section-num\u00E9rot\u00E9e': 'sectnum',
u'liens': 'target-notes',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#u'footnotes (translation required)': 'footnotes',
#u'citations (translation required)': 'citations',
}
......
# Author: Nicola Larosa, Lele Gaifax
# Contact: docutils@tekNico.net, lele@seldati.it
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/docs/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
# Beware: the italian translation of the reStructuredText documentation
# at http://docit.bice.dyndns.org/static/ReST, in particular
# http://docit.bice.dyndns.org/static/ReST/ref/rst/directives.html, needs
# to be synced with the content of this file.
"""
Italian-language mappings for language-dependent features of
......@@ -34,14 +34,15 @@ directives = {
'blocco-interpretato': 'parsed-literal',
'rubrica': 'rubric',
'epigrafe': 'epigraph',
'evidenzia': 'highlights',
'pull-quote (translation required)': 'pull-quote',
'compound (translation required)': 'compound',
'punti-salienti': 'highlights',
'estratto-evidenziato': 'pull-quote',
'composito': 'compound',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
'tabella': 'table',
'csv-table (translation required)': 'csv-table',
'tabella-csv': 'csv-table',
'tabella-elenco': 'list-table',
'meta': 'meta',
#'imagemap': 'imagemap',
'immagine': 'image',
......@@ -53,9 +54,12 @@ directives = {
'classe': 'class',
'ruolo': 'role',
'indice': 'contents',
'contenuti': 'contents',
'seznum': 'sectnum',
'sezioni-autonumerate': 'sectnum',
'annota-riferimenti-esterni': 'target-notes',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#'footnotes': 'footnotes',
#'citations': 'citations',
'restructuredtext-test-directive': 'restructuredtext-test-directive'}
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.1.4.4 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -43,6 +43,7 @@ directives = {
#'faq': 'questions',
u'table (translation required)': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
'meta': 'meta',
#'imagemap': 'imagemap',
'imagem': 'image',
......@@ -56,6 +57,8 @@ directives = {
u'\u00EDndice': 'contents',
'numsec': 'sectnum',
u'numera\u00E7\u00E3o-de-se\u00E7\u00F5es': 'sectnum',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#u'notas-de-rorap\u00E9': 'footnotes',
#u'cita\u00E7\u00F5es': 'citations',
u'links-no-rodap\u00E9': 'target-notes',
......
# Author: Roman Suzi
# Contact: rnd@onego.ru
# Revision: $Revision: 1.1.2.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -26,6 +26,7 @@ directives = {
u'compound (translation required)': 'compound',
u'table (translation required)': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
u'\u0441\u044b\u0440\u043e\u0439': u'raw',
u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace',
u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext':
......@@ -60,7 +61,9 @@ directives = {
u'\u0441\u043e\u0432\u0435\u0442': u'hint',
u'\u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435': u'contents',
u'\u0442\u0435\u043c\u0430': u'topic',
u'\u044d\u043f\u0438\u0433\u0440\u0430\u0444': u'epigraph'}
u'\u044d\u043f\u0438\u0433\u0440\u0430\u0444': u'epigraph',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',}
"""Russian name to registered (in directives/__init__.py) directive name
mapping."""
......
# Author: Miroslav Vasko
# Contact: zemiak@zoznam.sk
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -42,6 +42,7 @@ directives = {
#u'faq': 'questions',
u'table (translation required)': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
u'meta': 'meta',
#u'imagemap': 'imagemap',
u'obr\xe1zok': 'image',
......@@ -56,6 +57,8 @@ directives = {
u'\xe8as\x9d': 'sectnum',
u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum',
u'cie\xbeov\xe9-pozn\xe1mky': 'target-notes',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#u'footnotes': 'footnotes',
#u'citations': 'citations',
}
......
# Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -42,6 +42,7 @@ directives = {
# u'vanliga-fr\u00e5gor': 'questions',
u'table (translation required)': 'table',
u'csv-table (translation required)': 'csv-table',
u'list-table (translation required)': 'list-table',
u'meta': 'meta',
# u'bildkarta': 'imagemap', # FIXME: Translation might be too literal.
u'bild': 'image',
......@@ -55,6 +56,8 @@ directives = {
u'inneh\u00e5ll': 'contents',
u'sektionsnumrering': 'sectnum',
u'target-notes (translation required)': 'target-notes',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
# u'fotnoter': 'footnotes',
# u'citeringar': 'citations',
}
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 3184 $
# Date: $Date: 2005-04-07 21:36:11 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -41,6 +41,7 @@ directives = {
#'questions (translation required)': 'questions',
'table (translation required)': 'table',
'csv-table (translation required)': 'csv-table',
'list-table (translation required)': 'list-table',
#'qa (translation required)': 'questions',
#'faq (translation required)': 'questions',
'meta (translation required)': 'meta',
......@@ -56,6 +57,8 @@ directives = {
'contents (translation required)': 'contents',
'sectnum (translation required)': 'sectnum',
'section-numbering (translation required)': 'sectnum',
u'header (translation required)': 'header',
u'footer (translation required)': 'footer',
#'footnotes (translation required)': 'footnotes',
#'citations (translation required)': 'citations',
'target-notes (translation required)': 'target-notes',
......
# Author: Edward Loper
# Contact: edloper@gradient.cis.upenn.edu
# Revision: $Revision: 1.1.4.4 $
# Date: $Date: 2005/01/07 13:26:03 $
# Revision: $Revision: 3155 $
# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -174,7 +174,7 @@ def set_implicit_options(role_fn):
if not hasattr(role_fn, 'options') or role_fn.options is None:
role_fn.options = {'class': directives.class_option}
elif not role_fn.options.has_key('class'):
role_fn.options['class'] = directives.class_option
role_fn.options['class'] = directives.class_option
def register_generic_role(canonical_name, node_class):
"""For roles which simply wrap a given `node_class` around the text."""
......@@ -195,6 +195,7 @@ class GenericRole:
def __call__(self, role, rawtext, text, lineno, inliner,
options={}, content=[]):
set_classes(options)
return [self.node_class(rawtext, utils.unescape(text), **options)], []
......@@ -233,6 +234,7 @@ def generic_custom_role(role, rawtext, text, lineno, inliner,
""""""
# Once nested inline markup is implemented, this and other methods should
# recursively call inliner.nested_parse().
set_classes(options)
return [nodes.inline(rawtext, utils.unescape(text), **options)], []
generic_custom_role.options = {'class': directives.class_option}
......@@ -265,6 +267,7 @@ def pep_reference_role(role, rawtext, text, lineno, inliner,
return [prb], [msg]
# Base URL mainly used by inliner.pep_reference; so this is correct:
ref = inliner.document.settings.pep_base_url + inliner.pep_url % pepnum
set_classes(options)
return [nodes.reference(rawtext, 'PEP ' + utils.unescape(text), refuri=ref,
**options)], []
......@@ -284,6 +287,7 @@ def rfc_reference_role(role, rawtext, text, lineno, inliner,
return [prb], [msg]
# Base URL mainly used by inliner.rfc_reference, so this is correct:
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
set_classes(options)
node = nodes.reference(rawtext, 'RFC ' + utils.unescape(text), refuri=ref,
**options)
return [node], []
......@@ -299,10 +303,11 @@ def raw_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
'an associated format.' % role, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
set_classes(options)
node = nodes.raw(rawtext, utils.unescape(text, 1), **options)
return [node], []
raw_role.options = {'format': directives.class_option}
raw_role.options = {'format': directives.unchanged}
register_canonical_role('raw', raw_role)
......@@ -329,3 +334,14 @@ register_canonical_role('target', unimplemented_role)
# This should remain unimplemented, for testing purposes:
register_canonical_role('restructuredtext-unimplemented-role',
unimplemented_role)
def set_classes(options):
"""
Auxiliary function to set options['classes'] and delete
options['class'].
"""
if options.has_key('class'):
assert not options.has_key('classes')
options['classes'] = options['class']
del options['class']
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:03 $
# Revision: $Revision: 3253 $
# Date: $Date: 2005-04-25 17:08:01 +0200 (Mon, 25 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -365,7 +365,7 @@ class RSTState(StateWS):
textnodes, title_messages = self.inline_text(title, lineno)
titlenode = nodes.title(title, '', *textnodes)
name = normalize_name(titlenode.astext())
section_node['name'] = name
section_node['names'].append(name)
section_node += titlenode
section_node += messages
section_node += title_messages
......@@ -533,7 +533,7 @@ class Inliner:
emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9\x00]"""
email_pattern = r"""
%(emailc)s+(?:\.%(emailc)s+)* # name
@ # at
(?<!\x00)@ # at
%(emailc)s+(?:\.%(emailc)s*)* # host
%(uri_end)s # final URI char
"""
......@@ -787,7 +787,7 @@ class Inliner:
else:
if target:
reference['refuri'] = uri
target['name'] = refname
target['names'].append(refname)
self.document.note_external_target(target)
self.document.note_explicit_target(target, self.parent)
node_list.append(target)
......@@ -829,7 +829,7 @@ class Inliner:
assert len(inlines) == 1
target = inlines[0]
name = normalize_name(target.astext())
target['name'] = name
target['names'].append(name)
self.document.note_explicit_target(target, self.parent)
return before, inlines, remaining, sysmessages
......@@ -1036,10 +1036,10 @@ class Body(RSTState):
pats['alphanum'] = '[a-zA-Z0-9]'
pats['alphanumplus'] = '[a-zA-Z0-9_-]'
pats['enum'] = ('(%(arabic)s|%(loweralpha)s|%(upperalpha)s|%(lowerroman)s'
'|%(upperroman)s)' % enum.sequencepats)
'|%(upperroman)s|#)' % enum.sequencepats)
pats['optname'] = '%(alphanum)s%(alphanumplus)s*' % pats
# @@@ Loosen up the pattern? Allow Unicode?
pats['optarg'] = '(%(alpha)s%(alphanumplus)s*|<%(alphanum)s[^ <>]+>)' % pats
pats['optarg'] = '(%(alpha)s%(alphanumplus)s*|<[^<>]+>)' % pats
pats['shortopt'] = r'(-|\+)%(alphanum)s( ?%(optarg)s)?' % pats
pats['longopt'] = r'(--|/)%(optname)s([ =]%(optarg)s)?' % pats
pats['option'] = r'(%(shortopt)s|%(longopt)s)' % pats
......@@ -1182,7 +1182,10 @@ class Body(RSTState):
raise statemachine.TransitionCorrection('text')
enumlist = nodes.enumerated_list()
self.parent += enumlist
enumlist['enumtype'] = sequence
if sequence == '#':
enumlist['enumtype'] = 'arabic'
else:
enumlist['enumtype'] = sequence
enumlist['prefix'] = self.enum.formatinfo[format].prefix
enumlist['suffix'] = self.enum.formatinfo[format].suffix
if ordinal != 1:
......@@ -1199,7 +1202,9 @@ class Body(RSTState):
input_offset=self.state_machine.abs_line_offset() + 1,
node=enumlist, initial_state='EnumeratedList',
blank_finish=blank_finish,
extra_settings={'lastordinal': ordinal, 'format': format})
extra_settings={'lastordinal': ordinal,
'format': format,
'auto': sequence == '#'})
self.goto_line(newline_offset)
if not blank_finish:
self.parent += self.unindent_warning('Enumerated list')
......@@ -1232,7 +1237,9 @@ class Body(RSTState):
raise ParserError('enumerator format not matched')
text = groupdict[format][self.enum.formatinfo[format].start
:self.enum.formatinfo[format].end]
if expected_sequence:
if text == '#':
sequence = '#'
elif expected_sequence:
try:
if self.enum.sequenceregexps[expected_sequence].match(text):
sequence = expected_sequence
......@@ -1249,10 +1256,13 @@ class Body(RSTState):
break
else: # shouldn't happen
raise ParserError('enumerator sequence not matched')
try:
ordinal = self.enum.converters[sequence](text)
except roman.InvalidRomanNumeralError:
ordinal = None
if sequence == '#':
ordinal = 1
else:
try:
ordinal = self.enum.converters[sequence](text)
except roman.InvalidRomanNumeralError:
ordinal = None
return format, sequence, text, ordinal
def is_enumerated_list_item(self, ordinal, sequence, format):
......@@ -1260,7 +1270,7 @@ class Body(RSTState):
Check validity based on the ordinal value and the second line.
Return true iff the ordinal is valid and the second line is blank,
indented, or starts with the next enumerator.
indented, or starts with the next enumerator or an auto-enumerator.
"""
if ordinal is None:
return None
......@@ -1273,9 +1283,11 @@ class Body(RSTState):
self.state_machine.previous_line()
if not next_line[:1].strip(): # blank or indented
return 1
next_enumerator = self.make_enumerator(ordinal + 1, sequence, format)
next_enumerator, auto_enumerator = self.make_enumerator(
ordinal + 1, sequence, format)
try:
if next_line.startswith(next_enumerator):
if ( next_line.startswith(next_enumerator) or
next_line.startswith(auto_enumerator) ):
return 1
except TypeError:
pass
......@@ -1283,11 +1295,14 @@ class Body(RSTState):
def make_enumerator(self, ordinal, sequence, format):
"""
Construct and return an enumerated list item marker.
Construct and return the next enumerated list item marker, and an
auto-enumerator ("#" instead of the regular enumerator).
Return ``None`` for invalid (out of range) ordinals.
"""
if sequence == 'arabic':
""" #"
if sequence == '#':
enumerator = '#'
elif sequence == 'arabic':
enumerator = str(ordinal)
else:
if sequence.endswith('alpha'):
......@@ -1310,7 +1325,10 @@ class Body(RSTState):
raise ParserError('unknown enumerator sequence: "%s"'
% sequence)
formatinfo = self.enum.formatinfo[format]
return formatinfo.prefix + enumerator + formatinfo.suffix + ' '
next_enumerator = (formatinfo.prefix + enumerator + formatinfo.suffix
+ ' ')
auto_enumerator = formatinfo.prefix + '#' + formatinfo.suffix + ' '
return next_enumerator, auto_enumerator
def field_marker(self, match, context, next_state):
"""Field list item."""
......@@ -1415,14 +1433,20 @@ class Body(RSTState):
delimiter = ' '
firstopt = tokens[0].split('=')
if len(firstopt) > 1:
# "--opt=value" form
tokens[:1] = firstopt
delimiter = '='
elif (len(tokens[0]) > 2
and ((tokens[0].startswith('-')
and not tokens[0].startswith('--'))
or tokens[0].startswith('+'))):
# "-ovalue" form
tokens[:1] = [tokens[0][:2], tokens[0][2:]]
delimiter = ''
if len(tokens) > 1 and (tokens[1].startswith('<')
and tokens[-1].endswith('>')):
# "-o <value1 value2>" form; join all values into one token
tokens[1:] = [' '.join(tokens[1:])]
if 0 < len(tokens) <= 2:
option = nodes.option(optionstring)
option += nodes.option_string(tokens[0], tokens[0])
......@@ -1432,7 +1456,7 @@ class Body(RSTState):
optlist.append(option)
else:
raise MarkupError(
'wrong numer of option tokens (=%s), should be 1 or 2: '
'wrong number of option tokens (=%s), should be 1 or 2: '
'"%s"' % (len(tokens), optionstring),
self.state_machine.abs_line_number() + 1)
return optlist
......@@ -1541,7 +1565,8 @@ class Body(RSTState):
table = self.build_table(tabledata, tableline)
nodelist = [table] + messages
except tableparser.TableMarkupError, detail:
nodelist = self.malformed_table(block, str(detail)) + messages
nodelist = self.malformed_table(
block, ' '.join(detail.args)) + messages
else:
nodelist = messages
return nodelist, blank_finish
......@@ -1633,13 +1658,17 @@ class Body(RSTState):
line=lineno)
return [error]
def build_table(self, tabledata, tableline):
colspecs, headrows, bodyrows = tabledata
def build_table(self, tabledata, tableline, stub_columns=0):
colwidths, headrows, bodyrows = tabledata
table = nodes.table()
tgroup = nodes.tgroup(cols=len(colspecs))
tgroup = nodes.tgroup(cols=len(colwidths))
table += tgroup
for colspec in colspecs:
tgroup += nodes.colspec(colwidth=colspec)
for colwidth in colwidths:
colspec = nodes.colspec(colwidth=colwidth)
if stub_columns:
colspec.attributes['stub'] = 1
stub_columns -= 1
tgroup += colspec
if headrows:
thead = nodes.thead()
tgroup += thead
......@@ -1727,7 +1756,7 @@ class Body(RSTState):
name = name[1:] # autonumber label
footnote['auto'] = 1
if name:
footnote['name'] = name
footnote['names'].append(name)
self.document.note_autofootnote(footnote)
elif name == '*': # auto-symbol
name = ''
......@@ -1735,7 +1764,7 @@ class Body(RSTState):
self.document.note_symbol_footnote(footnote)
else: # manually numbered
footnote += nodes.label('', label)
footnote['name'] = name
footnote['names'].append(name)
self.document.note_footnote(footnote)
if name:
self.document.note_explicit_target(footnote, footnote)
......@@ -1754,7 +1783,7 @@ class Body(RSTState):
citation = nodes.citation('\n'.join(indented))
citation.line = lineno
citation += nodes.label('', label)
citation['name'] = name
citation['names'].append(name)
self.document.note_citation(citation)
self.document.note_explicit_target(citation, citation)
if indented:
......@@ -1790,7 +1819,6 @@ class Body(RSTState):
target_type, data = self.parse_target(block, block_text, lineno)
if target_type == 'refname':
target = nodes.target(block_text, '', refname=normalize_name(data))
target.indirect_reference_name = data
self.add_target(target_name, '', target, lineno)
self.document.note_indirect_target(target)
return target
......@@ -1816,15 +1844,8 @@ class Body(RSTState):
refname = self.is_reference(reference)
if refname:
return 'refname', refname
reference = ''.join([line.strip() for line in block])
if reference.find(' ') == -1:
return 'refuri', unescape(reference)
else:
warning = self.reporter.warning(
'Hyperlink target contains whitespace. Perhaps a footnote '
'was intended?',
nodes.literal_block(block_text, block_text), line=lineno)
return 'malformed', warning
reference = ''.join([''.join(line.split()) for line in block])
return 'refuri', unescape(reference)
def is_reference(self, reference):
match = self.explicit.patterns.reference.match(
......@@ -1837,7 +1858,7 @@ class Body(RSTState):
target.line = lineno
if targetname:
name = normalize_name(unescape(targetname))
target['name'] = name
target['names'].append(name)
if refuri:
uri = self.inliner.adjust_uri(refuri)
if uri:
......@@ -1851,6 +1872,8 @@ class Body(RSTState):
else: # anonymous target
if refuri:
target['refuri'] = refuri
else:
self.document.note_internal_target(target)
target['anonymous'] = 1
self.document.note_anonymous_target(target)
......@@ -1960,7 +1983,8 @@ class Body(RSTState):
directive_fn, option_presets))
except MarkupError, detail:
error = self.reporter.error(
'Error in "%s" directive:\n%s.' % (type_name, detail),
'Error in "%s" directive:\n%s.' % (type_name,
' '.join(detail.args)),
nodes.literal_block(block_text, block_text), line=lineno)
return [error], blank_finish
result = directive_fn(type_name, arguments, options, content, lineno,
......@@ -2071,9 +2095,9 @@ class Body(RSTState):
except KeyError, detail:
return 0, ('unknown option: "%s"' % detail.args[0])
except (ValueError, TypeError), detail:
return 0, ('invalid option value: %s' % detail)
return 0, ('invalid option value: %s' % ' '.join(detail.args))
except utils.ExtensionOptionError, detail:
return 0, ('invalid option data: %s' % detail)
return 0, ('invalid option data: %s' % ' '.join(detail.args))
if blank_finish:
return 1, options
else:
......@@ -2127,13 +2151,13 @@ class Body(RSTState):
re.compile(r"""
\.\.[ ]+ # explicit markup start
_ # target indicator
(?![ ]) # first char. not space
(?![ ]|$) # first char. not space or EOL
""", re.VERBOSE)),
(substitution_def,
re.compile(r"""
\.\.[ ]+ # explicit markup start
\| # substitution indicator
(?![ ]) # first char. not space
(?![ ]|$) # first char. not space or EOL
""", re.VERBOSE)),
(directive,
re.compile(r"""
......@@ -2240,7 +2264,7 @@ class RFC2822Body(Body):
def rfc2822(self, match, context, next_state):
"""RFC2822-style field list item."""
fieldlist = nodes.field_list(CLASS='rfc2822')
fieldlist = nodes.field_list(classes=['rfc2822'])
self.parent += fieldlist
field, blank_finish = self.rfc2822_field(match)
fieldlist += field
......@@ -2347,12 +2371,15 @@ class EnumeratedList(SpecializedBody):
"""Enumerated list item."""
format, sequence, text, ordinal = self.parse_enumerator(
match, self.parent['enumtype'])
if (sequence != self.parent['enumtype'] or
format != self.format or
ordinal != (self.lastordinal + 1) or
not self.is_enumerated_list_item(ordinal, sequence, format)):
if ( format != self.format
or (sequence != '#' and (sequence != self.parent['enumtype']
or self.auto
or ordinal != (self.lastordinal + 1)))
or not self.is_enumerated_list_item(ordinal, sequence, format)):
# different enumeration: new list
self.invalid_input()
if sequence == '#':
self.auto = 1
listitem, blank_finish = self.list_item(match.end())
self.parent += listitem
self.blank_finish = blank_finish
......@@ -2475,7 +2502,7 @@ class SubstitutionDef(Body):
def embedded_directive(self, match, context, next_state):
nodelist, blank_finish = self.directive(match,
alt=self.parent['name'])
alt=self.parent['names'][0])
self.parent += nodelist
if not self.state_machine.at_eof():
self.blank_finish = blank_finish
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:04 $
# Revision: $Revision: 1574 $
# Date: $Date: 2003-07-06 00:38:28 +0200 (Sun, 06 Jul 2003) $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger; Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 1645 $
# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 3129 $
# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -31,9 +31,9 @@ class Reader(standalone.Reader):
config_section_dependencies = ('readers', 'standalone reader')
default_transforms = (references.Substitutions,
references.PropagateTargets,
peps.Headers,
peps.Contents,
references.ChainedTargets,
references.AnonymousHyperlinks,
references.IndirectHyperlinks,
peps.TargetNotes,
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3.2.5 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 3038 $
# Date: $Date: 2005-03-14 17:16:57 +0100 (Mon, 14 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -88,7 +88,7 @@ class DocstringFormattingVisitor(nodes.SparseNodeVisitor):
node['docformat'] = docformat
parser = self.get_parser(docformat)
parser.parse(text, self.document)
for child in self.document.get_children():
for child in self.document.children:
node.append(child)
self.document.current_source = self.document.current_line = None
del self.document[:]
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3.2.5 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 2449 $
# Date: $Date: 2004-07-25 03:45:27 +0200 (Sun, 25 Jul 2004) $
# Copyright: This module has been placed in the public domain.
"""
......
......@@ -3,8 +3,8 @@
"""
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Revision: $Revision: 1.1.4.4 $
:Date: $Date: 2005/01/07 13:26:05 $
:Revision: $Revision: 1881 $
:Date: $Date: 2004-03-24 00:21:11 +0100 (Wed, 24 Mar 2004) $
:Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 3353 $
# Date: $Date: 2005-05-19 02:49:14 +0200 (Thu, 19 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -37,15 +37,26 @@ class Reader(readers.Reader):
'default).',
['--no-doc-info'],
{'dest': 'docinfo_xform', 'action': 'store_false', 'default': 1,
'validator': frontend.validate_boolean}),))
'validator': frontend.validate_boolean}),
('Activate the promotion of lone subsection titles to '
'section subtitles (disabled by default).',
['--section-subtitles'],
{'dest': 'sectsubtitle_xform', 'action': 'store_true', 'default': 0,
'validator': frontend.validate_boolean}),
('Deactivate the promotion of lone subsection titles.',
['--no-section-subtitles'],
{'dest': 'sectsubtitle_xform', 'action': 'store_false',
'validator': frontend.validate_boolean}),
))
config_section = 'standalone reader'
config_section_dependencies = ('readers',)
default_transforms = (references.Substitutions,
references.PropagateTargets,
frontmatter.DocTitle,
frontmatter.SectionSubTitle,
frontmatter.DocInfo,
references.ChainedTargets,
references.AnonymousHyperlinks,
references.IndirectHyperlinks,
references.Footnotes,
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 2299 $
# Date: $Date: 2004-06-17 23:46:50 +0200 (Thu, 17 Jun 2004) $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:05 $
# Revision: $Revision: 3066 $
# Date: $Date: 2005-03-21 18:33:42 +0100 (Mon, 21 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -59,7 +59,7 @@ class Transform:
self.language = languages.get_language(
document.settings.language_code)
"""Language module local to this document."""
def apply(self):
"""Override to apply the transform to the document tree."""
......@@ -164,7 +164,6 @@ class Transformer(TransformSpec):
decorated_list.sort()
self.unknown_reference_resolvers.extend([f[1] for f in decorated_list])
def apply_transforms(self):
"""Apply all of the stored transforms, in priority order."""
self.document.reporter.attach_observer(
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 853 $
# Date: $Date: 2002-10-24 02:51:10 +0200 (Thu, 24 Oct 2002) $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3351 $
# Date: $Date: 2005-05-19 00:27:52 +0200 (Thu, 19 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
Transforms related to the front matter of a document (information
found before the main text):
Transforms related to the front matter of a document or a section
(information found before the main text):
- `DocTitle`: Used to transform a lone top level section's title to
the document title, and promote a remaining lone top-level section's
title to the document subtitle.
- `SectionTitle`: Used to transform a lone subsection into a subtitle.
- `DocInfo`: Used to transform a bibliographic field list into docinfo
elements.
"""
......@@ -23,7 +25,100 @@ from docutils import nodes, utils
from docutils.transforms import TransformError, Transform
class DocTitle(Transform):
class TitlePromoter(Transform):
"""
Abstract base class for DocTitle and SectionSubTitle transforms.
"""
def promote_title(self, node):
"""
Transform the following tree::
<node>
<section>
<title>
...
into ::
<node>
<title>
...
`node` is normally a document.
"""
# `node` must not have a title yet.
assert not (len(node) and isinstance(node[0], nodes.title))
section, index = self.candidate_index(node)
if index is None:
return None
# Transfer the section's attributes to the node:
node.attributes.update(section.attributes)
# setup_child is called automatically for all nodes.
node[:] = (section[:1] # section title
+ node[:index] # everything that was in the
# node before the section
+ section[1:]) # everything that was in the section
assert isinstance(node[0], nodes.title)
return 1
def promote_subtitle(self, node):
"""
Transform the following node tree::
<node>
<title>
<section>
<title>
...
into ::
<node>
<title>
<subtitle>
...
"""
subsection, index = self.candidate_index(node)
if index is None:
return None
subtitle = nodes.subtitle()
# Transfer the subsection's attributes to the new subtitle:
# This causes trouble with list attributes! To do: Write a
# test case which catches direct access to the `attributes`
# dictionary and/or write a test case which shows problems in
# this particular case.
subtitle.attributes.update(subsection.attributes)
# We're losing the subtitle's attributes here! To do: Write a
# test case which shows this behavior.
# Transfer the contents of the subsection's title to the
# subtitle:
subtitle[:] = subsection[0][:]
node[:] = (node[:1] # title
+ [subtitle]
# everything that was before the section:
+ node[1:index]
# everything that was in the subsection:
+ subsection[1:])
return 1
def candidate_index(self, node):
"""
Find and return the promotion candidate and its index.
Return (None, None) if no valid candidate was found.
"""
index = node.first_child_not_matching_class(
nodes.PreBibliographic)
if index is None or len(node) > (index + 1) or \
not isinstance(node[index], nodes.section):
return None, None
else:
return node[index], index
class DocTitle(TitlePromoter):
"""
In reStructuredText_, there is no way to specify a document title
......@@ -50,7 +145,7 @@ class DocTitle(Transform):
Once parsed, it looks like this::
<document>
<section name="top-level title">
<section names="top-level title">
<title>
Top-Level Title
<paragraph>
......@@ -58,7 +153,7 @@ class DocTitle(Transform):
After running the DocTitle transform, we have::
<document name="top-level title">
<document names="top-level title">
<title>
Top-Level Title
<paragraph>
......@@ -85,10 +180,10 @@ class DocTitle(Transform):
After parsing and running the Section Promotion transform, the
result is::
<document name="top-level title">
<document names="top-level title">
<title>
Top-Level Title
<subtitle name="second-level title">
<subtitle names="second-level title">
Second-Level Title
<paragraph>
A paragraph.
......@@ -107,54 +202,47 @@ class DocTitle(Transform):
def apply(self):
if not getattr(self.document.settings, 'doctitle_xform', 1):
return
if self.promote_document_title():
self.promote_document_subtitle()
if self.promote_title(self.document):
self.promote_subtitle(self.document)
def promote_document_title(self):
section, index = self.candidate_index()
if index is None:
return None
document = self.document
# Transfer the section's attributes to the document element (at root):
document.attributes.update(section.attributes)
document[:] = (section[:1] # section title
+ document[:index] # everything that was in the
# document before the section
+ section[1:]) # everything that was in the section
return 1
def promote_document_subtitle(self):
subsection, index = self.candidate_index()
if index is None:
return None
subtitle = nodes.subtitle()
# Transfer the subsection's attributes to the new subtitle:
subtitle.attributes.update(subsection.attributes)
# Transfer the contents of the subsection's title to the subtitle:
subtitle[:] = subsection[0][:]
document = self.document
document[:] = (document[:1] # document title
+ [subtitle]
# everything that was before the section:
+ document[1:index]
# everything that was in the subsection:
+ subsection[1:])
return 1
class SectionSubTitle(TitlePromoter):
def candidate_index(self):
"""
Find and return the promotion candidate and its index.
"""
This works like document subtitles, but for sections. For example, ::
Return (None, None) if no valid candidate was found.
"""
document = self.document
index = document.first_child_not_matching_class(
nodes.PreBibliographic)
if index is None or len(document) > (index + 1) or \
not isinstance(document[index], nodes.section):
return None, None
else:
return document[index], index
<section>
<title>
Title
<section>
<title>
Subtitle
...
is transformed into ::
<section>
<title>
Title
<subtitle>
Subtitle
...
For details refer to the docstring of DocTitle.
"""
default_priority = 350
def apply(self):
if not getattr(self.document.settings, 'sectsubtitle_xform', 1):
return
for section in self.document.traverse(lambda n:
isinstance(n, nodes.section)):
# On our way through the node tree, we are deleting
# sections, but we call self.promote_subtitle for those
# sections nonetheless. To do: Write a test case which
# shows the problem and discuss on Docutils-develop.
self.promote_subtitle(section)
class DocInfo(Transform):
......@@ -189,7 +277,7 @@ class DocInfo(Transform):
Status
<field_body>
<paragraph>
$RCSfile: frontmatter.py,v $
$RCSfile$
...
After running the bibliographic field list transform, the
......@@ -258,11 +346,10 @@ class DocInfo(Transform):
candidate = document[index]
if isinstance(candidate, nodes.field_list):
biblioindex = document.first_child_not_matching_class(
nodes.Titular)
(nodes.Titular, nodes.Decorative))
nodelist = self.extract_bibliographic(candidate)
del document[index] # untransformed field list (candidate)
document[biblioindex:biblioindex] = nodelist
return
def extract_bibliographic(self, field_list):
docinfo = nodes.docinfo()
......@@ -294,7 +381,7 @@ class DocInfo(Transform):
raise TransformError
title = nodes.title(name, labels[canonical])
topics[canonical] = biblioclass(
'', title, CLASS=canonical, *field[1].children)
'', title, classes=[canonical], *field[1].children)
else:
docinfo.append(biblioclass('', *field[1].children))
except TransformError:
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3155 $
# Date: $Date: 2005-04-02 23:57:06 +0200 (Sat, 02 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -45,7 +45,6 @@ class ClassAttribute(Transform):
def apply(self):
pending = self.startnode
class_value = pending.details['class']
parent = pending.parent
child = pending
while parent:
......@@ -55,7 +54,7 @@ class ClassAttribute(Transform):
if (isinstance(element, nodes.Invisible) or
isinstance(element, nodes.system_message)):
continue
element.set_class(class_value)
element['classes'] += pending.details['class']
pending.parent.remove(pending)
return
else:
......
# Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3199 $
# Date: $Date: 2005-04-09 03:32:29 +0200 (Sat, 09 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -54,7 +54,7 @@ class SectNum(Transform):
generated = nodes.generated(
'', (self.prefix + '.'.join(numbers) + self.suffix
+ u'\u00a0' * 3),
CLASS='sectnum')
classes=['sectnum'])
title.insert(0, generated)
title['auto'] = 1
if depth < self.maxdepth:
......@@ -84,14 +84,13 @@ class Contents(Transform):
details = self.startnode.details
if details.has_key('local'):
startnode = self.startnode.parent.parent
# @@@ generate an error if the startnode (directive) not at
# section/document top-level? Drag it up until it is?
while not isinstance(startnode, nodes.Structural):
while not (isinstance(startnode, nodes.section)
or isinstance(startnode, nodes.document)):
# find the ToC root: a direct ancestor of startnode
startnode = startnode.parent
else:
startnode = self.document
self.toc_id = self.startnode.parent['id']
self.toc_id = self.startnode.parent['ids'][0]
if details.has_key('backlinks'):
self.backlinks = details['backlinks']
else:
......@@ -117,15 +116,17 @@ class Contents(Transform):
title = section[0]
auto = title.get('auto') # May be set by SectNum.
entrytext = self.copy_and_filter(title)
reference = nodes.reference('', '', refid=section['id'],
reference = nodes.reference('', '', refid=section['ids'][0],
*entrytext)
ref_id = self.document.set_id(reference)
entry = nodes.paragraph('', '', reference)
item = nodes.list_item('', entry)
if self.backlinks == 'entry':
title['refid'] = ref_id
elif self.backlinks == 'top':
title['refid'] = self.toc_id
if (self.backlinks in ('entry', 'top') and title.next_node(
lambda n: isinstance(n, nodes.reference)) is None):
if self.backlinks == 'entry':
title['refid'] = ref_id
elif self.backlinks == 'top':
title['refid'] = self.toc_id
if level < depth:
subsects = self.build_contents(section, level)
item += subsects
......@@ -133,7 +134,7 @@ class Contents(Transform):
if entries:
contents = nodes.bullet_list('', *entries)
if auto:
contents.set_class('auto-toc')
contents['classes'].append('auto-toc')
return contents
else:
return []
......@@ -148,7 +149,7 @@ class Contents(Transform):
class ContentsFilter(nodes.TreeCopyVisitor):
def get_entry_text(self):
return self.get_tree_copy().get_children()
return self.get_tree_copy().children
def visit_citation_reference(self, node):
raise nodes.SkipNode
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3129 $
# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -46,7 +46,7 @@ class Headers(Transform):
raise DataError('Document tree is empty.')
header = self.document[0]
if not isinstance(header, nodes.field_list) or \
header.get('class') != 'rfc2822':
'rfc2822' not in header['classes']:
raise DataError('Document does not begin with an RFC-2822 '
'header; it is not a PEP.')
pep = None
......@@ -149,10 +149,10 @@ class Contents(Transform):
language = languages.get_language(self.document.settings.language_code)
name = language.labels['contents']
title = nodes.title('', name)
topic = nodes.topic('', title, CLASS='contents')
topic = nodes.topic('', title, classes=['contents'])
name = nodes.fully_normalize_name(name)
if not self.document.has_name(name):
topic['name'] = name
topic['names'].append(name)
self.document.note_implicit_target(topic)
pending = nodes.pending(parts.Contents)
topic += pending
......@@ -244,7 +244,7 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
node.parent.replace(node, mask_email(node))
def visit_field_list(self, node):
if node.hasattr('class') and node['class'] == 'rfc2822':
if 'rfc2822' in node['classes']:
raise nodes.SkipNode
def visit_tgroup(self, node):
......@@ -254,7 +254,7 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
def visit_colspec(self, node):
self.entry += 1
if self.pep_table and self.entry == 2:
node['class'] = 'num'
node['classes'].append('num')
def visit_row(self, node):
self.entry = 0
......@@ -262,7 +262,7 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
def visit_entry(self, node):
self.entry += 1
if self.pep_table and self.entry == 2 and len(node) == 1:
node['class'] = 'num'
node['classes'].append('num')
p = node[0]
if isinstance(p, nodes.paragraph) and len(p) == 1:
text = p.astext()
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3149 $
# Date: $Date: 2005-03-30 22:51:06 +0200 (Wed, 30 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -16,87 +16,75 @@ from docutils import nodes, utils
from docutils.transforms import TransformError, Transform
indices = xrange(sys.maxint)
class ChainedTargets(Transform):
class PropagateTargets(Transform):
"""
Attributes "refuri" and "refname" are migrated from the final direct
target up the chain of contiguous adjacent internal targets, using
`ChainedTargetResolver`.
"""
default_priority = 420
def apply(self):
visitor = ChainedTargetResolver(self.document)
self.document.walk(visitor)
Propagate empty internal targets to the next element.
class ChainedTargetResolver(nodes.SparseNodeVisitor):
Given the following nodes::
"""
Copy reference attributes up the length of a hyperlink target chain.
"Chained targets" are multiple adjacent internal hyperlink targets which
"point to" an external or indirect target. After the transform, all
chained targets will effectively point to the same place.
Given the following ``document`` as input::
<document>
<target id="a" name="a">
<target id="b" name="b">
<target id="c" name="c" refuri="http://chained.external.targets">
<target id="d" name="d">
<paragraph>
I'm known as "d".
<target id="e" name="e">
<target id="id1">
<target id="f" name="f" refname="d">
<target ids="internal1" names="internal1">
<target anonymous="1" ids="id1">
<target ids="internal2" names="internal2">
<paragraph>
This is a test.
``ChainedTargetResolver(document).walk()`` will transform the above into::
PropagateTargets propagates the ids and names of the internal
targets preceding the paragraph to the paragraph itself::
<document>
<target id="a" name="a" refuri="http://chained.external.targets">
<target id="b" name="b" refuri="http://chained.external.targets">
<target id="c" name="c" refuri="http://chained.external.targets">
<target id="d" name="d">
<paragraph>
I'm known as "d".
<target id="e" name="e" refname="d">
<target id="id1" refname="d">
<target id="f" name="f" refname="d">
<target refid="internal1">
<target anonymous="1" refid="id1">
<target refid="internal2">
<paragraph ids="internal2 id1 internal1" names="internal2 internal1">
This is a test.
"""
def unknown_visit(self, node):
pass
default_priority = 260
def visit_target(self, node):
if node.hasattr('refuri'):
attname = 'refuri'
call_if_named = self.document.note_external_target
elif node.hasattr('refname'):
attname = 'refname'
call_if_named = self.document.note_indirect_target
elif node.hasattr('refid'):
attname = 'refid'
call_if_named = None
else:
return
attval = node[attname]
index = node.parent.index(node)
for i in range(index - 1, -1, -1):
sibling = node.parent[i]
if not isinstance(sibling, nodes.target) \
or sibling.hasattr('refuri') \
or sibling.hasattr('refname') \
or sibling.hasattr('refid'):
break
sibling[attname] = attval
if sibling.hasattr('name') and call_if_named:
call_if_named(sibling)
def apply(self):
for target in self.document.internal_targets:
if not (len(target) == 0 and
not (target.attributes.has_key('refid') or
target.attributes.has_key('refuri') or
target.attributes.has_key('refname'))):
continue
next_node = target.next_node(ascend=1)
# Do not move names and ids into Invisibles (we'd lose the
# attributes) or different Targetables (e.g. footnotes).
if (next_node is not None and
((not isinstance(next_node, nodes.Invisible) and
not isinstance(next_node, nodes.Targetable)) or
isinstance(next_node, nodes.target))):
next_node['ids'].extend(target['ids'])
next_node['names'].extend(target['names'])
# Set defaults for next_node.expect_referenced_by_name/id.
if not hasattr(next_node, 'expect_referenced_by_name'):
next_node.expect_referenced_by_name = {}
if not hasattr(next_node, 'expect_referenced_by_id'):
next_node.expect_referenced_by_id = {}
for id in target['ids']:
# Update IDs to node mapping.
self.document.ids[id] = next_node
# If next_node is referenced by id ``id``, this
# target shall be marked as referenced.
next_node.expect_referenced_by_id[id] = target
for name in target['names']:
next_node.expect_referenced_by_name[name] = target
# If there are any expect_referenced_by_... attributes
# in target set, copy them to next_node.
next_node.expect_referenced_by_name.update(
getattr(target, 'expect_referenced_by_name', {}))
next_node.expect_referenced_by_id.update(
getattr(target, 'expect_referenced_by_id', {}))
# Set refid to point to the first former ID of target
# which is now an ID of next_node.
target['refid'] = target['ids'][0]
# Clear ids and names; they have been moved to
# next_node.
target['ids'] = []
target['names'] = []
self.document.note_refid(target)
self.document.note_internal_target(next_node)
class AnonymousHyperlinks(Transform):
......@@ -109,8 +97,8 @@ class AnonymousHyperlinks(Transform):
internal
<reference anonymous="1">
external
<target anonymous="1" id="id1">
<target anonymous="1" id="id2" refuri="http://external">
<target anonymous="1" ids="id1">
<target anonymous="1" ids="id2" refuri="http://external">
Corresponding references are linked via "refid" or resolved via "refuri"::
......@@ -119,8 +107,8 @@ class AnonymousHyperlinks(Transform):
text
<reference anonymous="1" refuri="http://external">
external
<target anonymous="1" id="id1">
<target anonymous="1" id="id2" refuri="http://external">
<target anonymous="1" ids="id1">
<target anonymous="1" ids="id2" refuri="http://external">
"""
default_priority = 440
......@@ -140,16 +128,28 @@ class AnonymousHyperlinks(Transform):
prbid = self.document.set_id(prb)
msg.add_backref(prbid)
ref.parent.replace(ref, prb)
for target in self.document.anonymous_targets:
# Assume that all anonymous targets have been
# referenced to avoid generating lots of
# system_messages.
target.referenced = 1
return
for ref, target in zip(self.document.anonymous_refs,
self.document.anonymous_targets):
if target.hasattr('refuri'):
ref['refuri'] = target['refuri']
ref.resolved = 1
else:
ref['refid'] = target['id']
self.document.note_refid(ref)
target.referenced = 1
while 1:
if target.hasattr('refuri'):
ref['refuri'] = target['refuri']
ref.resolved = 1
break
else:
if not target['ids']:
# Propagated target.
target = self.document.ids[target['refid']]
continue
ref['refid'] = target['ids'][0]
self.document.note_refid(ref)
break
class IndirectHyperlinks(Transform):
......@@ -213,20 +213,24 @@ class IndirectHyperlinks(Transform):
self.resolve_indirect_references(target)
def resolve_indirect_target(self, target):
refname = target['refname']
reftarget_id = self.document.nameids.get(refname)
if not reftarget_id:
# Check the unknown_reference_resolvers
for resolver_function in (self.document.transformer
.unknown_reference_resolvers):
if resolver_function(target):
break
else:
self.nonexistent_indirect_target(target)
return
refname = target.get('refname')
if refname is None:
reftarget_id = target['refid']
else:
reftarget_id = self.document.nameids.get(refname)
if not reftarget_id:
# Check the unknown_reference_resolvers
for resolver_function in \
self.document.transformer.unknown_reference_resolvers:
if resolver_function(target):
break
else:
self.nonexistent_indirect_target(target)
return
reftarget = self.document.ids[reftarget_id]
reftarget.note_referenced_by(id=reftarget_id)
if isinstance(reftarget, nodes.target) \
and not reftarget.resolved and reftarget.hasattr('refname'):
and not reftarget.resolved and reftarget.hasattr('refname'):
if hasattr(target, 'multiply_indirect'):
#and target.multiply_indirect):
#del target.multiply_indirect
......@@ -237,21 +241,23 @@ class IndirectHyperlinks(Transform):
del target.multiply_indirect
if reftarget.hasattr('refuri'):
target['refuri'] = reftarget['refuri']
if target.hasattr('name'):
if target['names']:
self.document.note_external_target(target)
if target.has_key('refid'):
del target['refid']
elif reftarget.hasattr('refid'):
target['refid'] = reftarget['refid']
self.document.note_refid(target)
else:
try:
target['refid'] = reftarget['id']
if reftarget['ids']:
target['refid'] = reftarget_id
self.document.note_refid(target)
except KeyError:
else:
self.nonexistent_indirect_target(target)
return
del target['refname']
if refname is not None:
del target['refname']
target.resolved = 1
reftarget.referenced = 1
def nonexistent_indirect_target(self, target):
if self.document.nameids.has_key(target['refname']):
......@@ -265,18 +271,19 @@ class IndirectHyperlinks(Transform):
def indirect_target_error(self, target, explanation):
naming = ''
if target.hasattr('name'):
naming = '"%s" ' % target['name']
reflist = self.document.refnames.get(target['name'], [])
else:
reflist = self.document.refids.get(target['id'], [])
naming += '(id="%s")' % target['id']
reflist = []
if target['names']:
naming = '"%s" ' % target['names'][0]
for name in target['names']:
reflist.extend(self.document.refnames.get(name, []))
for id in target['ids']:
reflist.extend(self.document.refids.get(id, []))
naming += '(id="%s")' % target['ids'][0]
msg = self.document.reporter.error(
'Indirect hyperlink target %s refers to target "%s", %s.'
% (naming, target['refname'], explanation),
base_node=target)
% (naming, target['refname'], explanation), base_node=target)
msgid = self.document.set_id(msg)
for ref in reflist:
for ref in uniq(reflist):
prb = nodes.problematic(
ref.rawsource, ref.rawsource, refid=msgid)
prbid = self.document.set_id(prb)
......@@ -296,43 +303,34 @@ class IndirectHyperlinks(Transform):
else:
return
attval = target[attname]
if target.hasattr('name'):
name = target['name']
try:
reflist = self.document.refnames[name]
except KeyError, instance:
if target.referenced:
return
msg = self.document.reporter.info(
'Indirect hyperlink target "%s" is not referenced.'
% name, base_node=target)
target.referenced = 1
return
delatt = 'refname'
else:
id = target['id']
try:
reflist = self.document.refids[id]
except KeyError, instance:
if target.referenced:
return
msg = self.document.reporter.info(
'Indirect hyperlink target id="%s" is not referenced.'
% id, base_node=target)
target.referenced = 1
return
delatt = 'refid'
for ref in reflist:
if ref.resolved:
continue
del ref[delatt]
ref[attname] = attval
if not call_if_named or ref.hasattr('name'):
call_method(ref)
ref.resolved = 1
if isinstance(ref, nodes.target):
self.resolve_indirect_references(ref)
target.referenced = 1
for name in target['names']:
reflist = self.document.refnames.get(name, [])
if reflist:
target.note_referenced_by(name=name)
for ref in reflist:
if ref.resolved:
continue
del ref['refname']
ref[attname] = attval
if not call_if_named or ref['names']:
call_method(ref)
ref.resolved = 1
if isinstance(ref, nodes.target):
self.resolve_indirect_references(ref)
for id in target['ids']:
reflist = self.document.refids.get(id, [])
if reflist:
target.note_referenced_by(id=id)
for ref in reflist:
if ref.resolved:
continue
del ref['refid']
ref[attname] = attval
if not call_if_named or ref['names']:
call_method(ref)
ref.resolved = 1
if isinstance(ref, nodes.target):
self.resolve_indirect_references(ref)
class ExternalTargets(Transform):
......@@ -357,74 +355,59 @@ class ExternalTargets(Transform):
def apply(self):
for target in self.document.external_targets:
if target.hasattr('refuri') and target.hasattr('name'):
name = target['name']
if target.hasattr('refuri'):
refuri = target['refuri']
try:
reflist = self.document.refnames[name]
except KeyError, instance:
# @@@ First clause correct???
if not isinstance(target, nodes.target) or target.referenced:
continue
msg = self.document.reporter.info(
'External hyperlink target "%s" is not referenced.'
% name, base_node=target)
target.referenced = 1
continue
for ref in reflist:
if ref.resolved:
continue
del ref['refname']
ref['refuri'] = refuri
ref.resolved = 1
target.referenced = 1
for name in target['names']:
reflist = self.document.refnames.get(name, [])
if reflist:
target.note_referenced_by(name=name)
for ref in reflist:
if ref.resolved:
continue
del ref['refname']
ref['refuri'] = refuri
ref.resolved = 1
class InternalTargets(Transform):
"""
Given::
default_priority = 660
<paragraph>
<reference refname="direct internal">
direct internal
<target id="id1" name="direct internal">
def apply(self):
for target in self.document.internal_targets:
self.resolve_reference_ids(target)
The "refname" attribute is replaced by "refid" linking to the target's
"id"::
def resolve_reference_ids(self, target):
"""
Given::
<paragraph>
<reference refid="id1">
direct internal
<target id="id1" name="direct internal">
"""
<paragraph>
<reference refname="direct internal">
direct internal
<target id="id1" name="direct internal">
default_priority = 660
The "refname" attribute is replaced by "refid" linking to the target's
"id"::
def apply(self):
for target in self.document.internal_targets:
if target.hasattr('refuri') or target.hasattr('refid') \
or not target.hasattr('name'):
continue
name = target['name']
refid = target['id']
try:
reflist = self.document.refnames[name]
except KeyError, instance:
if target.referenced:
continue
msg = self.document.reporter.info(
'Internal hyperlink target "%s" is not referenced.'
% name, base_node=target)
target.referenced = 1
continue
<paragraph>
<reference refid="id1">
direct internal
<target id="id1" name="direct internal">
"""
if target.hasattr('refuri') or target.hasattr('refid') \
or not target['names']:
return
for name in target['names']:
refid = self.document.nameids[name]
reflist = self.document.refnames.get(name, [])
if reflist:
target.note_referenced_by(name=name)
for ref in reflist:
if ref.resolved:
continue
del ref['refname']
ref['refid'] = refid
ref.resolved = 1
target.referenced = 1
class Footnotes(Transform):
......@@ -532,19 +515,17 @@ class Footnotes(Transform):
if not self.document.nameids.has_key(label):
break
footnote.insert(0, nodes.label('', label))
if footnote.hasattr('dupname'):
continue
if footnote.hasattr('name'):
name = footnote['name']
for name in footnote['names']:
for ref in self.document.footnote_refs.get(name, []):
ref += nodes.Text(label)
ref.delattr('refname')
ref['refid'] = footnote['id']
footnote.add_backref(ref['id'])
assert len(footnote['ids']) == len(ref['ids']) == 1
ref['refid'] = footnote['ids'][0]
footnote.add_backref(ref['ids'][0])
self.document.note_refid(ref)
ref.resolved = 1
else:
footnote['name'] = label
if not footnote['names'] and not footnote['dupnames']:
footnote['names'].append(label)
self.document.note_explicit_target(footnote, footnote)
self.autofootnote_labels.append(label)
return startnum
......@@ -577,7 +558,8 @@ class Footnotes(Transform):
footnote = self.document.ids[id]
ref['refid'] = id
self.document.note_refid(ref)
footnote.add_backref(ref['id'])
assert len(ref['ids']) == 1
footnote.add_backref(ref['ids'][0])
ref.resolved = 1
i += 1
......@@ -612,9 +594,10 @@ class Footnotes(Transform):
ref.parent.replace(ref, prb)
break
footnote = self.document.symbol_footnotes[i]
ref['refid'] = footnote['id']
assert len(footnote['ids']) == 1
ref['refid'] = footnote['ids'][0]
self.document.note_refid(ref)
footnote.add_backref(ref['id'])
footnote.add_backref(ref['ids'][0])
i += 1
def resolve_footnotes_and_citations(self):
......@@ -623,24 +606,26 @@ class Footnotes(Transform):
references.
"""
for footnote in self.document.footnotes:
label = footnote['name']
if self.document.footnote_refs.has_key(label):
reflist = self.document.footnote_refs[label]
self.resolve_references(footnote, reflist)
for label in footnote['names']:
if self.document.footnote_refs.has_key(label):
reflist = self.document.footnote_refs[label]
self.resolve_references(footnote, reflist)
for citation in self.document.citations:
label = citation['name']
if self.document.citation_refs.has_key(label):
reflist = self.document.citation_refs[label]
self.resolve_references(citation, reflist)
for label in citation['names']:
if self.document.citation_refs.has_key(label):
reflist = self.document.citation_refs[label]
self.resolve_references(citation, reflist)
def resolve_references(self, note, reflist):
id = note['id']
assert len(note['ids']) == 1
id = note['ids'][0]
for ref in reflist:
if ref.resolved:
continue
ref.delattr('refname')
ref['refid'] = id
note.add_backref(ref['id'])
assert len(ref['ids']) == 1
note.add_backref(ref['ids'][0])
ref.resolved = 1
note.resolved = 1
......@@ -680,7 +665,9 @@ class Substitutions(Transform):
def apply(self):
defs = self.document.substitution_defs
normed = self.document.substitution_names
for refname, refs in self.document.substitution_refs.items():
subreflist = self.document.substitution_refs.items()
subreflist.sort()
for refname, refs in subreflist:
for ref in refs:
key = None
if defs.has_key(refname):
......@@ -715,7 +702,7 @@ class Substitutions(Transform):
and isinstance(parent[index + 1], nodes.Text)):
parent.replace(parent[index + 1],
parent[index + 1].lstrip())
parent.replace(ref, subdef.get_children())
parent.replace(ref, subdef.children)
self.document.substitution_refs = None # release replaced references
......@@ -734,11 +721,12 @@ class TargetNotes(Transform):
notes = {}
nodelist = []
for target in self.document.external_targets:
name = target.get('name')
if not name:
print >>sys.stderr, 'no name on target: %r' % target
continue
refs = self.document.refnames.get(name, [])
names = target['names']
# Only named targets.
assert names
refs = []
for name in names:
refs.extend(self.document.refnames.get(name, []))
if not refs:
continue
footnote = self.make_target_footnote(target, refs, notes)
......@@ -760,14 +748,16 @@ class TargetNotes(Transform):
refuri = target['refuri']
if notes.has_key(refuri): # duplicate?
footnote = notes[refuri]
footnote_name = footnote['name']
assert len(footnote['names']) == 1
footnote_name = footnote['names'][0]
else: # original
footnote = nodes.footnote()
footnote_id = self.document.set_id(footnote)
# Use a colon; they can't be produced inside names by the parser:
footnote_name = 'target_note: ' + footnote_id
# Use uppercase letters and a colon; they can't be
# produced inside names by the parser.
footnote_name = 'TARGET_NOTE: ' + footnote_id
footnote['auto'] = 1
footnote['name'] = footnote_name
footnote['names'] = [footnote_name]
footnote_paragraph = nodes.paragraph()
footnote_paragraph += nodes.reference('', refuri, refuri=refuri)
footnote += footnote_paragraph
......@@ -786,3 +776,11 @@ class TargetNotes(Transform):
reflist.insert(0, nodes.Text(' '))
ref.parent.insert(index, reflist)
return footnote
def uniq(L):
r = []
for item in L:
if not item in r:
r.append(item)
return r
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3186 $
# Date: $Date: 2005-04-07 21:51:45 +0200 (Thu, 07 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -32,19 +32,16 @@ class Decorations(Transform):
default_priority = 820
def apply(self):
header = self.generate_header()
footer = self.generate_footer()
if header or footer:
decoration = nodes.decoration()
decoration += header
decoration += footer
document = self.document
index = document.first_child_not_matching_class(
nodes.PreDecorative)
if index is None:
document += decoration
else:
document[index:index] = [decoration]
header_nodes = self.generate_header()
if header_nodes:
decoration = self.document.get_decoration()
header = decoration.get_header()
header.extend(header_nodes)
footer_nodes = self.generate_footer()
if footer_nodes:
decoration = self.document.get_decoration()
footer = decoration.get_footer()
footer.extend(footer_nodes)
def generate_header(self):
return None
......@@ -79,9 +76,7 @@ class Decorations(Transform):
nodes.reference('', 'reStructuredText', refuri='http://'
'docutils.sourceforge.net/rst.html'),
nodes.Text(' source.\n')])
footer = nodes.footer()
footer += nodes.paragraph('', '', *text)
return footer
return [nodes.paragraph('', '', *text)]
else:
return None
......@@ -97,13 +92,13 @@ class Messages(Transform):
def apply(self):
unfiltered = self.document.transform_messages
threshold = self.document.reporter['writer'].report_level
threshold = self.document.reporter.report_level
messages = []
for msg in unfiltered:
if msg['level'] >= threshold and not msg.parent:
messages.append(msg)
if messages:
section = nodes.section(CLASS='system-messages')
section = nodes.section(classes=['system-messages'])
# @@@ get this from the language module?
section += nodes.title('', 'Docutils System Messages')
section += messages
......@@ -130,7 +125,7 @@ class SystemMessageFilterVisitor(nodes.SparseNodeVisitor):
pass
def visit_system_message(self, node):
if node['level'] < self.document.reporter['writer'].report_level:
if node['level'] < self.document.reporter.report_level:
node.parent.remove(node)
......@@ -167,6 +162,21 @@ class FinalChecks(Transform):
if self.document.settings.expose_internals:
visitor = InternalAttributeExposer(self.document)
self.document.walk(visitor)
# *After* resolving all references, check for unreferenced
# targets:
for target in self.document.traverse():
if isinstance(target, nodes.target) and not target.referenced:
if target['names']:
naming = target['names'][0]
elif target['ids']:
naming = target['ids'][0]
else:
# Hack: Propagated targets always have their refid
# attribute set.
naming = target['refid']
self.document.reporter.info(
'Hyperlink target "%s" is not referenced.'
% naming, base_node=target)
class FinalCheckVisitor(nodes.SparseNodeVisitor):
......@@ -206,7 +216,7 @@ class FinalCheckVisitor(nodes.SparseNodeVisitor):
else:
del node['refname']
node['refid'] = id
self.document.ids[id].referenced = 1
self.document.ids[id].note_referenced_by(id=id)
node.resolved = 1
visit_footnote_reference = visit_citation_reference = visit_reference
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:02 $
# Revision: $Revision: 3253 $
# Date: $Date: 2005-04-25 17:08:01 +0200 (Mon, 25 Apr 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -13,6 +13,7 @@ __docformat__ = 'reStructuredText'
import sys
import os
import os.path
import warnings
from types import StringType, UnicodeType
from docutils import ApplicationError, DataError
from docutils import frontend, nodes
......@@ -39,27 +40,14 @@ class Reporter:
There is typically one Reporter object per process. A Reporter object is
instantiated with thresholds for reporting (generating warnings) and
halting processing (raising exceptions), a switch to turn debug output on
or off, and an I/O stream for warnings. These are stored in the default
reporting category, '' (zero-length string).
Multiple reporting categories [#]_ may be set, each with its own reporting
and halting thresholds, debugging switch, and warning stream
(collectively a `ConditionSet`). Categories are hierarchical dotted-name
strings that look like attribute references: 'spam', 'spam.eggs',
'neeeow.wum.ping'. The 'spam' category is the ancestor of
'spam.bacon.eggs'. Unset categories inherit stored conditions from their
closest ancestor category that has been set.
When a system message is generated, the stored conditions from its
category (or ancestor if unset) are retrieved. The system message level
is compared to the thresholds stored in the category, and a warning or
error is generated as appropriate. Debug messages are produced iff the
stored debug switch is on. Message output is sent to the stored warning
stream if not set to ''.
The default category is '' (empty string). By convention, Writers should
retrieve reporting conditions from the 'writer' category (which, unless
explicitly set, defaults to the conditions of the default category).
or off, and an I/O stream for warnings. These are stored as instance
attributes.
When a system message is generated, its level is compared to the stored
thresholds, and a warning or error is generated as appropriate. Debug
messages are produced iff the stored debug switch is on, independently of
other thresholds. Message output is sent to the stored warning stream if
not set to ''.
The Reporter class also employs a modified form of the "Observer" pattern
[GoF95]_ to track system messages generated. The `attach_observer` method
......@@ -67,9 +55,6 @@ class Reporter:
accepts system messages. The observer can be removed with
`detach_observer`, and another added in its place.
.. [#] The concept of "categories" was inspired by the log4j project:
http://jakarta.apache.org/log4j/.
.. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
1995.
......@@ -81,10 +66,7 @@ class Reporter:
def __init__(self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace'):
"""
Initialize the `ConditionSet` forthe `Reporter`'s default category.
:Parameters:
- `source`: The path to or description of the source data.
- `report_level`: The level at or above which warning output will
be sent to `stream`.
......@@ -101,6 +83,23 @@ class Reporter:
self.source = source
"""The path to or description of the source data."""
self.encoding = encoding
"""The character encoding for the stderr output."""
self.error_handler = error_handler
"""The character encoding error handler."""
self.debug_flag = debug
"""Show debug (level=0) system messages?"""
self.report_level = report_level
"""The level at or above which warning output will be sent
to `self.stream`."""
self.halt_level = halt_level
"""The level at or above which `SystemMessage` exceptions
will be raised, halting execution."""
if stream is None:
stream = sys.stderr
elif type(stream) in (StringType, UnicodeType):
......@@ -111,15 +110,8 @@ class Reporter:
elif type(stream) == UnicodeType:
stream = open(stream.encode(), 'w')
self.encoding = encoding
"""The character encoding for the stderr output."""
self.error_handler = error_handler
"""The character encoding error handler."""
self.categories = {'': ConditionSet(debug, report_level, halt_level,
stream)}
"""Mapping of category names to conditions. Default category is ''."""
self.stream = stream
"""Where warning output is sent."""
self.observers = []
"""List of bound methods or functions to call with each system_message
......@@ -130,23 +122,15 @@ class Reporter:
def set_conditions(self, category, report_level, halt_level,
stream=None, debug=0):
warnings.warn('docutils.utils.Reporter.set_conditions deprecated; '
'set attributes via configuration settings or directly',
DeprecationWarning, stacklevel=2)
self.report_level = report_level
self.halt_level = halt_level
if stream is None:
stream = sys.stderr
self.categories[category] = ConditionSet(debug, report_level,
halt_level, stream)
def unset_conditions(self, category):
if category and self.categories.has_key(category):
del self.categories[category]
__delitem__ = unset_conditions
def get_conditions(self, category):
while not self.categories.has_key(category):
category = category[:category.rfind('.') + 1][:-1]
return self.categories[category]
__getitem__ = get_conditions
self.stream = stream
self.debug = debug
def attach_observer(self, observer):
"""
......@@ -169,9 +153,6 @@ class Reporter:
Raise an exception or generate a warning if appropriate.
"""
attributes = kwargs.copy()
category = kwargs.get('category', '')
if kwargs.has_key('category'):
del attributes['category']
if kwargs.has_key('base_node'):
source, line = get_source_line(kwargs['base_node'])
del attributes['base_node']
......@@ -183,16 +164,13 @@ class Reporter:
msg = nodes.system_message(message, level=level,
type=self.levels[level],
*children, **attributes)
debug, report_level, halt_level, stream = self[category].astuple()
if (level >= report_level or debug and level == 0) and stream:
if self.stream and (level >= self.report_level
or self.debug_flag and level == 0):
msgtext = msg.astext().encode(self.encoding, self.error_handler)
if category:
print >>stream, msgtext, '[%s]' % category
else:
print >>stream, msgtext
if level >= halt_level:
print >>self.stream, msgtext
if level >= self.halt_level:
raise SystemMessage(msg, level)
if level > 0 or debug:
if level > 0 or self.debug_flag:
self.notify_observers(msg)
self.max_level = max(level, self.max_level)
return msg
......@@ -203,7 +181,8 @@ class Reporter:
effect on the processing. Level-0 system messages are handled
separately from the others.
"""
return self.system_message(0, *args, **kwargs)
if self.debug_flag:
return self.system_message(0, *args, **kwargs)
def info(self, *args, **kwargs):
"""
......@@ -235,25 +214,6 @@ class Reporter:
return self.system_message(4, *args, **kwargs)
class ConditionSet:
"""
A set of two thresholds (`report_level` & `halt_level`), a switch
(`debug`), and an I/O stream (`stream`), corresponding to one `Reporter`
category.
"""
def __init__(self, debug, report_level, halt_level, stream):
self.debug = debug
self.report_level = report_level
self.halt_level = halt_level
self.stream = stream
def astuple(self):
return (self.debug, self.report_level, self.halt_level,
self.stream)
class ExtensionOptionError(DataError): pass
class BadOptionError(ExtensionOptionError): pass
class BadOptionDataError(ExtensionOptionError): pass
......@@ -346,7 +306,7 @@ def assemble_option_dict(option_list, options_spec):
options[name] = convertor(value)
except (ValueError, TypeError), detail:
raise detail.__class__('(option: "%s"; value: %r)\n%s'
% (name, value, detail))
% (name, value, ' '.join(detail.args)))
return options
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 2321 $
# Date: $Date: 2004-06-20 14:28:08 +0200 (Sun, 20 Jun 2004) $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 2223 $
# Date: $Date: 2004-06-05 21:32:15 +0200 (Sat, 05 Jun 2004) $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.7 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3367 $
# Date: $Date: 2005-05-26 02:44:13 +0200 (Thu, 26 May 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -63,6 +63,20 @@ class Writer(writers.Writer):
['--initial-header-level'],
{'choices': '1 2 3 4 5 6'.split(), 'default': '1',
'metavar': '<level>'}),
('Specify the maximum width (in characters) for one-column field '
'names. Longer field names will span an entire row of the table '
'used to render the field list. Default is 14 characters. '
'Use 0 for "no limit".',
['--field-name-limit'],
{'default': 14, 'metavar': '<level>',
'validator': frontend.validate_nonnegative_int}),
('Specify the maximum width (in characters) for options in option '
'lists. Longer options will span an entire row of the table used '
'to render the option list. Default is 14 characters. '
'Use 0 for "no limit".',
['--option-limit'],
{'default': 14, 'metavar': '<level>',
'validator': frontend.validate_nonnegative_int}),
('Format for footnote references: one of "superscript" or '
'"brackets". Default is "brackets".',
['--footnote-references'],
......@@ -87,7 +101,12 @@ class Writer(writers.Writer):
('Omit the XML declaration. Use with caution.',
['--no-xml-declaration'],
{'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
'validator': frontend.validate_boolean}),))
'validator': frontend.validate_boolean}),
('Scramble email addresses to confuse harvesters. '
'For example, "abc@example.org" will become '
'``<a href="mailto:%61%62%63%40...">abc at example dot org</a>``.',
['--cloak-email-addresses'],
{'action': 'store_true', 'validator': frontend.validate_boolean}),))
relative_path_settings = ('stylesheet_path',)
......@@ -99,10 +118,9 @@ class Writer(writers.Writer):
self.translator_class = HTMLTranslator
def translate(self):
visitor = self.translator_class(self.document)
self.visitor = visitor = self.translator_class(self.document)
self.document.walkabout(visitor)
self.output = visitor.astext()
self.visitor = visitor
for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
'body_pre_docinfo', 'docinfo', 'body', 'fragment',
'body_suffix'):
......@@ -111,7 +129,9 @@ class Writer(writers.Writer):
def assemble_parts(self):
writers.Writer.assemble_parts(self)
for part in ('title', 'subtitle', 'docinfo', 'body', 'header',
'footer', 'meta', 'stylesheet', 'fragment'):
'footer', 'meta', 'stylesheet', 'fragment',
'html_prolog', 'html_head', 'html_title', 'html_subtitle',
'html_body'):
self.parts[part] = ''.join(getattr(self.visitor, part))
......@@ -163,16 +183,15 @@ class HTMLTranslator(nodes.NodeVisitor):
' PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
' "http://www.w3.org/TR/xhtml1/DTD/'
'xhtml1-transitional.dtd">\n')
html_head = ('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="%s" '
'lang="%s">\n<head>\n')
content_type = ('<meta http-equiv="Content-Type" content="text/html; '
'charset=%s" />\n')
head_prefix_template = ('<html xmlns="http://www.w3.org/1999/xhtml"'
' xml:lang="%s" lang="%s">\n<head>\n')
content_type = ('<meta http-equiv="Content-Type"'
' content="text/html; charset=%s" />\n')
generator = ('<meta name="generator" content="Docutils %s: '
'http://docutils.sourceforge.net/" />\n')
stylesheet_link = '<link rel="stylesheet" href="%s" type="text/css" />\n'
embedded_stylesheet = '<style type="text/css">\n\n%s\n</style>\n'
named_tags = {'a': 1, 'applet': 1, 'form': 1, 'frame': 1, 'iframe': 1,
'img': 1, 'map': 1}
named_tags = ['a', 'applet', 'form', 'frame', 'iframe', 'img', 'map']
words_and_spaces = re.compile(r'\S+| +|\n')
def __init__(self, document):
......@@ -182,14 +201,17 @@ class HTMLTranslator(nodes.NodeVisitor):
self.language = languages.get_language(lcode)
self.meta = [self.content_type % settings.output_encoding,
self.generator % docutils.__version__]
self.head_prefix = [
self.doctype,
self.html_head % (lcode, lcode)]
self.head_prefix.extend(self.meta)
self.head_prefix = []
self.html_prolog = []
if settings.xml_declaration:
self.head_prefix.insert(0, self.xml_declaration
self.head_prefix.append(self.xml_declaration
% settings.output_encoding)
self.head = []
# encoding not interpolated:
self.html_prolog.append(self.xml_declaration)
self.head_prefix.extend([self.doctype,
self.head_prefix_template % (lcode, lcode)])
self.html_prolog.append(self.doctype)
self.head = self.meta[:]
if settings.embed_stylesheet:
stylesheet = utils.get_stylesheet_reference(settings,
os.path.join(os.getcwd(), 'dummy'))
......@@ -199,7 +221,8 @@ class HTMLTranslator(nodes.NodeVisitor):
else:
stylesheet = utils.get_stylesheet_reference(settings)
if stylesheet:
self.stylesheet = [self.stylesheet_link % stylesheet]
self.stylesheet = [self.stylesheet_link
% self.encode(stylesheet)]
else:
self.stylesheet = []
self.body_prefix = ['</head>\n<body>\n']
......@@ -215,7 +238,7 @@ class HTMLTranslator(nodes.NodeVisitor):
# A heterogenous stack used in conjunction with the tree traversal.
# Make sure that the pops correspond to the pushes:
self.context = []
self.topic_class = ''
self.topic_classes = []
self.colspecs = []
self.compact_p = 1
self.compact_simple = None
......@@ -225,7 +248,12 @@ class HTMLTranslator(nodes.NodeVisitor):
self.subtitle = []
self.header = []
self.footer = []
self.html_head = [self.content_type] # charset not interpolated
self.html_title = []
self.html_subtitle = []
self.html_body = []
self.in_document_title = 0
self.in_mailto = 0
def astext(self):
return ''.join(self.head_prefix + self.head
......@@ -245,6 +273,20 @@ class HTMLTranslator(nodes.NodeVisitor):
text = text.replace(u'\u00a0', "&nbsp;")
return text
def cloak_mailto(self, uri):
"""Try to hide a mailto: URL from harvesters."""
addr = uri.split(':', 1)[1]
if '?' in addr:
addr, query = addr.split('?', 1)
query = '?' + query
else:
query = ''
escaped = ['%%%02X' % ord(c) for c in addr]
return 'mailto:%s%s' % (''.join(escaped), query)
def cloak_email(self, addr):
return addr.replace('@', ' at ').replace('.', ' dot ')
def attval(self, text,
whitespace=re.compile('[\n\r\t\v\f]')):
"""Cleanse, HTML encode, and return attribute value text."""
......@@ -256,17 +298,21 @@ class HTMLTranslator(nodes.NodeVisitor):
are extracted), tag name, and optional attributes.
"""
tagname = tagname.lower()
prefix = []
atts = {}
for (name, value) in attributes.items():
atts[name.lower()] = value
for att in ('class',): # append to node attribute
if node.has_key(att) or atts.has_key(att):
atts[att] = \
(node.get(att, '') + ' ' + atts.get(att, '')).strip()
for att in ('id',): # node attribute overrides
if node.has_key(att):
atts[att] = node[att]
if atts.has_key('id') and self.named_tags.has_key(tagname):
classes = node.get('classes', [])
if atts.has_key('class'):
classes.append(atts['class'])
if classes:
atts['class'] = ' '.join(classes)
assert not atts.has_key('id')
if node.get('ids'):
atts['id'] = node['ids'][0]
for id in node['ids'][1:]:
prefix.append('<span id="%s"></span>' % id)
if atts.has_key('id') and tagname in self.named_tags:
atts['name'] = atts['id'] # for compatibility with old browsers
attlist = atts.items()
attlist.sort()
......@@ -285,19 +331,23 @@ class HTMLTranslator(nodes.NodeVisitor):
except TypeError: # for Python 2.1 compatibility:
uval = unicode(str(value))
parts.append('%s="%s"' % (name.lower(), self.attval(uval)))
return '<%s%s>%s' % (' '.join(parts), infix, suffix)
return ''.join(prefix) + '<%s%s>' % (' '.join(parts), infix) + suffix
def emptytag(self, node, tagname, suffix='\n', **attributes):
"""Construct and return an XML-compatible empty tag."""
return self.starttag(node, tagname, suffix, infix=' /', **attributes)
def set_first_last(self, node):
if len(node):
node[0].set_class('first')
node[-1].set_class('last')
children = [n for n in node if not isinstance(n, nodes.Invisible)]
if children:
children[0]['classes'].append('first')
children[-1]['classes'].append('last')
def visit_Text(self, node):
self.body.append(self.encode(node.astext()))
text = node.astext()
if self.in_mailto and self.settings.cloak_email_addresses:
text = self.cloak_email(text)
self.body.append(self.encode(text))
def depart_Text(self, node):
pass
......@@ -389,7 +439,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.compact_p = None
self.compact_simple = (self.settings.compact_lists and
(self.compact_simple
or self.topic_class == 'contents'
or self.topic_classes == ['contents']
or self.check_simple_list(node)))
if self.compact_simple and not old_compact_simple:
atts['class'] = 'simple'
......@@ -425,14 +475,9 @@ class HTMLTranslator(nodes.NodeVisitor):
'</tbody>\n</table>\n')
def visit_citation_reference(self, node):
href = ''
if node.has_key('refid'):
href = '#' + node['refid']
elif node.has_key('refname'):
href = '#' + self.document.nameids[node['refname']]
self.body.append(self.starttag(node, 'a', '[',
CLASS='citation-reference',
**(href and {'href': href} or {})))
href = '#' + node['refid']
self.body.append(self.starttag(
node, 'a', '[', CLASS='citation-reference', href=href))
def depart_citation_reference(self, node):
self.body.append(']</a>')
......@@ -446,6 +491,8 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_colspec(self, node):
self.colspecs.append(node)
# "stubs" list is an attribute of the tgroup element:
node.parent.stubs.append(node.attributes.get('stub'))
def depart_colspec(self, node):
pass
......@@ -470,10 +517,10 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_compound(self, node):
self.body.append(self.starttag(node, 'div', CLASS='compound'))
if len(node) > 1:
node[0].set_class('compound-first')
node[-1].set_class('compound-last')
node[0]['classes'].append('compound-first')
node[-1]['classes'].append('compound-last')
for child in node[1:-1]:
child.set_class('compound-middle')
child['classes'].append('compound-middle')
def depart_compound(self, node):
self.body.append('</div>\n')
......@@ -562,9 +609,9 @@ class HTMLTranslator(nodes.NodeVisitor):
% self.language.labels[name])
if len(node):
if isinstance(node[0], nodes.Element):
node[0].set_class('first')
node[0]['classes'].append('first')
if isinstance(node[-1], nodes.Element):
node[-1].set_class('last')
node[-1]['classes'].append('last')
def depart_docinfo_item(self):
self.body.append('</td></tr>\n')
......@@ -579,12 +626,17 @@ class HTMLTranslator(nodes.NodeVisitor):
# empty or untitled document?
if not len(node) or not isinstance(node[0], nodes.title):
# for XHTML conformance, modulo IE6 appeasement:
self.head.insert(0, '<title></title>\n')
self.head.append('<title></title>\n')
def depart_document(self, node):
self.fragment.extend(self.body)
self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
self.body_suffix.insert(0, '</div>\n')
# skip content-type meta tag with interpolated charset value:
self.html_head.extend(self.head[1:])
self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+ self.docinfo + self.body
+ self.body_suffix[:-1])
def visit_emphasis(self, node):
self.body.append('<em>')
......@@ -593,15 +645,24 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('</em>')
def visit_entry(self, node):
atts = {'class': []}
if isinstance(node.parent.parent, nodes.thead):
atts['class'].append('head')
if node.parent.parent.parent.stubs[node.parent.column]:
# "stubs" list is an attribute of the tgroup element
atts['class'].append('stub')
if atts['class']:
tagname = 'th'
atts['class'] = ' '.join(atts['class'])
else:
tagname = 'td'
atts = {}
del atts['class']
node.parent.column += 1
if node.has_key('morerows'):
atts['rowspan'] = node['morerows'] + 1
if node.has_key('morecols'):
atts['colspan'] = node['morecols'] + 1
node.parent.column += node['morecols']
self.body.append(self.starttag(node, tagname, '', **atts))
self.context.append('</%s>\n' % tagname.lower())
if len(node) == 0: # empty cell
......@@ -629,7 +690,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.compact_p = None
self.compact_simple = (self.settings.compact_lists and
(self.compact_simple
or self.topic_class == 'contents'
or self.topic_classes == ['contents']
or self.check_simple_list(node)))
if self.compact_simple and not old_compact_simple:
atts['class'] = (atts.get('class', '') + ' simple').strip()
......@@ -675,7 +736,8 @@ class HTMLTranslator(nodes.NodeVisitor):
atts['class'] = 'docinfo-name'
else:
atts['class'] = 'field-name'
if len(node.astext()) > 14:
if ( self.settings.field_name_limit
and len(node.astext()) > self.settings.field_name_limit):
atts['colspan'] = 2
self.context.append('</tr>\n<tr><td>&nbsp;</td>')
else:
......@@ -690,6 +752,8 @@ class HTMLTranslator(nodes.NodeVisitor):
atts = {'class': 'figure'}
if node.get('width'):
atts['style'] = 'width: %spx' % node['width']
if node.get('align'):
atts['align'] = node['align']
self.body.append(self.starttag(node, 'div', **atts))
def depart_figure(self, node):
......@@ -700,9 +764,10 @@ class HTMLTranslator(nodes.NodeVisitor):
def depart_footer(self, node):
start = self.context.pop()
footer = (['<hr class="docutils footer" />\n',
self.starttag(node, 'div', CLASS='footer')]
+ self.body[start:] + ['</div>\n'])
footer = [self.starttag(node, 'div', CLASS='footer'),
'<hr class="footer" />\n']
footer.extend(self.body[start:])
footer.append('\n</div>\n')
self.footer.extend(footer)
self.body_suffix[:0] = footer
del self.body[start:]
......@@ -718,12 +783,13 @@ class HTMLTranslator(nodes.NodeVisitor):
def footnote_backrefs(self, node):
backlinks = []
if self.settings.footnote_backlinks and node.hasattr('backrefs'):
backrefs = node['backrefs']
backrefs = node['backrefs']
if self.settings.footnote_backlinks and backrefs:
if len(backrefs) == 1:
self.context.append('')
self.context.append('<a class="fn-backref" href="#%s" '
'name="%s">' % (backrefs[0], node['id']))
self.context.append(
'<a class="fn-backref" href="#%s" name="%s">'
% (backrefs[0], node['ids'][0]))
else:
i = 1
for backref in backrefs:
......@@ -731,41 +797,34 @@ class HTMLTranslator(nodes.NodeVisitor):
% (backref, i))
i += 1
self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
self.context.append('<a name="%s">' % node['id'])
self.context.append('<a name="%s">' % node['ids'][0])
else:
self.context.append('')
self.context.append('<a name="%s">' % node['id'])
self.context.append('<a name="%s">' % node['ids'][0])
# If the node does not only consist of a label.
if len(node) > 1:
# If there are preceding backlinks, we do not set class
# 'first', because we need to retain the top-margin.
if not backlinks:
node[1].set_class('first')
node[-1].set_class('last')
node[1]['classes'].append('first')
node[-1]['classes'].append('last')
def depart_footnote(self, node):
self.body.append('</td></tr>\n'
'</tbody>\n</table>\n')
def visit_footnote_reference(self, node):
href = ''
if node.has_key('refid'):
href = '#' + node['refid']
elif node.has_key('refname'):
href = '#' + self.document.nameids[node['refname']]
href = '#' + node['refid']
format = self.settings.footnote_references
if format == 'brackets':
suffix = '['
self.context.append(']')
elif format == 'superscript':
else:
assert format == 'superscript'
suffix = '<sup>'
self.context.append('</sup>')
else: # shouldn't happen
suffix = '???'
self.content.append('???')
self.body.append(self.starttag(node, 'a', suffix,
CLASS='footnote-reference',
**(href and {'href': href} or {})))
CLASS='footnote-reference', href=href))
def depart_footnote_reference(self, node):
self.body.append(self.context.pop() + '</a>')
......@@ -783,9 +842,9 @@ class HTMLTranslator(nodes.NodeVisitor):
start = self.context.pop()
header = [self.starttag(node, 'div', CLASS='header')]
header.extend(self.body[start:])
header.append('<hr class="docutils header"/>\n</div>\n')
header.append('\n<hr class="header"/>\n</div>\n')
self.body_prefix.extend(header)
self.header = header
self.header.extend(header)
del self.body[start:]
def visit_hint(self, node):
......@@ -795,9 +854,9 @@ class HTMLTranslator(nodes.NodeVisitor):
self.depart_admonition()
def visit_image(self, node):
atts = node.attributes.copy()
if atts.has_key('class'):
del atts['class'] # prevent duplication with node attrs
atts = node.non_default_attributes()
if atts.has_key('classes'):
del atts['classes'] # prevent duplication with node attrs
atts['src'] = atts['uri']
del atts['uri']
if atts.has_key('scale'):
......@@ -832,9 +891,8 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.emptytag(node, 'img', '', **atts))
def image_div_atts(self, image_node):
div_atts = {'class': 'image'}
if image_node.attributes.has_key('class'):
div_atts['class'] += ' ' + image_node.attributes['class']
div_atts = {}
div_atts['class'] = ' '.join(['image'] + image_node['classes'])
if image_node.attributes.has_key('align'):
div_atts['align'] = self.attval(image_node.attributes['align'])
div_atts['class'] += ' align-%s' % div_atts['align']
......@@ -885,14 +943,15 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_list_item(self, node):
self.body.append(self.starttag(node, 'li', ''))
if len(node):
node[0].set_class('first')
node[0]['classes'].append('first')
def depart_list_item(self, node):
self.body.append('</li>\n')
def visit_literal(self, node):
"""Process text to prevent tokens from wrapping."""
self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal'))
self.body.append(
self.starttag(node, 'tt', '', CLASS='docutils literal'))
text = node.astext()
for token in self.words_and_spaces.findall(text):
if token.strip():
......@@ -916,7 +975,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('\n</pre>\n')
def visit_meta(self, node):
meta = self.emptytag(node, 'meta', **node.attributes)
meta = self.emptytag(node, 'meta', **node.non_default_attributes())
self.add_meta(meta)
def depart_meta(self, node):
......@@ -935,8 +994,10 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_option(self, node):
if self.context[-1]:
self.body.append(', ')
self.body.append(self.starttag(node, 'span', '', CLASS='option'))
def depart_option(self, node):
self.body.append('</span>')
self.context[-1] += 1
def visit_option_argument(self, node):
......@@ -948,12 +1009,14 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_option_group(self, node):
atts = {}
if len(node.astext()) > 14:
if ( self.settings.option_limit
and len(node.astext()) > self.settings.option_limit):
atts['colspan'] = 2
self.context.append('</tr>\n<tr><td>&nbsp;</td>')
else:
self.context.append('')
self.body.append(self.starttag(node, 'td', **atts))
self.body.append(
self.starttag(node, 'td', CLASS='option-group', **atts))
self.body.append('<kbd>')
self.context.append(0) # count number of options
......@@ -980,10 +1043,10 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('</tr>\n')
def visit_option_string(self, node):
self.body.append(self.starttag(node, 'span', '', CLASS='option'))
pass
def depart_option_string(self, node):
self.body.append('</span>')
pass
def visit_organization(self, node):
self.visit_docinfo_item(node, 'organization')
......@@ -999,12 +1062,16 @@ class HTMLTranslator(nodes.NodeVisitor):
isinstance(node.parent, nodes.compound)):
# Never compact paragraphs in document or compound.
return 0
if ((node.attributes in ({}, {'class': 'first'}, {'class': 'last'},
{'class': 'first last'})) and
(self.compact_simple or
self.compact_p and (len(node.parent) == 1 or
len(node.parent) == 2 and
isinstance(node.parent[0], nodes.label)))):
for key, value in node.attlist():
if (node.is_not_default(key) and
not (key == 'classes' and value in
([], ['first'], ['last'], ['first', 'last']))):
# Attribute which needs to survive.
return 0
if (self.compact_simple or
self.compact_p and (len(node.parent) == 1 or
len(node.parent) == 2 and
isinstance(node.parent[0], nodes.label))):
return 1
return 0
......@@ -1021,7 +1088,7 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_problematic(self, node):
if node.hasattr('refid'):
self.body.append('<a href="#%s" name="%s">' % (node['refid'],
node['id']))
node['ids'][0]))
self.context.append('</a>')
else:
self.context.append('')
......@@ -1033,12 +1100,11 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_raw(self, node):
if 'html' in node.get('format', '').split():
add_class = node.attributes.get('class') is not None
t = isinstance(node.parent, nodes.TextElement) and 'span' or 'div'
if add_class:
if node['classes']:
self.body.append(self.starttag(node, t, suffix=''))
self.body.append(node.astext())
if add_class:
if node['classes']:
self.body.append('</%s>' % t)
# Keep non-HTML raw text out of output:
raise nodes.SkipNode
......@@ -1052,19 +1118,23 @@ class HTMLTranslator(nodes.NodeVisitor):
div_atts['class'] += ' image-reference'
self.body.append(self.starttag({}, 'div', '', **div_atts))
self.context.append('</div>\n')
href = ''
if node.has_key('refuri'):
href = node['refuri']
elif node.has_key('refid'):
if ( self.settings.cloak_email_addresses
and href.startswith('mailto:')):
href = self.cloak_mailto(href)
self.in_mailto = 1
else:
assert node.has_key('refid'), \
'References must have "refuri" or "refid" attribute.'
href = '#' + node['refid']
elif node.has_key('refname'):
href = '#' + self.document.nameids[node['refname']]
self.body.append(self.starttag(node, 'a', '', CLASS='reference',
**(href and {'href': href} or {})))
href=href))
def depart_reference(self, node):
self.body.append('</a>')
self.body.append(self.context.pop())
self.in_mailto = 0
def visit_revision(self, node):
self.visit_docinfo_item(node, 'revision', meta=None)
......@@ -1074,6 +1144,7 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_row(self, node):
self.body.append(self.starttag(node, 'tr', ''))
node.column = 0
def depart_row(self, node):
self.body.append('</tr>\n')
......@@ -1135,6 +1206,12 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
self.context.append('</h2>\n')
self.in_document_title = len(self.body)
elif isinstance(node.parent, nodes.section):
tag = 'h%s' % (self.section_level + self.initial_header_level - 1)
self.body.append(
self.starttag(node, tag, '', CLASS='section-subtitle') +
self.starttag({}, 'span', '', CLASS='section-subtitle'))
self.context.append('</span></%s>\n' % tag)
def depart_subtitle(self, node):
self.body.append(self.context.pop())
......@@ -1142,6 +1219,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.subtitle = self.body[self.in_document_title:-1]
self.in_document_title = 0
self.body_pre_docinfo.extend(self.body)
self.html_subtitle.extend(self.body)
del self.body[:]
def visit_superscript(self, node):
......@@ -1151,16 +1229,16 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('</sup>')
def visit_system_message(self, node):
if node['level'] < self.document.reporter['writer'].report_level:
if node['level'] < self.document.reporter.report_level:
# Level is too low to display:
raise nodes.SkipNode
self.body.append(self.starttag(node, 'div', CLASS='system-message'))
self.body.append('<p class="system-message-title">')
attr = {}
backref_text = ''
if node.hasattr('id'):
attr['name'] = node['id']
if node.hasattr('backrefs'):
if node['ids']:
attr['name'] = node['ids'][0]
if len(node['backrefs']):
backrefs = node['backrefs']
if len(backrefs) == 1:
backref_text = ('; <em><a href="#%s">backlink</a></em>'
......@@ -1200,8 +1278,8 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_target(self, node):
if not (node.has_key('refuri') or node.has_key('refid')
or node.has_key('refname')):
self.body.append(self.starttag(node, 'a', '', CLASS='target'))
self.context.append('</a>')
self.body.append(self.starttag(node, 'span', '', CLASS='target'))
self.context.append('</span>')
else:
self.context.append('')
......@@ -1231,6 +1309,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'colgroup'))
# Appended by thead or tbody:
self.context.append('</colgroup>\n')
node.stubs = []
def depart_tgroup(self, node):
pass
......@@ -1273,6 +1352,7 @@ class HTMLTranslator(nodes.NodeVisitor):
check_id = 1
close_tag = '</caption>\n'
elif self.section_level == 0:
assert node.parent is self.document
# document title
self.head.append('<title>%s</title>\n'
% self.encode(node.astext()))
......@@ -1280,21 +1360,26 @@ class HTMLTranslator(nodes.NodeVisitor):
self.context.append('</h1>\n')
self.in_document_title = len(self.body)
else:
assert isinstance(node.parent, nodes.section)
h_level = self.section_level + self.initial_header_level - 1
atts = {}
if (len(node.parent) >= 2 and
isinstance(node.parent[1], nodes.subtitle)):
atts['CLASS'] = 'with-subtitle'
self.body.append(
self.starttag(node, 'h%s' % h_level, ''))
self.starttag(node, 'h%s' % h_level, '', **atts))
atts = {}
if node.parent.hasattr('id'):
atts['name'] = node.parent['id']
if node.parent['ids']:
atts['name'] = node.parent['ids'][0]
if node.hasattr('refid'):
atts['class'] = 'toc-backref'
atts['href'] = '#' + node['refid']
self.body.append(self.starttag({}, 'a', '', **atts))
self.context.append('</a></h%s>\n' % (h_level))
if check_id:
if node.parent.hasattr('id'):
if node.parent['ids']:
self.body.append(
self.starttag({}, 'a', '', name=node.parent['id']))
self.starttag({}, 'a', '', name=node.parent['ids'][0]))
self.context.append('</a>' + close_tag)
else:
self.context.append(close_tag)
......@@ -1305,6 +1390,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.title = self.body[self.in_document_title:-1]
self.in_document_title = 0
self.body_pre_docinfo.extend(self.body)
self.html_title.extend(self.body)
del self.body[:]
def visit_title_reference(self, node):
......@@ -1315,11 +1401,11 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_topic(self, node):
self.body.append(self.starttag(node, 'div', CLASS='topic'))
self.topic_class = node.get('class')
self.topic_classes = node['classes']
def depart_topic(self, node):
self.body.append('</div>\n')
self.topic_class = ''
self.topic_classes = []
def visit_transition(self, node):
self.body.append(self.emptytag(node, 'hr', CLASS='docutils'))
......@@ -1364,7 +1450,7 @@ class SimpleListChecker(nodes.GenericNodeVisitor):
def visit_list_item(self, node):
children = []
for child in node.get_children():
for child in node.children:
if not isinstance(child, nodes.Invisible):
children.append(child)
if (children and isinstance(children[0], nodes.paragraph)
......
"""
:Author: Engelbert Gruber
:Contact: grubert@users.sourceforge.net
:Revision: $Revision: 1.1.2.7 $
:Date: $Date: 2005/01/07 13:26:06 $
:Revision: $Revision: 3367 $
:Date: $Date: 2005-05-26 02:44:13 +0200 (Thu, 26 May 2005) $
:Copyright: This module has been placed in the public domain.
LaTeX2e document tree Writer.
......@@ -349,7 +349,7 @@ class DocumentClass:
return self._deepest_section
class Table:
""" Manage a table while traversing.
""" Manage a table while traversing.
Maybe change to a mixin defining the visit/departs, but then
class Table internal variables are in the Translator.
"""
......@@ -381,7 +381,7 @@ class Table:
return ''
def get_latex_type(self):
return self._latex_type
def set(self,attr,value):
self._attrs[attr] = value
def get(self,attr):
......@@ -442,7 +442,7 @@ class Table:
return latex_table_spec+bar
def get_column_width(self):
""" return columnwidth for current cell (not multicell)
""" return columnwidth for current cell (not multicell)
"""
return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
......@@ -471,7 +471,7 @@ class Table:
for i in range(len(self._rowspan)):
if (self._rowspan[i]>0):
self._rowspan[i] -= 1
if self._table_style == 'standard':
rowspans = []
for i in range(len(self._rowspan)):
......@@ -507,7 +507,7 @@ class Table:
def visit_entry(self):
self._cell_in_row += 1
class LaTeXTranslator(nodes.NodeVisitor):
# When options are given to the documentclass, latex will pass them
......@@ -664,21 +664,24 @@ class LaTeXTranslator(nodes.NodeVisitor):
# NOTE: Latex wants a date and an author, rst puts this into
# docinfo, so normally we donot want latex author/date handling.
# latex article has its own handling of date and author, deactivate.
# So we always emit \title{...} \author{...} \date{...}, even if the
# "..." are empty strings.
self.head = [ ]
if not self.use_latex_docinfo:
self.head.extend( [ '\\author{}\n', '\\date{}\n' ] )
self.body_prefix = ['\\raggedbottom\n']
# separate title, so we can appen subtitle.
self.title = ""
self.title = ''
# if use_latex_docinfo: collects lists of author/organization/contact/address lines
self.author_stack = []
self.date = ''
self.body_prefix = ['\\raggedbottom\n']
self.body = []
self.body_suffix = ['\n']
self.section_level = 0
self.context = []
self.topic_class = ''
self.topic_classes = []
# column specification for tables
self.table_caption = None
# do we have one or more authors
self.author_stack = None
# Flags to encode
# ---------------
# verbatim: to tell encode not to encode.
......@@ -878,15 +881,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
return self.encode(whitespace.sub(' ', text))
def astext(self):
if self.pdfinfo:
if self.pdfinfo is not None:
if self.pdfauthor:
self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
if self.pdfinfo:
pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
else:
pdfinfo = ''
title = '\\title{%s}\n' % self.title
return ''.join(self.head_prefix + [title]
+ self.head + [pdfinfo]
head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
(self.title,
' \\and\n'.join(['~\\\\\n'.join(author_lines)
for author_lines in self.author_stack]),
self.date)
return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
+ self.body_prefix + self.body + self.body_suffix)
def visit_Text(self, node):
......@@ -927,14 +934,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_authors(self, node):
# not used: visit_author is called anyway for each author.
if self.use_latex_docinfo:
self.author_stack = []
pass
def depart_authors(self, node):
if self.use_latex_docinfo:
self.head.append('\\author{%s}\n' % \
' \\and '.join(self.author_stack) )
self.author_stack = None
pass
def visit_block_quote(self, node):
self.body.append( '\\begin{quote}\n')
......@@ -943,14 +946,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append( '\\end{quote}\n')
def visit_bullet_list(self, node):
if self.topic_class == 'contents':
if self.topic_classes == ['contents']:
if not self.use_latex_toc:
self.body.append( '\\begin{list}{}{}\n' )
else:
self.body.append( '\\begin{itemize}\n' )
def depart_bullet_list(self, node):
if self.topic_class == 'contents':
if self.topic_classes == ['contents']:
if not self.use_latex_toc:
self.body.append( '\\end{list}\n' )
else:
......@@ -998,7 +1001,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.context.append(len(self.body))
else:
self.body.append('\\begin{figure}[b]')
self.body.append('\\hypertarget{%s}' % node['id'])
for id in node['ids']:
self.body.append('\\hypertarget{%s}' % id)
def depart_citation(self, node):
if self._use_latex_citations:
......@@ -1128,15 +1132,23 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.pdfauthor = self.attval(node.astext())
else:
self.pdfauthor += self.author_separator + self.attval(node.astext())
if self.use_latex_docinfo:
if self.author_stack == None:
self.head.append('\\author{%s}\n' % self.attval(node.astext()))
if self.use_latex_docinfo:
if name in ('author', 'organization', 'contact', 'address'):
# We attach these to the last author. If any of them precedes
# the first author, put them in a separate "author" group (for
# no better semantics).
if name == 'author' or not self.author_stack:
self.author_stack.append([])
if name == 'address': # newlines are meaningful
self.insert_newline = 1
text = self.encode(node.astext())
self.insert_newline = 0
else:
self.author_stack.append( self.attval(node.astext()) )
text = self.attval(node.astext())
self.author_stack[-1].append(text)
raise nodes.SkipNode
elif name == 'date':
if self.use_latex_docinfo:
self.head.append('\\date{%s}\n' % self.attval(node.astext()))
elif name == 'date':
self.date = self.attval(node.astext())
raise nodes.SkipNode
self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
if name == 'address':
......@@ -1169,7 +1181,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_document(self, node):
self.body_prefix.append('\\begin{document}\n')
# titled document?
if len(node) and isinstance(node[0], nodes.title):
if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
self.body_prefix.append('\\maketitle\n\n')
# alternative use titlepage environment.
# \begin{titlepage}
......@@ -1186,7 +1198,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
for bi in self._bibitems:
self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], bi[0], bi[1]))
self.body.append('\\end{thebibliography}\n')
self.body_suffix.append('\\end{document}\n')
def visit_emphasis(self, node):
......@@ -1204,7 +1216,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
# if the firstrow is a multirow, this actually is the second row.
# this gets hairy if rowspans follow each other.
if self.active_table.get_rowspan(0):
self.body.append(' & ')
count = 0
while self.active_table.get_rowspan(count):
count += 1
self.body.append(' & ')
self.active_table.visit_entry() # increment cell count
else:
self.body.append(' & ')
......@@ -1384,7 +1399,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('{')
else:
self.body.append('\\begin{figure}[b]')
self.body.append('\\hypertarget{%s}' % node['id'])
for id in node['ids']:
self.body.append('\\hypertarget{%s}' % id)
def depart_footnote(self, node):
if self.use_latex_footnotes:
......@@ -1692,7 +1708,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_paragraph(self, node):
index = node.parent.index(node)
if not (self.topic_class == 'contents' or
if not (self.topic_classes == ['contents'] or
(isinstance(node.parent, nodes.compound) and
index > 0 and
not isinstance(node.parent[index - 1], nodes.paragraph) and
......@@ -1799,17 +1815,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
if isinstance(node.parent, nodes.sidebar):
self.body.append('~\\\\\n\\textbf{')
self.context.append('}\n\\smallskip\n')
else:
elif isinstance(node.parent, nodes.document):
self.title = self.title + \
'\\\\\n\\large{%s}\n' % self.encode(node.astext())
raise nodes.SkipNode
elif isinstance(node.parent, nodes.section):
self.body.append('\\textbf{')
self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
def depart_subtitle(self, node):
if isinstance(node.parent, nodes.sidebar):
self.body.append(self.context.pop())
self.body.append(self.context.pop())
def visit_system_message(self, node):
if node['level'] < self.document.reporter['writer'].report_level:
if node['level'] < self.document.reporter.report_level:
raise nodes.SkipNode
def depart_system_message(self, node):
......@@ -1830,8 +1848,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
# BUG: why not (refuri or refid or refname) means not footnote ?
if not (node.has_key('refuri') or node.has_key('refid')
or node.has_key('refname')):
self.body.append('\\hypertarget{%s}{' % node['id'])
self.context.append('}')
for id in node['ids']:
self.body.append('\\hypertarget{%s}{' % id)
self.context.append('}' * len(node['ids']))
else:
self.context.append('')
......@@ -1849,11 +1868,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
pass
def visit_term(self, node):
self.body.append('\\item[')
self.body.append('\\item[{')
def depart_term(self, node):
# definition list term.
self.body.append('] ')
self.body.append('}] ')
def visit_tgroup(self, node):
#self.body.append(self.starttag(node, 'colgroup'))
......@@ -1895,8 +1914,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
def bookmark(self, node):
"""Append latex href and pdfbookmarks for titles.
"""
if node.parent.hasattr('id'):
self.body.append('\\hypertarget{%s}{}\n' % node.parent['id'])
if node.parent['ids']:
for id in node.parent['ids']:
self.body.append('\\hypertarget{%s}{}\n' % id)
if not self.use_latex_toc:
# BUG level depends on style. pdflatex allows level 0 to 3
# ToC would be the only on level 0 so i choose to decrement the rest.
......@@ -1907,8 +1927,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
l = l-1
# pdftex does not like "_" subscripts in titles
text = self.encode(node.astext())
self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
(l,text,node.parent['id']))
for id in node.parent['ids']:
self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
(l, text, id))
def visit_title(self, node):
"""Only 3 section levels are supported by LaTeX article (AFAIR)."""
......@@ -1957,10 +1978,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.context.pop())
def visit_topic(self, node):
self.topic_class = node.get('class')
if self.use_latex_toc:
self.topic_classes = node['classes']
if 'contents' in node['classes'] and self.use_latex_toc:
self.body.append('\\tableofcontents\n\n\\bigskip\n')
self.topic_class = ''
self.topic_classes = []
raise nodes.SkipNode
def visit_inline(self, node): # titlereference
......@@ -1970,7 +1991,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append( '}' )
def depart_topic(self, node):
self.topic_class = ''
self.topic_classes = []
self.body.append('\n')
def visit_rubric(self, node):
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 3129 $
# Date: $Date: 2005-03-26 17:21:28 +0100 (Sat, 26 Mar 2005) $
# Copyright: This module has been placed in the public domain.
"""
......@@ -11,7 +11,6 @@ PEP HTML Writer.
__docformat__ = 'reStructuredText'
import random
import sys
import docutils
from docutils import frontend, nodes, utils
......@@ -22,8 +21,7 @@ class Writer(html4css1.Writer):
settings_spec = html4css1.Writer.settings_spec + (
'PEP/HTML-Specific Options',
"""The HTML --footnote-references option's default is set to """
'"brackets".',
None,
(('Specify a template file. Default is "pep-html-template".',
['--template'],
{'default': 'pep-html-template', 'metavar': '<file>'}),
......@@ -32,9 +30,11 @@ class Writer(html4css1.Writer):
{'default': '..', 'metavar': '<URL>'}),
('Home URL prefix for PEPs. Default is "." (current directory).',
['--pep-home'],
{'default': '.', 'metavar': '<URL>'}),))
settings_default_overrides = {'footnote_references': 'brackets'}
{'default': '.', 'metavar': '<URL>'}),
# For testing.
(frontend.SUPPRESS_HELP,
['--no-random'],
{'action': 'store_true', 'validator': frontend.validate_boolean}),))
relative_path_settings = (html4css1.Writer.relative_path_settings
+ ('template',))
......@@ -66,7 +66,11 @@ class Writer(html4css1.Writer):
header = self.document[index]
pepnum = header[0][1].astext()
subs['pep'] = pepnum
subs['banner'] = random.randrange(64)
if settings.no_random:
subs['banner'] = 0
else:
import random
subs['banner'] = random.randrange(64)
try:
subs['pepnum'] = '%04i' % int(pepnum)
except ValueError:
......@@ -82,5 +86,5 @@ class HTMLTranslator(html4css1.HTMLTranslator):
def depart_field_list(self, node):
html4css1.HTMLTranslator.depart_field_list(self, node)
if node.get('class') == 'rfc2822':
if 'rfc2822' in node['classes']:
self.body.append('<hr />\n')
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.2.10.6 $
# Date: $Date: 2005/01/07 13:26:06 $
# Revision: $Revision: 1645 $
# Date: $Date: 2003-08-27 22:50:43 +0200 (Wed, 27 Aug 2003) $
# Copyright: This module has been placed in the public domain.
"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment