Commit d7e93e24 authored by Andreas Jung's avatar Andreas Jung

update to docutils 0.3.9

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