Commit 7e454840 authored by Christian Heimes's avatar Christian Heimes

Updated docutils to 0.3.4 (merge from tiran-restfixing-branch)

parent 7dd7aa5c
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:37 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -51,12 +51,14 @@ Subpackages:
__docformat__ = 'reStructuredText'
__version__ = '0.3.1'
"""``major.minor.micro`` version number. The micro number is bumped any time
there's a change in the API incompatible with one of the front ends or
significant new functionality. The minor number is bumped whenever there is a
__version__ = '0.3.4'
"""``major.minor.micro`` version number. The micro number is bumped
any time there's a change in the API incompatible with one of the
front ends or significant new functionality, and at any alpha or beta
release. The minor number is bumped whenever there is a stable
project release. The major number will be bumped when the project is
feature-complete, and perhaps if there is a major change in the design."""
feature-complete, and perhaps if there is a major change in the
design."""
class ApplicationError(StandardError): pass
......@@ -123,13 +125,30 @@ class TransformSpec:
default_transforms = ()
"""Transforms required by this class. Override in subclasses."""
unknown_reference_resolvers = ()
"""List of functions to try to resolve unknown references. Called when
FinalCheckVisitor is unable to find a correct target. The list should
contain functions which will try to resolve unknown references, with the
following signature::
def reference_resolver(node):
'''Returns boolean: true if resolved, false if not.'''
Each function must have a "priority" attribute which will affect the order
the unknown_reference_resolvers are run::
reference_resolver.priority = 100
Override in subclasses."""
class Component(SettingsSpec, TransformSpec):
"""Base class for Docutils components."""
component_type = None
"""Override in subclasses."""
"""Name of the component type ('reader', 'parser', 'writer'). Override in
subclasses."""
supported = ()
"""Names for this component. Override in subclasses."""
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -180,6 +180,7 @@ class Publisher:
self.settings)
self.apply_transforms(document)
output = self.writer.write(document, self.destination)
self.writer.assemble_parts()
except utils.SystemMessage, error:
if self.settings.traceback:
raise
......@@ -376,3 +377,63 @@ def publish_string(source, source_path=None, destination_path=None,
pub.set_source(source, source_path)
pub.set_destination(destination_path=destination_path)
return pub.publish(enable_exit=enable_exit)
def publish_parts(source, source_path=None, destination_path=None,
reader=None, reader_name='standalone',
parser=None, parser_name='restructuredtext',
writer=None, writer_name='pseudoxml',
settings=None, settings_spec=None,
settings_overrides=None, config_section=None,
enable_exit=None):
"""
Set up & run a `Publisher`, and return a dictionary of document parts.
Dictionary keys are the names of parts, and values are Unicode strings;
encoding is up to the client. For programmatic use with string I/O.
For encoded string input, be sure to set the "input_encoding" setting to
the desired encoding. Set it to "unicode" for unencoded Unicode string
input. Here's how::
publish_string(..., settings_overrides={'input_encoding': 'unicode'})
Parameters:
- `source`: An input string; required. This can be an encoded 8-bit
string (set the "input_encoding" setting to the correct encoding) or a
Unicode string (set the "input_encoding" setting to "unicode").
- `source_path`: Path to the file or object that produced `source`;
optional. Only used for diagnostic output.
- `destination_path`: Path to the file or object which will receive the
output; optional. Used for determining relative paths (stylesheets,
source links, etc.).
- `reader`: A `docutils.readers.Reader` object.
- `reader_name`: Name or alias of the Reader class to be instantiated if
no `reader` supplied.
- `parser`: A `docutils.parsers.Parser` object.
- `parser_name`: Name or alias of the Parser class to be instantiated if
no `parser` supplied.
- `writer`: A `docutils.writers.Writer` object.
- `writer_name`: Name or alias of the Writer class to be instantiated if
no `writer` supplied.
- `settings`: Runtime settings object.
- `settings_spec`: Extra settings specification; a `docutils.SettingsSpec`
subclass. Used only if no `settings` specified.
- `settings_overrides`: A dictionary containing program-specific overrides
of component settings.
- `config_section`: Name of configuration file section for application.
Used only if no `settings` or `settings_spec` specified.
- `enable_exit`: Boolean; enable exit status at end of processing?
"""
pub = Publisher(reader, parser, writer, settings=settings,
source_class=io.StringInput,
destination_class=io.NullOutput)
pub.set_components(reader_name, parser_name, writer_name)
if settings is None:
settings = pub.get_settings(settings_spec=settings_spec,
config_section=config_section)
if settings_overrides:
settings._update(settings_overrides, 'loose')
pub.set_source(source, source_path)
pub.set_destination(destination_path=destination_path)
pub.publish(enable_exit=enable_exit)
return pub.writer.parts
# Authors: David Goodger
# Contact: goodger@python.org
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain.
"""
This module contains practical examples of Docutils client code.
Importing this module is not recommended; its contents are subject to change
in future Docutils releases. Instead, it is recommended that you copy and
paste the parts you need into your own code, modifying as necessary.
"""
from docutils import core
def html_parts(input_string, source_path=None, destination_path=None,
input_encoding='unicode', doctitle=1, initial_header_level=1):
"""
Given an input string, returns a dictionary of HTML document parts.
Dictionary keys are the names of parts, and values are Unicode strings;
encoding is up to the client.
Parameters:
- `input_string`: A multi-line text string; required.
- `source_path`: Path to the source file or object. Optional, but useful
for diagnostic output (system messages).
- `destination_path`: Path to the file or object which will receive the
output; optional. Used for determining relative paths (stylesheets,
source links, etc.).
- `input_encoding`: The encoding of `input_string`. If it is an encoded
8-bit string, provide the correct encoding. If it is a Unicode string,
use "unicode", the default.
- `doctitle`: Disable the promotion of a lone top-level section title to
document title (and subsequent section title to document subtitle
promotion); enabled by default.
- `initial_header_level`: The initial level for header elements (e.g. 1
for "<h1>").
"""
overrides = {'input_encoding': input_encoding,
'doctitle_xform': doctitle,
'initial_header_level': initial_header_level}
parts = core.publish_parts(
source=input_string, source_path=source_path,
destination_path=destination_path,
writer_name='html', settings_overrides=overrides)
return parts
def html_fragment(input_string, source_path=None, destination_path=None,
input_encoding='unicode', output_encoding='unicode',
doctitle=1, initial_header_level=1):
"""
Given an input string, returns an HTML fragment as a string.
The return value is the contents of the <body> tag, less the title,
subtitle, and docinfo.
Parameters (see `html_parts()` for the remainder):
- `output_encoding`: The desired encoding of the output. If a Unicode
string is desired, use the default value of "unicode" .
"""
parts = html_parts(
input_string=input_string, source_path=source_path,
destination_path=destination_path,
input_encoding=input_encoding, doctitle=doctitle,
initial_header_level=initial_header_level)
fragment = parts['fragment']
if output_encoding != 'unicode':
fragment = fragment.encode(output_encoding)
return fragment
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.7 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.4.8.1 $
# Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -126,8 +126,7 @@ def validate_boolean(setting, value, option_parser,
def validate_threshold(setting, value, option_parser,
config_parser=None, config_section=None):
try:
int(value)
return value
return int(value)
except ValueError:
try:
return option_parser.thresholds[value.lower()]
......@@ -294,6 +293,10 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
('Disable backlinks from footnotes and citations.',
['--no-footnote-backlinks'],
{'dest': 'footnote_backlinks', 'action': 'store_false'}),
('Disable Docutils section numbering',
['--no-section-numbering'],
{'action': 'store_false', 'dest': 'sectnum_xform',
'default': 1, 'validator': validate_boolean}),
('Set verbosity threshold; report system messages at or higher than '
'<level> (by name or number: "info" or "1", warning/2, error/3, '
'severe/4; also, "none" or "5"). Default is 2 (warning).',
......@@ -346,7 +349,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
{'metavar': '<name[:handler]>', 'default': 'utf-8',
'validator': validate_encoding_and_error_handler}),
(SUPPRESS_HELP, # usually handled by --output-encoding
['--output_encoding_error_handler'],
['--output-encoding-error-handler'],
{'default': 'strict', 'validator': validate_encoding_error_handler}),
('Specify the text encoding for error output. Default is ASCII. '
'Optionally also specify the encoding error handler for unencodable '
......@@ -357,7 +360,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
{'metavar': '<name[:handler]>', 'default': 'ascii',
'validator': validate_encoding_and_error_handler}),
(SUPPRESS_HELP, # usually handled by --error-encoding
['--error_encoding_error_handler'],
['--error-encoding-error-handler'],
{'default': default_error_encoding_error_handler,
'validator': validate_encoding_error_handler}),
('Specify the language of input text (ISO 639 2-letter identifier).'
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -12,7 +12,10 @@ will exist for a variety of input/output mechanisms.
__docformat__ = 'reStructuredText'
import sys
import locale
try:
import locale
except:
pass
from types import UnicodeType
from docutils import TransformSpec
......@@ -156,7 +159,7 @@ class FileInput(Input):
print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
error)
print >>sys.stderr, (
'Unable to open source file for reading (%s). Exiting.'
'Unable to open source file for reading (%r). Exiting.'
% source_path)
sys.exit(1)
else:
......@@ -224,7 +227,7 @@ class FileOutput(Output):
print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
error)
print >>sys.stderr, ('Unable to open destination file for writing '
'(%s). Exiting.' % source_path)
'(%r). Exiting.' % self.destination_path)
sys.exit(1)
self.opened = 1
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# Internationalization details are documented in
......
# Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Marek Blaha
# Contact: mb@dat.cz
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/spec/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
"""
Czech-language mappings for language-dependent features of Docutils.
"""
__docformat__ = 'reStructuredText'
labels = {
# fixed: language-dependent
'author': u'Autor',
'authors': u'Auto\u0159i',
'organization': u'Organizace',
'address': u'Adresa',
'contact': u'Kontakt',
'version': u'Verze',
'revision': u'Revize',
'status': u'Stav',
'date': u'Datum',
'copyright': u'Copyright',
'dedication': u'V\u011Bnov\u00E1n\u00ED',
'abstract': u'Abstrakt',
'attention': u'Pozor!',
'caution': u'Opatrn\u011B!',
'danger': u'!NEBEZPE\u010C\u00CD!',
'error': u'Chyba',
'hint': u'Rada',
'important': u'D\u016Fle\u017Eit\u00E9',
'note': u'Pozn\u00E1mka',
'tip': u'Tip',
'warning': u'Varov\u00E1n\u00ED',
'contents': u'Obsah'}
"""Mapping of node class name to label text."""
bibliographic_fields = {
# language-dependent: fixed
u'autor': 'author',
u'auto\u0159i': 'authors',
u'organizace': 'organization',
u'adresa': 'address',
u'kontakt': 'contact',
u'verze': 'version',
u'revize': 'revision',
u'stav': 'status',
u'datum': 'date',
u'copyright': 'copyright',
u'v\u011Bnov\u00E1n\u00ED': 'dedication',
u'abstrakt': 'abstract'}
"""Czech (lowcased) to canonical name mapping for bibliographic fields."""
author_separators = [';', ',']
"""List of separator strings for the 'Authors' bibliographic field. Tried in
order."""
# Authors: David Goodger; Gunnar Schwant
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.1.2.1.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn
# Contact: mghsm@uol.com.ar
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Stefane Fermigier
# Contact: sf@fermigier.com
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Nicola Larosa
# Contact: docutils@tekNico.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# -*- coding: iso-8859-1 -*-
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/spec/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
"""
Brazilian Portuguese-language mappings for language-dependent features of Docutils.
"""
__docformat__ = 'reStructuredText'
labels = {
# fixed: language-dependent
'author': u'Autor',
'authors': u'Autores',
'organization': unicode('Organizao', 'latin1'),
'address': unicode('Endereo', 'latin1'),
'contact': u'Contato',
'version': unicode('Verso', 'latin1'),
'revision': unicode('Reviso', 'latin1'),
'status': u'Estado',
'date': u'Data',
'copyright': u'Copyright',
'dedication': unicode('Dedicatria', 'latin1'),
'abstract': u'Resumo',
'attention': unicode('Atteno!', 'latin1'),
'caution': u'Cuidado!',
'danger': u'PERIGO!',
'error': u'Erro',
'hint': unicode('Sugesto', 'latin1'),
'important': u'Importante',
'note': u'Nota',
'tip': u'Dica',
'warning': u'Aviso',
'contents': unicode('Sumrio', 'latin1')}
"""Mapping of node class name to label text."""
bibliographic_fields = {
# language-dependent: fixed
u'autor': 'author',
u'autores': 'authors',
unicode('organizao', 'latin1'): 'organization',
unicode('endereo', 'latin1'): 'address',
u'contato': 'contact',
unicode('verso', 'latin1'): 'version',
unicode('reviso', 'latin1'): 'revision',
u'estado': 'status',
u'data': 'date',
u'copyright': 'copyright',
unicode('dedicatria', 'latin1'): 'dedication',
u'resumo': 'abstract'}
"""English (lowcased) to canonical name mapping for bibliographic fields."""
author_separators = [';', ',']
"""List of separator strings for the 'Authors' bibliographic field. Tried in
order."""
# Author: Roman Suzi
# Contact: rnd@onego.ru
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# :Author: Miroslav Vasko
# :Contact: zemiak@zoznam.sk
# :Revision: $Revision: 1.5 $
# :Date: $Date: 2003/11/30 15:06:05 $
# :Revision: $Revision: 1.2.10.3.8.1 $
# :Date: $Date: 2004/05/12 19:57:42 $
# :Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:05 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -517,6 +517,9 @@ class TextElement(Element):
its immediate parent is a `TextElement` instance (including subclasses).
This is handy for nodes like `image` that can appear both inline and as
standalone body elements.
If passing children to `__init__()`, make sure to set `text` to
``''`` or some other suitable value.
"""
child_text_separator = ''
......@@ -599,6 +602,9 @@ class Targetable(Resolvable):
referenced = 0
indirect_reference_name = None
"""Holds the whitespace_normalized_name (contains mixed case) of a target"""
class Labeled:
"""Contains a `label` as its first element."""
......@@ -820,6 +826,7 @@ class document(Root, Structural, Element):
def has_name(self, name):
return self.nameids.has_key(name)
# "note" here is an imperative verb: "take note of".
def note_implicit_target(self, target, msgnode=None):
id = self.set_id(target, msgnode)
self.set_name_id_map(target, id, msgnode, explicit=None)
......@@ -939,6 +946,7 @@ class rubric(Titular, TextElement): pass
# ========================
class docinfo(Bibliographic, Element): pass
class info(Bibliographic, Element): pass
class author(Bibliographic, TextElement): pass
class authors(Bibliographic, Element): pass
class organization(Bibliographic, TextElement): pass
......@@ -1182,7 +1190,7 @@ class raw(Special, Inline, PreBibliographic, FixedTextElement):
class emphasis(Inline, TextElement): pass
class strong(Inline, TextElement): pass
class literal(Inline, TextElement): pass
class reference(Inline, Referential, TextElement): pass
class reference(General, Inline, Referential, TextElement): pass
class footnote_reference(Inline, Referential, TextElement): pass
class citation_reference(Inline, Referential, TextElement): pass
class substitution_reference(Inline, TextElement): pass
......@@ -1222,7 +1230,7 @@ node_class_names = """
footnote footnote_reference
generated
header hint
image important inline
image important info inline
label legend line_block list_item literal literal_block
note
option option_argument option_group option_list option_list_item
......@@ -1273,8 +1281,8 @@ class NodeVisitor:
Raise an exception unless overridden.
"""
raise NotImplementedError('visiting unknown node type: %s'
% node.__class__.__name__)
raise NotImplementedError('%s visiting unknown node type: %s'
% (self.__class__, node.__class__.__name__))
def unknown_departure(self, node):
"""
......@@ -1282,8 +1290,8 @@ class NodeVisitor:
Raise exception unless overridden.
"""
raise NotImplementedError('departing unknown node type: %s'
% node.__class__.__name__)
raise NotImplementedError('%s departing unknown node type: %s'
% (self.__class__, node.__class__.__name__))
class SparseNodeVisitor(NodeVisitor):
......@@ -1295,16 +1303,6 @@ class SparseNodeVisitor(NodeVisitor):
subclasses), subclass `NodeVisitor` instead.
"""
def _nop(self, node):
pass
# Save typing with dynamic assignments:
for _name in node_class_names:
setattr(SparseNodeVisitor, "visit_" + _name, _nop)
setattr(SparseNodeVisitor, "depart_" + _name, _nop)
del _name, _nop
class GenericNodeVisitor(NodeVisitor):
"""
......@@ -1337,12 +1335,18 @@ def _call_default_visit(self, node):
def _call_default_departure(self, node):
self.default_departure(node)
# Save typing with dynamic assignments:
for _name in node_class_names:
def _nop(self, node):
pass
def _add_node_class_names(names):
"""Save typing with dynamic assignments:"""
for _name in names:
setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
del _name, _call_default_visit, _call_default_departure
setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
setattr(SparseNodeVisitor, 'depart' + _name, _nop)
_add_node_class_names(node_class_names)
class TreeCopyVisitor(GenericNodeVisitor):
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:45 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:07:46 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:46 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.6 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.4.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -102,6 +102,7 @@ _directive_registry = {
'epigraph': ('body', 'epigraph'),
'highlights': ('body', 'highlights'),
'pull-quote': ('body', 'pull_quote'),
'table': ('body', 'table'),
#'questions': ('body', 'question_list'),
'image': ('images', 'image'),
'figure': ('images', 'figure'),
......@@ -117,6 +118,7 @@ _directive_registry = {
'replace': ('misc', 'replace'),
'unicode': ('misc', 'unicode_directive'),
'class': ('misc', 'class_directive'),
'role': ('misc', 'role'),
'restructuredtext-test-directive': ('misc', 'directive_test_function'),}
"""Mapping of directive name to (module name, function name). The directive
name is canonical & must be lowercase. Language-dependent names are defined
......@@ -165,23 +167,37 @@ def directive(directive_name, language_module, document):
try:
modulename, functionname = _directive_registry[canonicalname]
except KeyError:
messages.append(document.reporter.error(
'Directive "%s" not registered (canonical name "%s").'
% (directive_name, canonicalname), line=document.current_line))
return None, messages
if _modules.has_key(modulename):
module = _modules[modulename]
else:
try:
module = __import__(modulename, globals(), locals())
except ImportError:
except ImportError, detail:
messages.append(document.reporter.error(
'Error importing directive module "%s" (directive "%s"):\n%s'
% (modulename, directive_name, detail),
line=document.current_line))
return None, messages
try:
function = getattr(module, functionname)
_directives[normname] = function
except AttributeError:
messages.append(document.reporter.error(
'No function "%s" in module "%s" (directive "%s").'
% (functionname, modulename, directive_name),
line=document.current_line))
return None, messages
return function, messages
def register_directive(name, directive):
"""Register a nonstandard application-defined directive function."""
"""
Register a nonstandard application-defined directive function.
Language lookups are not needed for such functions.
"""
_directives[name] = directive
def flag(argument):
......@@ -257,7 +273,10 @@ def class_option(argument):
"""
if argument is None:
raise ValueError('argument required but none supplied')
return nodes.make_id(argument)
class_name = nodes.make_id(argument)
if not class_name:
raise ValueError('cannot make "%s" into a class name' % argument)
return class_name
def format_values(values):
return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]),
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -34,6 +34,7 @@ def topic(name, arguments, options, content, lineno,
title_text = arguments[0]
textnodes, messages = state.inline_text(title_text, lineno)
titles = [nodes.title(title_text, '', *textnodes)]
# sidebar uses this code
if options.has_key('subtitle'):
textnodes, more_messages = state.inline_text(options['subtitle'],
lineno)
......@@ -120,3 +121,38 @@ def pull_quote(name, arguments, options, content, lineno,
return [block_quote] + messages
pull_quote.content = 1
def table(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
if not content:
warning = state_machine.reporter.warning(
'Content block expected for the "%s" directive; none found.'
% name, nodes.literal_block(block_text, block_text),
line=lineno)
return [warning]
if arguments:
title_text = arguments[0]
text_nodes, messages = state.inline_text(title_text, lineno)
title = nodes.title(title_text, '', *text_nodes)
else:
title = None
node = nodes.Element() # anonymous container for parsing
text = '\n'.join(content)
state.nested_parse(content, content_offset, node)
if len(node) != 1 or not isinstance(node[0], nodes.table):
error = state_machine.reporter.error(
'Error parsing content block for the "%s" directive: '
'exactly one table expected.'
% name, nodes.literal_block(block_text, block_text),
line=lineno)
return [error]
table_node = node[0]
if options.has_key('class'):
table_node.set_class(options['class'])
if title:
table_node.insert(0, title)
return [table_node]
table.arguments = (0, 1, 1)
table.options = {'class': directives.class_option}
table.content = 1
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -13,7 +13,8 @@ __docformat__ = 'reStructuredText'
import sys
from docutils import nodes, utils
from docutils.parsers.rst import directives
from docutils.parsers.rst import directives, states
from docutils.nodes import whitespace_normalize_name
try:
import Image # PIL
......@@ -34,8 +35,23 @@ def image(name, arguments, options, content, lineno,
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
options['uri'] = reference
image_node = nodes.image(block_text, **options)
return [image_node]
if options.has_key('target'):
block = states.escape2null(options['target']).splitlines()
block = [line for line in block]
target_type, data = state.parse_target(block, block_text, lineno)
if target_type == 'refuri':
node_list = nodes.reference(refuri=data)
elif target_type == 'refname':
node_list = nodes.reference(
refname=data, name=whitespace_normalize_name(options['target']))
state.document.note_refname(node_list)
else: # malformed target
node_list = [data] # data is a system message
del options['target']
else:
node_list = []
node_list.append(nodes.image(block_text, **options))
return node_list
image.arguments = (1, 0, 1)
image.options = {'alt': directives.unchanged,
......@@ -43,6 +59,7 @@ image.options = {'alt': directives.unchanged,
'width': directives.nonnegative_int,
'scale': directives.nonnegative_int,
'align': align,
'target': directives.unchanged_required,
'class': directives.class_option}
def figure(name, arguments, options, content, lineno,
......
# Authors: David Goodger, Dethe Elza
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""Miscellaneous directives."""
......@@ -11,11 +11,15 @@ __docformat__ = 'reStructuredText'
import sys
import os.path
import re
from urllib2 import urlopen, URLError
from docutils import io, nodes, statemachine, utils
from docutils.parsers.rst import directives, states
from docutils.parsers.rst import directives, roles, states
from docutils.transforms import misc
try:
import urllib2
except ImportError:
urllib2 = None
def include(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
......@@ -97,9 +101,16 @@ def raw(name, arguments, options, content, lineno,
raw_file.close()
attributes['source'] = path
elif options.has_key('url'):
if not urllib2:
severe = state_machine.reporter.severe(
'Problems with the "%s" directive and its "url" option: '
'unable to access the required functionality (from the '
'"urllib2" module).' % name,
nodes.literal_block(block_text, block_text), line=lineno)
return [severe]
try:
raw_file = urlopen(options['url'])
except (URLError, IOError, OSError), error:
raw_file = urllib2.urlopen(options['url'])
except (urllib2.URLError, IOError, OSError), error:
severe = state_machine.reporter.severe(
'Problems with "%s" directive URL "%s":\n%s.'
% (name, options['url'], error),
......@@ -209,7 +220,7 @@ def class_directive(name, arguments, options, content, lineno,
return [pending]
else:
error = state_machine.reporter.error(
'Invalid class attribute value for "%s" directive: %s'
'Invalid class attribute value for "%s" directive: "%s".'
% (name, arguments[0]),
nodes.literal_block(block_text, block_text), line=lineno)
return [error]
......@@ -217,8 +228,67 @@ def class_directive(name, arguments, options, content, lineno,
class_directive.arguments = (1, 0, 0)
class_directive.content = 1
role_arg_pat = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
% ((states.Inliner.simplename,) * 2))
def role(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""Dynamically create and register a custom interpreted text role."""
if content_offset > lineno or not content:
error = state_machine.reporter.error(
'"%s" directive requires arguments on the first line.'
% name, nodes.literal_block(block_text, block_text), line=lineno)
return [error]
args = content[0]
match = role_arg_pat.match(args)
if not match:
error = state_machine.reporter.error(
'"%s" directive arguments not valid role names: "%s".'
% (name, args), nodes.literal_block(block_text, block_text),
line=lineno)
return [error]
new_role_name = match.group(1)
base_role_name = match.group(3)
messages = []
if base_role_name:
base_role, messages = roles.role(
base_role_name, state_machine.language, lineno, state.reporter)
if base_role is None:
error = state.reporter.error(
'Unknown interpreted text role "%s".' % base_role_name,
nodes.literal_block(block_text, block_text), line=lineno)
return messages + [error]
else:
base_role = roles.generic_custom_role
assert not hasattr(base_role, 'arguments'), (
'Supplemental directive arguments for "%s" directive not supported'
'(specified by "%r" role).' % (name, base_role))
try:
(arguments, options, content, content_offset) = (
state.parse_directive_block(content[1:], content_offset, base_role,
option_presets={}))
except states.MarkupError, detail:
error = state_machine.reporter.error(
'Error in "%s" directive:\n%s.' % (name, detail),
nodes.literal_block(block_text, block_text), line=lineno)
return messages + [error]
if not options.has_key('class'):
try:
options['class'] = directives.class_option(new_role_name)
except ValueError, detail:
error = state_machine.reporter.error(
'Invalid argument for "%s" directive:\n%s.'
% (name, detail),
nodes.literal_block(block_text, block_text), line=lineno)
return messages + [error]
role = roles.CustomRole(new_role_name, base_role, options, content)
roles.register_local_role(new_role_name, role)
return messages
role.content = 1
def directive_test_function(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""This directive is useful only for testing purposes."""
if content:
text = '\n'.join(content)
info = state_machine.reporter.info(
......
# Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -10,7 +10,7 @@ Directives for document parts.
__docformat__ = 'reStructuredText'
from docutils import nodes
from docutils import nodes, languages
from docutils.transforms import parts
from docutils.parsers.rst import directives
......@@ -27,17 +27,42 @@ def backlinks(arg):
def contents(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
"""Table of contents."""
document = state_machine.document
language = languages.get_language(document.settings.language_code)
if arguments:
title_text = arguments[0]
text_nodes, messages = state.inline_text(title_text, lineno)
title = nodes.title(title_text, '', *text_nodes)
else:
messages = []
if options.has_key('local'):
title = None
pending = nodes.pending(parts.Contents, {'title': title}, block_text)
else:
title = nodes.title('', language.labels['contents'])
topic = nodes.topic(CLASS='contents')
cls = options.get('class')
if cls:
topic.set_class(cls)
if title:
name = title.astext()
topic += title
else:
name = language.labels['contents']
name = nodes.fully_normalize_name(name)
if not document.has_name(name):
topic['name'] = name
document.note_implicit_target(topic)
pending = nodes.pending(parts.Contents, rawsource=block_text)
pending.details.update(options)
state_machine.document.note_pending(pending)
return [pending] + messages
document.note_pending(pending)
topic += pending
return [topic] + messages
contents.arguments = (0, 1, 1)
contents.options = {'depth': directives.nonnegative_int,
......
# Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:06 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# Internationalization details are documented in
......
# Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -36,6 +36,7 @@ directives = {
'epigraaf': 'epigraph',
'hoogtepunte': 'highlights',
'pull-quote (translation required)': 'pull-quote',
'table (translation required)': 'table',
#'vrae': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -48,6 +49,7 @@ directives = {
'vervang': 'replace',
'unicode': 'unicode', # should this be translated? unikode
'klas': 'class',
'role (translation required)': 'role',
'inhoud': 'contents',
'sectnum': 'sectnum',
'section-numbering': 'sectnum',
......
# Author: Marek Blaha
# Contact: mb@dat.cz
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/spec/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
"""
Czech-language mappings for language-dependent features of
reStructuredText.
"""
__docformat__ = 'reStructuredText'
directives = {
# language-dependent: fixed
u'pozor': 'attention',
u'caution': 'caution', # jak rozlisit caution a warning?
u'nebezpe\u010D\u00ED': 'danger',
u'chyba': 'error',
u'rada': 'hint',
u'd\u016Fle\u017Eit\u00E9': 'important',
u'pozn\u00E1mka': 'note',
u'tip': 'tip',
u'varov\u00E1n\u00ED': 'warning',
u'admonition': 'admonition',
u'sidebar': 'sidebar',
u't\u00E9ma': 'topic',
u'line-block': 'line-block',
u'parsed-literal': 'parsed-literal',
u'odd\u00EDl': 'rubric',
u'moto': 'epigraph',
u'highlights': 'highlights',
u'pull-quote': 'pull-quote',
u'table (translation required)': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
u'meta': 'meta',
#'imagemap': 'imagemap',
u'image': 'image', # obrazek
u'figure': 'figure', # a tady?
u'include': 'include',
u'raw': 'raw',
u'replace': 'replace',
u'unicode': 'unicode',
u't\u0159\u00EDda': 'class',
u'role (translation required)': 'role',
u'obsah': 'contents',
u'sectnum': 'sectnum',
u'section-numbering': 'sectnum',
#'footnotes': 'footnotes',
#'citations': 'citations',
u'target-notes': 'target-notes',
u'restructuredtext-test-directive': 'restructuredtext-test-directive'}
"""Czech name to registered (in directives/__init__.py) directive name
mapping."""
roles = {
# language-dependent: fixed
u'abbreviation': 'abbreviation',
u'ab': 'abbreviation',
u'acronym': 'acronym',
u'ac': 'acronym',
u'index': 'index',
u'i': 'index',
u'subscript': 'subscript',
u'sub': 'subscript',
u'superscript': 'superscript',
u'sup': 'superscript',
u'title-reference': 'title-reference',
u'title': 'title-reference',
u't': 'title-reference',
u'pep-reference': 'pep-reference',
u'pep': 'pep-reference',
u'rfc-reference': 'rfc-reference',
u'rfc': 'rfc-reference',
u'emphasis': 'emphasis',
u'strong': 'strong',
u'literal': 'literal',
u'named-reference': 'named-reference',
u'anonymous-reference': 'anonymous-reference',
u'footnote-reference': 'footnote-reference',
u'citation-reference': 'citation-reference',
u'substitution-reference': 'substitution-reference',
u'target': 'target',
u'uri-reference': 'uri-reference',
u'uri': 'uri-reference',
u'url': 'uri-reference',}
"""Mapping of Czech role names to canonical role names for interpreted text.
"""
# -*- coding: iso-8859-1 -*-
# Author: Engelbert Gruber
# Contact: grubert@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -37,6 +37,7 @@ directives = {
'epigraph (translation required)': 'epigraph',
'highlights (translation required)': 'highlights',
'pull-quote (translation required)': 'pull-quote', # kasten too ?
'table (translation required)': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -49,7 +50,8 @@ directives = {
# einfügung would be the noun.
'ersetzung': 'replace', # ersetzen, ersetze
'unicode': 'unicode',
'klasse': 'class', # offer class too ?
'klasse': 'class', # offer "class" too ?
'role (translation required)': 'role',
'inhalt': 'contents',
'sectnum': 'sectnum',
'section-numbering': 'sectnum',
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -37,6 +37,7 @@ directives = {
'epigraph': 'epigraph',
'highlights': 'highlights',
'pull-quote': 'pull-quote',
'table': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -49,6 +50,7 @@ directives = {
'replace': 'replace',
'unicode': 'unicode',
'class': 'class',
'role': 'role',
'contents': 'contents',
'sectnum': 'sectnum',
'section-numbering': 'sectnum',
......
# Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.1.2.1.8.1 $
# Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -40,6 +40,7 @@ directives = {
u'elstara\u0135oj': 'highlights',
u'ekstera-citajxo': 'pull-quote',
u'ekstera-cita\u0135o': 'pull-quote',
u'tabelo': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -53,6 +54,7 @@ directives = {
u'anstata\u016di': 'replace',
u'unicode': 'unicode',
u'klaso': 'class',
u'rolo': 'role',
u'enhavo': 'contents',
u'seknum': 'sectnum',
u'sekcia-numerado': 'sectnum',
......
# -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn
# Contact: mghsm@uol.com.ar
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -42,6 +42,7 @@ directives = {
u'epigrafe': 'epigraph',
u'destacado': 'highlights',
u'cita-destacada': 'pull-quote',
u'tabla': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -54,6 +55,7 @@ directives = {
u'reemplazar': 'replace',
u'unicode': 'unicode',
u'clase': 'class',
u'rol': 'role',
u'contenido': 'contents',
u'numseccion': 'sectnum',
u'numsecci\u00f3n': 'sectnum',
......@@ -74,8 +76,10 @@ roles = {
u'ac': 'acronym',
u'indice': 'index',
u'i': 'index',
u'subscript (translation required)': 'subscript',
u'superscript (translation required)': 'superscript',
u'subindice': 'subscript',
u'sub\u00edndice': 'subscript',
u'superindice': 'superscript',
u'super\u00edndice': 'superscript',
u'referencia-titulo': 'title-reference',
u'titulo': 'title-reference',
u't': 'title-reference',
......
# Authors: David Goodger; William Dode
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -38,6 +38,7 @@ directives = {
u'\u00E9pigraphe': 'epigraph',
u'chapeau': 'highlights',
u'accroche': 'pull-quote',
u'tableau': 'table',
#u'questions': 'questions',
#u'qr': 'questions',
#u'faq': 'questions',
......@@ -51,6 +52,7 @@ directives = {
u'remplace': 'replace',
u'unicode': 'unicode',
u'classe': 'class',
u'role (translation required)': 'role',
u'sommaire': 'contents',
u'table-des-mati\u00E8res': 'contents',
u'sectnum': 'sectnum',
......
# Author: Nicola Larosa
# Contact: docutils@tekNico.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Author: Nicola Larosa, Lele Gaifax
# Contact: docutils@tekNico.net, lele@seldati.it
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/spec/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
"""
Italian-language mappings for language-dependent features of
reStructuredText.
......@@ -27,15 +22,16 @@ directives = {
'nota': 'note',
'consiglio': 'tip',
'avvertenza': 'warning',
'admonition (translation required)': 'admonition',
'sidebar (translation required)': 'sidebar',
'ammonizione': 'admonition',
'riquadro': 'sidebar',
'argomento': 'topic',
'blocco di linee': 'line-block',
'parsed-literal': 'parsed-literal',
'rubric (translation required)': 'rubric',
'epigraph (translation required)': 'epigraph',
'highlights (translation required)': 'highlights',
'blocco-di-righe': 'line-block',
'blocco-interpretato': 'parsed-literal',
'rubrica': 'rubric',
'epigrafe': 'epigraph',
'evidenzia': 'highlights',
'pull-quote (translation required)': 'pull-quote',
'tabella': 'table',
#'questions': 'questions',
#'qa': 'questions',
#'faq': 'questions',
......@@ -47,11 +43,12 @@ directives = {
'grezzo': 'raw',
'sostituisci': 'replace',
'unicode': 'unicode',
'class (translation required)': 'class',
'classe': 'class',
'ruolo': 'role',
'indice': 'contents',
'seznum': 'sectnum',
'section-numbering': 'sectnum',
'target-notes': 'target-notes',
'sezioni-autonumerate': 'sectnum',
'annota-riferimenti-esterni': 'target-notes',
#'footnotes': 'footnotes',
#'citations': 'citations',
'restructuredtext-test-directive': 'restructuredtext-test-directive'}
......@@ -59,23 +56,23 @@ directives = {
mapping."""
roles = {
'abbreviation (translation required)': 'abbreviation',
'acronym (translation required)': 'acronym',
'index (translation required)': 'index',
'subscript (translation required)': 'subscript',
'superscript (translation required)': 'superscript',
'title-reference (translation required)': 'title-reference',
'pep-reference (translation required)': 'pep-reference',
'rfc-reference (translation required)': 'rfc-reference',
'emphasis (translation required)': 'emphasis',
'strong (translation required)': 'strong',
'literal (translation required)': 'literal',
'named-reference (translation required)': 'named-reference',
'anonymous-reference (translation required)': 'anonymous-reference',
'footnote-reference (translation required)': 'footnote-reference',
'citation-reference (translation required)': 'citation-reference',
'substitution-reference (translation required)': 'substitution-reference',
'target (translation required)': 'target',
'uri-reference (translation required)': 'uri-reference',}
'abbreviazione': 'abbreviation',
'acronimo': 'acronym',
'indice': 'index',
'deponente': 'subscript',
'esponente': 'superscript',
'riferimento-titolo': 'title-reference',
'riferimento-pep': 'pep-reference',
'riferimento-rfc': 'rfc-reference',
'enfasi': 'emphasis',
'forte': 'strong',
'letterale': 'literal',
'riferimento-con-nome': 'named-reference',
'riferimento-anonimo': 'anonymous-reference',
'riferimento-nota': 'footnote-reference',
'riferimento-citazione': 'citation-reference',
'riferimento-sostituzione': 'substitution-reference',
'destinazione': 'target',
'riferimento-uri': 'uri-reference',}
"""Mapping of Italian role names to canonical role names for interpreted text.
"""
# -*- coding: iso-8859-1 -*-
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# read <http://docutils.sf.net/spec/howto/i18n.html>. Two files must be
# translated for each language: one in docutils/languages, the other in
# docutils/parsers/rst/languages.
"""
Brazilian Portuguese-language mappings for language-dependent features of
reStructuredText.
"""
__docformat__ = 'reStructuredText'
directives = {
# language-dependent: fixed
u'ateno': 'attention',
'cuidado': 'caution',
'perigo': 'danger',
'erro': 'error',
u'sugesto': 'hint',
'importante': 'important',
'nota': 'note',
'dica': 'tip',
'aviso': 'warning',
u'exortao': 'admonition',
'barra-lateral': 'sidebar',
u'tpico': 'topic',
'bloco-de-linhas': 'line-block',
'literal-interpretado': 'parsed-literal',
'rubrica': 'rubric',
u'epgrafo': 'epigraph',
'destaques': 'highlights',
u'citao-destacada': 'pull-quote',
u'table (translation required)': 'table',
#'perguntas': 'questions',
#'qa': 'questions',
#'faq': 'questions',
'meta': 'meta',
#'imagemap': 'imagemap',
'imagem': 'image',
'figura': 'figure',
u'incluso': 'include',
'cru': 'raw',
u'substituio': 'replace',
'unicode': 'unicode',
'classe': 'class',
'role (translation required)': 'role',
u'ndice': 'contents',
'numsec': 'sectnum',
u'numerao-de-sees': 'sectnum',
#u'notas-de-rorap': 'footnotes',
#u'citaes': 'citations',
u'links-no-rodap': 'target-notes',
'restructuredtext-test-directive': 'restructuredtext-test-directive'}
"""English name to registered (in directives/__init__.py) directive name
mapping."""
roles = {
# language-dependent: fixed
u'abbreviao': 'abbreviation',
'ab': 'abbreviation',
u'acrnimo': 'acronym',
'ac': 'acronym',
u'ndice-remissivo': 'index',
'i': 'index',
'subscrito': 'subscript',
'sub': 'subscript',
'sobrescrito': 'superscript',
'sob': 'superscript',
u'referncia-a-ttulo': 'title-reference',
u'ttulo': 'title-reference',
't': 'title-reference',
u'referncia-a-pep': 'pep-reference',
'pep': 'pep-reference',
u'referncia-a-rfc': 'rfc-reference',
'rfc': 'rfc-reference',
u'nfase': 'emphasis',
'forte': 'strong',
'literal': 'literal',
u'referncia-por-nome': 'named-reference',
u'referncia-annima': 'anonymous-reference',
u'referncia-a-nota-de-rodap': 'footnote-reference',
u'referncia-a-citao': 'citation-reference',
u'referncia-a-substituio': 'substitution-reference',
'alvo': 'target',
u'referncia-a-uri': 'uri-reference',
'uri': 'uri-reference',
'url': 'uri-reference',}
"""Mapping of English role names to canonical role names for interpreted text.
"""
# Author: Roman Suzi
# Contact: rnd@onego.ru
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -23,6 +23,7 @@ directives = {
u'parsed-literal',
u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u0430\u044f-\u0446\u0438\u0442\u0430\u0442\u0430':
u'pull-quote',
u'table (translation required)': 'table',
u'\u0441\u044b\u0440\u043e\u0439': u'raw',
u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace',
u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext':
......@@ -40,6 +41,7 @@ directives = {
u'\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435':
u'image',
u'\u043a\u043b\u0430\u0441\u0441': u'class',
u'role (translation required)': 'role',
u'\u043d\u043e\u043c\u0435\u0440-\u0440\u0430\u0437\u0434\u0435\u043b\u0430':
u'sectnum',
u'\u043d\u0443\u043c\u0435\u0440\u0430\u0446\u0438\u044f-\u0440\u0430\u0437'
......
# Author: Miroslav Vasko
# Contact: zemiak@zoznam.sk
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -36,6 +36,7 @@ directives = {
u'epigraph (translation required)': 'epigraph',
u'highlights (translation required)': 'highlights',
u'pull-quote (translation required)': 'pull-quote',
u'table (translation required)': 'table',
#u'questions': 'questions',
#u'qa': 'questions',
#u'faq': 'questions',
......@@ -48,6 +49,7 @@ directives = {
u'nahradi\x9d': 'replace',
u'unicode': 'unicode',
u'class (translation required)': 'class',
u'role (translation required)': 'role',
u'obsah': 'contents',
u'\xe8as\x9d': 'sectnum',
u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum',
......
# Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:07 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
......@@ -35,6 +35,7 @@ directives = {
u'epigraph (translation required)': 'epigraph',
u'highlights (translation required)': 'highlights',
u'pull-quote (translation required)': 'pull-quote',
u'table (translation required)': 'table',
# u'fr\u00e5gor': 'questions',
# NOTE: A bit long, but recommended by http://www.nada.kth.se/dataterm/:
# u'fr\u00e5gor-och-svar': 'questions',
......@@ -48,6 +49,7 @@ directives = {
u'ers\u00e4tt': 'replace',
u'unicode': 'unicode',
u'class (translation required)': 'class',
u'role (translation required)': 'role',
u'inneh\u00e5ll': 'contents',
u'sektionsnumrering': 'sectnum',
u'target-notes (translation required)': 'target-notes',
......
# Author: Edward Loper
# Contact: edloper@gradient.cis.upenn.edu
# Revision: $Revision: 1.1.2.1 $
# Date: $Date: 2004/05/12 19:57:46 $
# Copyright: This module has been placed in the public domain.
"""
This module defines standard interpreted text role functions, a registry for
interpreted text roles, and an API for adding to and retrieving from the
registry.
The interface for interpreted role functions is as follows::
def role_fn(name, rawtext, text, lineno, inliner,
options={}, content=[]):
code...
# Set function attributes for customization:
role_fn.options = ...
role_fn.content = ...
Parameters:
- ``name`` is the local name of the interpreted text role, the role name
actually used in the document.
- ``rawtext`` is a string containing the entire interpreted text construct.
Return it as a ``problematic`` node linked to a system message if there is a
problem.
- ``text`` is the interpreted text content.
- ``lineno`` is the line number where the interpreted text beings.
- ``inliner`` is the Inliner object that called the role function.
It defines the following useful attributes: ``reporter``,
``problematic``, ``memo``, ``parent``, ``document``.
- ``options``: A dictionary of directive options for customization, to be
interpreted by the role function. Used for additional attributes for the
generated elements and other functionality.
- ``content``: A list of strings, the directive content for customization
("role" directive). To be interpreted by the role function.
Function attributes for customization, interpreted by the "role" directive:
- ``options``: A dictionary, mapping known option names to conversion
functions such as `int` or `float`. ``None`` or an empty dict implies no
options to parse. Several directive option conversion functions are defined
in the `directives` module.
All role functions implicitly support the "class" option, unless disabled
with an explicit ``{'class': None}``.
- ``content``: A boolean; true if content is allowed. Client code must handle
the case where content is required but not supplied (an empty content list
will be supplied).
Note that unlike directives, the "arguments" function attribute is not
supported for role customization. Directive arguments are handled by the
"role" directive itself.
Interpreted role functions return a tuple of two values:
- A list of nodes which will be inserted into the document tree at the
point where the interpreted role was encountered (can be an empty
list).
- A list of system messages, which will be inserted into the document tree
immediately after the end of the current inline block (can also be empty).
"""
__docformat__ = 'reStructuredText'
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst.languages import en as _fallback_language_module
DEFAULT_INTERPRETED_ROLE = 'title-reference'
"""
The canonical name of the default interpreted role. This role is used
when no role is specified for a piece of interpreted text.
"""
_role_registry = {}
"""Mapping of canonical role names to role functions. Language-dependent role
names are defined in the ``language`` subpackage."""
_roles = {}
"""Mapping of local or language-dependent interpreted text role names to role
functions."""
def role(role_name, language_module, lineno, reporter):
"""
Locate and return a role function from its language-dependent name, along
with a list of system messages. If the role is not found in the current
language, check English. Return a 2-tuple: role function (``None`` if the
named role cannot be found) and a list of system messages.
"""
normname = role_name.lower()
messages = []
msg_text = []
if _roles.has_key(normname):
return _roles[normname], messages
if role_name:
canonicalname = None
try:
canonicalname = language_module.roles[normname]
except AttributeError, error:
msg_text.append('Problem retrieving role entry from language '
'module %r: %s.' % (language_module, error))
except KeyError:
msg_text.append('No role entry for "%s" in module "%s".'
% (role_name, language_module.__name__))
else:
canonicalname = DEFAULT_INTERPRETED_ROLE
# If we didn't find it, try English as a fallback.
if not canonicalname:
try:
canonicalname = _fallback_language_module.roles[normname]
msg_text.append('Using English fallback for role "%s".'
% role_name)
except KeyError:
msg_text.append('Trying "%s" as canonical role name.'
% role_name)
# The canonical name should be an English name, but just in case:
canonicalname = normname
# Collect any messages that we generated.
if msg_text:
message = reporter.info('\n'.join(msg_text), line=lineno)
messages.append(message)
# Look the role up in the registry, and return it.
if _role_registry.has_key(canonicalname):
role_fn = _role_registry[canonicalname]
register_local_role(normname, role_fn)
return role_fn, messages
else:
return None, messages # Error message will be generated by caller.
def register_canonical_role(name, role_fn):
"""
Register an interpreted text role by its canonical name.
:Parameters:
- `name`: The canonical name of the interpreted role.
- `role_fn`: The role function. See the module docstring.
"""
set_implicit_options(role_fn)
_role_registry[name] = role_fn
def register_local_role(name, role_fn):
"""
Register an interpreted text role by its local or language-dependent name.
:Parameters:
- `name`: The local or language-dependent name of the interpreted role.
- `role_fn`: The role function. See the module docstring.
"""
set_implicit_options(role_fn)
_roles[name] = role_fn
def set_implicit_options(role_fn):
"""
Add customization options to role functions, unless explicitly set or
disabled.
"""
if not hasattr(role_fn, 'options') or role_fn.options is None:
role_fn.options = {'class': directives.class_option}
elif not role_fn.options.has_key('class'):
role_fn.options['class'] = directives.class_option
def register_generic_role(canonical_name, node_class):
"""For roles which simply wrap a given `node_class` around the text."""
role = GenericRole(canonical_name, node_class)
register_canonical_role(canonical_name, role)
class GenericRole:
"""
Generic interpreted text role, where the interpreted text is simply
wrapped with the provided node class.
"""
def __init__(self, role_name, node_class):
self.name = role_name
self.node_class = node_class
def __call__(self, role, rawtext, text, lineno, inliner,
options={}, content=[]):
return [self.node_class(rawtext, text, **options)], []
class CustomRole:
"""
Wrapper for custom interpreted text roles.
"""
def __init__(self, role_name, base_role, options={}, content=[]):
self.name = role_name
self.base_role = base_role
self.options = None
if hasattr(base_role, 'options'):
self.options = base_role.options
self.content = None
if hasattr(base_role, 'content'):
self.content = base_role.content
self.supplied_options = options
self.supplied_content = content
def __call__(self, role, rawtext, text, lineno, inliner,
options={}, content=[]):
opts = self.supplied_options.copy()
opts.update(options)
cont = list(self.supplied_content)
if cont and content:
cont += '\n'
cont.extend(content)
return self.base_role(role, rawtext, text, lineno, inliner,
options=opts, content=cont)
def generic_custom_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
""""""
# Once nested inline markup is implemented, this and other methods should
# recursively call inliner.nested_parse().
return [nodes.inline(rawtext, text, **options)], []
generic_custom_role.options = {'class': directives.class_option}
######################################################################
# Define and register the standard roles:
######################################################################
register_generic_role('abbreviation', nodes.abbreviation)
register_generic_role('acronym', nodes.acronym)
register_generic_role('emphasis', nodes.emphasis)
register_generic_role('literal', nodes.literal)
register_generic_role('strong', nodes.strong)
register_generic_role('subscript', nodes.subscript)
register_generic_role('superscript', nodes.superscript)
register_generic_role('title-reference', nodes.title_reference)
def pep_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
try:
pepnum = int(text)
if pepnum < 0 or pepnum > 9999:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
'PEP number must be a number from 0 to 9999; "%s" is invalid.'
% text, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
# Base URL mainly used by inliner.pep_reference; so this is correct:
ref = inliner.pep_url % pepnum
return [nodes.reference(rawtext, 'PEP ' + text, refuri=ref, **options)], []
register_canonical_role('pep-reference', pep_reference_role)
def rfc_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
try:
rfcnum = int(text)
if rfcnum <= 0:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
'RFC number must be a number greater than or equal to 1; '
'"%s" is invalid.' % text, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
# Base URL mainly used by inliner.rfc_reference, so this is correct:
ref = inliner.rfc_url % rfcnum
node = nodes.reference(rawtext, 'RFC ' + text, refuri=ref, **options)
return [node], []
register_canonical_role('rfc-reference', rfc_reference_role)
######################################################################
# Register roles that are currently unimplemented.
######################################################################
def unimplemented_role(role, rawtext, text, lineno, inliner, attributes={}):
msg = inliner.reporter.error(
'Interpreted text role "%s" not implemented.' % role, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
register_canonical_role('index', unimplemented_role)
register_canonical_role('named-reference', unimplemented_role)
register_canonical_role('anonymous-reference', unimplemented_role)
register_canonical_role('uri-reference', unimplemented_role)
register_canonical_role('footnote-reference', unimplemented_role)
register_canonical_role('citation-reference', unimplemented_role)
register_canonical_role('substitution-reference', unimplemented_role)
register_canonical_role('target', unimplemented_role)
# This should remain unimplemented, for testing purposes:
register_canonical_role('restructuredtext-unimplemented-role',
unimplemented_role)
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:07:46 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:46 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -107,13 +107,14 @@ __docformat__ = 'reStructuredText'
import sys
import re
from docutils import roman
import roman
from types import TupleType
from docutils import nodes, statemachine, utils, urischemes
from docutils import ApplicationError, DataError
from docutils.statemachine import StateMachineWS, StateWS
from docutils.nodes import fully_normalize_name as normalize_name
from docutils.parsers.rst import directives, languages, tableparser
from docutils.nodes import whitespace_normalize_name
from docutils.parsers.rst import directives, languages, tableparser, roles
from docutils.parsers.rst.languages import en as _fallback_language_module
......@@ -274,7 +275,7 @@ class RSTState(StateWS):
state_machine.unlink()
new_offset = state_machine.abs_line_offset()
# No `block.parent` implies disconnected -- lines aren't in sync:
if block.parent:
if block.parent and (len(block) - block_length) != 0:
# Adjustment for block if modified in nested parse:
self.state_machine.next_line(len(block) - block_length)
return new_offset
......@@ -475,46 +476,6 @@ class Inliner:
Parse inline markup; call the `parse()` method.
"""
_interpreted_roles = {
# Values of ``None`` mean "not implemented yet":
'title-reference': 'generic_interpreted_role',
'abbreviation': 'generic_interpreted_role',
'acronym': 'generic_interpreted_role',
'index': None,
'subscript': 'generic_interpreted_role',
'superscript': 'generic_interpreted_role',
'emphasis': 'generic_interpreted_role',
'strong': 'generic_interpreted_role',
'literal': 'generic_interpreted_role',
'named-reference': None,
'anonymous-reference': None,
'uri-reference': None,
'pep-reference': 'pep_reference_role',
'rfc-reference': 'rfc_reference_role',
'footnote-reference': None,
'citation-reference': None,
'substitution-reference': None,
'target': None,
'restructuredtext-unimplemented-role': None}
"""Mapping of canonical interpreted text role name to method name.
Initializes a name to bound-method mapping in `__init__`."""
default_interpreted_role = 'title-reference'
"""The role to use when no explicit role is given.
Override in subclasses."""
generic_roles = {'abbreviation': nodes.abbreviation,
'acronym': nodes.acronym,
'emphasis': nodes.emphasis,
'literal': nodes.literal,
'strong': nodes.strong,
'subscript': nodes.subscript,
'superscript': nodes.superscript,
'title-reference': nodes.title_reference,}
"""Mapping of canonical interpreted text role name to node class.
Used by the `generic_interpreted_role` method for simple, straightforward
roles (simple wrapping; no extra processing)."""
def __init__(self, roles=None):
"""
`roles` is a mapping of canonical role name to role function or bound
......@@ -525,17 +486,6 @@ class Inliner:
"""List of (pattern, bound method) tuples, used by
`self.implicit_inline`."""
self.interpreted_roles = {}
"""Mapping of canonical role name to role function or bound method.
Items removed from this mapping will be disabled."""
for canonical, method in self._interpreted_roles.items():
if method:
self.interpreted_roles[canonical] = getattr(self, method)
else:
self.interpreted_roles[canonical] = None
self.interpreted_roles.update(roles or {})
def init_customizations(self, settings):
"""Setting-based customizations; run when parsing begins."""
if settings.pep_references:
......@@ -546,6 +496,8 @@ class Inliner:
self.rfc_reference))
def parse(self, text, lineno, memo, parent):
# Needs to be refactored for nested inline markup.
# Add nested_parse() method?
"""
Return 2 lists: nodes (text and inline elements), and system_messages.
......@@ -601,11 +553,12 @@ class Inliner:
non_whitespace_after = r'(?![ \n])'
# Alphanumerics with isolated internal [-._] chars (i.e. not 2 together):
simplename = r'(?:(?!_)\w)+(?:[-._](?:(?!_)\w)+)*'
# Valid URI characters (see RFC 2396 & RFC 2732):
uric = r"""[-_.!~*'()[\];/:@&=+$,%a-zA-Z0-9]"""
# Valid URI characters (see RFC 2396 & RFC 2732);
# final \x00 allows backslash escapes in URIs:
uric = r"""[-_.!~*'()[\];/:@&=+$,%a-zA-Z0-9\x00]"""
# Last URI character; same as uric but no punctuation:
urilast = r"""[_~/a-zA-Z0-9]"""
emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9]"""
emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9\x00]"""
email_pattern = r"""
%(emailc)s+(?:\.%(emailc)s+)* # name
@ # at
......@@ -659,10 +612,10 @@ class Inliner:
embedded_uri=re.compile(
r"""
(
[ \n]+ # spaces or beginning of line
(?:[ \n]+|^) # spaces or beginning of line/string
< # open bracket
%(non_whitespace_after)s
([^<>\0]+) # anything but angle brackets & nulls
([^<>\x00]+) # anything but angle brackets & nulls
%(non_whitespace_before)s
> # close bracket w/o whitespace before
)
......@@ -823,26 +776,11 @@ class Inliner:
return self.phrase_ref(string[:matchstart], string[textend:],
rawsource, escaped, text)
else:
try:
return self.interpreted(
string[:rolestart], string[textend:],
rawsource, text, role, lineno)
except UnknownInterpretedRoleError, detail:
msg = self.reporter.error(
'Unknown interpreted text role "%s".' % role,
line=lineno)
text = unescape(string[rolestart:textend], 1)
prb = self.problematic(text, text, msg)
return (string[:rolestart], [prb], string[textend:],
detail.args[0] + [msg])
except InterpretedRoleNotImplementedError, detail:
msg = self.reporter.error(
'Interpreted text role "%s" not implemented.' % role,
line=lineno)
text = unescape(string[rolestart:textend], 1)
prb = self.problematic(text, text, msg)
return (string[:rolestart], [prb], string[textend:],
detail.args[0] + [msg])
rawsource = unescape(string[rolestart:textend], 1)
nodelist, messages = self.interpreted(rawsource, text, role,
lineno)
return (string[:rolestart], nodelist,
string[textend:], messages)
msg = self.reporter.warning(
'Inline interpreted text or phrase reference start-string '
'without end-string.', line=lineno)
......@@ -861,10 +799,13 @@ class Inliner:
target = nodes.target(match.group(1), refuri=uri)
else:
raise ApplicationError('problem with URI: %r' % uri_text)
if not text:
text = uri
else:
target = None
refname = normalize_name(text)
reference = nodes.reference(rawsource, text)
reference = nodes.reference(rawsource, text,
name=whitespace_normalize_name(text))
node_list = [reference]
if rawsource[-2:] == '__':
if target:
......@@ -891,50 +832,18 @@ class Inliner:
else:
return uri
def interpreted(self, before, after, rawsource, text, role, lineno):
role_function, canonical, messages = self.get_role_function(role,
lineno)
if role_function:
nodelist, messages2 = role_function(canonical, rawsource, text,
lineno)
messages.extend(messages2)
return before, nodelist, after, messages
def interpreted(self, rawsource, text, role, lineno):
role_fn, messages = roles.role(role, self.language, lineno,
self.reporter)
if role_fn:
nodes, messages2 = role_fn(role, rawsource, text, lineno, self)
return nodes, messages + messages2
else:
raise InterpretedRoleNotImplementedError(messages)
def get_role_function(self, role, lineno):
messages = []
msg_text = []
if role:
name = role.lower()
else:
name = self.default_interpreted_role
canonical = None
try:
canonical = self.language.roles[name]
except AttributeError, error:
msg_text.append('Problem retrieving role entry from language '
'module %r: %s.' % (self.language, error))
except KeyError:
msg_text.append('No role entry for "%s" in module "%s".'
% (name, self.language.__name__))
if not canonical:
try:
canonical = _fallback_language_module.roles[name]
msg_text.append('Using English fallback for role "%s".'
% name)
except KeyError:
msg_text.append('Trying "%s" as canonical role name.'
% name)
# Should be an English name, but just in case:
canonical = name
if msg_text:
message = self.reporter.info('\n'.join(msg_text), line=lineno)
messages.append(message)
try:
return self.interpreted_roles[canonical], canonical, messages
except KeyError:
raise UnknownInterpretedRoleError(messages)
msg = self.reporter.error(
'Unknown interpreted text role "%s".' % role,
line=lineno)
return ([self.problematic(rawsource, rawsource, msg)],
messages + [msg])
def literal(self, match, lineno):
before, inlines, remaining, sysmessages, endstring = self.inline_obj(
......@@ -1014,8 +923,9 @@ class Inliner:
def reference(self, match, lineno, anonymous=None):
referencename = match.group('refname')
refname = normalize_name(referencename)
referencenode = nodes.reference(referencename + match.group('refend'),
referencename)
referencenode = nodes.reference(
referencename + match.group('refend'), referencename,
name=whitespace_normalize_name(referencename))
if anonymous:
referencenode['anonymous'] = 1
self.document.note_anonymous_ref(referencenode)
......@@ -1104,44 +1014,6 @@ class Inliner:
'_': reference,
'__': anonymous_reference}
def generic_interpreted_role(self, role, rawtext, text, lineno):
try:
role_class = self.generic_roles[role]
except KeyError:
msg = self.reporter.error('Unknown interpreted text role: "%s".'
% role, line=lineno)
prb = self.problematic(text, text, msg)
return [prb], [msg]
return [role_class(rawtext, text)], []
def pep_reference_role(self, role, rawtext, text, lineno):
try:
pepnum = int(text)
if pepnum < 0 or pepnum > 9999:
raise ValueError
except ValueError:
msg = self.reporter.error(
'PEP number must be a number from 0 to 9999; "%s" is invalid.'
% text, line=lineno)
prb = self.problematic(text, text, msg)
return [prb], [msg]
ref = self.pep_url % pepnum
return [nodes.reference(rawtext, 'PEP ' + text, refuri=ref)], []
def rfc_reference_role(self, role, rawtext, text, lineno):
try:
rfcnum = int(text)
if rfcnum <= 0:
raise ValueError
except ValueError:
msg = self.reporter.error(
'RFC number must be a number greater than or equal to 1; '
'"%s" is invalid.' % text, line=lineno)
prb = self.problematic(text, text, msg)
return [prb], [msg]
ref = self.rfc_url % rfcnum
return [nodes.reference(rawtext, 'RFC ' + text, refuri=ref)], []
class Body(RSTState):
......@@ -1472,15 +1344,15 @@ class Body(RSTState):
def field_marker(self, match, context, next_state):
"""Field list item."""
fieldlist = nodes.field_list()
self.parent += fieldlist
field_list = nodes.field_list()
self.parent += field_list
field, blank_finish = self.field(match)
fieldlist += field
field_list += field
offset = self.state_machine.line_offset + 1 # next line
newline_offset, blank_finish = self.nested_list_parse(
self.state_machine.input_lines[offset:],
input_offset=self.state_machine.abs_line_offset() + 1,
node=fieldlist, initial_state='FieldList',
node=field_list, initial_state='FieldList',
blank_finish=blank_finish)
self.goto_line(newline_offset)
if not blank_finish:
......@@ -1492,14 +1364,15 @@ class Body(RSTState):
lineno = self.state_machine.abs_line_number()
indented, indent, line_offset, blank_finish = \
self.state_machine.get_first_known_indented(match.end())
fieldnode = nodes.field()
fieldnode.line = lineno
fieldnode += nodes.field_name(name, name)
fieldbody = nodes.field_body('\n'.join(indented))
fieldnode += fieldbody
field_node = nodes.field()
field_node.line = lineno
name_nodes, name_messages = self.inline_text(name, lineno)
field_node += nodes.field_name(name, '', *name_nodes)
field_body = nodes.field_body('\n'.join(indented), *name_messages)
field_node += field_body
if indented:
self.parse_field_body(indented, line_offset, fieldbody)
return fieldnode, blank_finish
self.parse_field_body(indented, line_offset, field_body)
return field_node, blank_finish
def parse_field_marker(self, match):
"""Extract & return field name from a field marker match."""
......@@ -1876,39 +1749,59 @@ class Body(RSTState):
raise MarkupError('malformed hyperlink target.', lineno)
del block[:blockindex]
block[0] = (block[0] + ' ')[targetmatch.end()-len(escaped)-1:].strip()
target = self.make_target(block, blocktext, lineno,
targetmatch.group('name'))
return [target], blank_finish
def make_target(self, block, block_text, lineno, target_name):
target_type, data = self.parse_target(block, block_text, lineno)
if target_type == 'refname':
target = nodes.target(block_text, '', refname=normalize_name(data))
target.indirect_reference_name = data
self.add_target(target_name, '', target, lineno)
self.document.note_indirect_target(target)
return target
elif target_type == 'refuri':
target = nodes.target(block_text, '')
self.add_target(target_name, data, target, lineno)
return target
else:
return data
def parse_target(self, block, block_text, lineno):
"""
Determine the type of reference of a target.
:Return: A 2-tuple, one of:
- 'refname' and the indirect reference name
- 'refuri' and the URI
- 'malformed' and a system_message node
"""
if block and block[-1].strip()[-1:] == '_': # possible indirect target
reference = ' '.join([line.strip() for line in block])
refname = self.is_reference(reference)
if refname:
target = nodes.target(blocktext, '', refname=refname)
target.line = lineno
self.add_target(targetmatch.group('name'), '', target)
self.document.note_indirect_target(target)
return [target], blank_finish
nodelist = []
return 'refname', refname
reference = ''.join([line.strip() for line in block])
if reference.find(' ') != -1:
if reference.find(' ') == -1:
return 'refuri', unescape(reference)
else:
warning = self.reporter.warning(
'Hyperlink target contains whitespace. Perhaps a footnote '
'was intended?',
nodes.literal_block(blocktext, blocktext), line=lineno)
nodelist.append(warning)
else:
unescaped = unescape(reference)
target = nodes.target(blocktext, '')
target.line = lineno
self.add_target(targetmatch.group('name'), unescaped, target)
nodelist.append(target)
return nodelist, blank_finish
nodes.literal_block(block_text, block_text), line=lineno)
return 'malformed', warning
def is_reference(self, reference):
match = self.explicit.patterns.reference.match(
normalize_name(reference))
whitespace_normalize_name(reference))
if not match:
return None
return unescape(match.group('simple') or match.group('phrase'))
def add_target(self, targetname, refuri, target):
def add_target(self, targetname, refuri, target, lineno):
target.line = lineno
if targetname:
name = normalize_name(unescape(targetname))
target['name'] = name
......@@ -1988,17 +1881,18 @@ class Body(RSTState):
return [msg], blank_finish
def directive(self, match, **option_presets):
"""Returns a 2-tuple: list of nodes, and a "blank finish" boolean."""
type_name = match.group(1)
directive_function, messages = directives.directive(
type_name, self.memo.language, self.document)
self.parent += messages
if directive_function:
return self.parse_directive(
return self.run_directive(
directive_function, match, type_name, option_presets)
else:
return self.unknown_directive(type_name)
def parse_directive(self, directive_fn, match, type_name, option_presets):
def run_directive(self, directive_fn, match, type_name, option_presets):
"""
Parse a directive then run its directive function.
......@@ -2020,13 +1914,6 @@ class Body(RSTState):
Returns a 2-tuple: list of nodes, and a "blank finish" boolean.
"""
arguments = []
options = {}
argument_spec = getattr(directive_fn, 'arguments', None)
if argument_spec and argument_spec[:2] == (0, 0):
argument_spec = None
option_spec = getattr(directive_fn, 'options', None)
content_spec = getattr(directive_fn, 'content', None)
lineno = self.state_machine.abs_line_number()
initial_line_offset = self.state_machine.line_offset
indented, indent, line_offset, blank_finish \
......@@ -2034,6 +1921,30 @@ class Body(RSTState):
strip_top=0)
block_text = '\n'.join(self.state_machine.input_lines[
initial_line_offset : self.state_machine.line_offset + 1])
try:
arguments, options, content, content_offset = (
self.parse_directive_block(indented, line_offset,
directive_fn, option_presets))
except MarkupError, detail:
error = self.reporter.error(
'Error in "%s" directive:\n%s.' % (type_name, detail),
nodes.literal_block(block_text, block_text), line=lineno)
return [error], blank_finish
result = directive_fn(type_name, arguments, options, content, lineno,
content_offset, block_text, self,
self.state_machine)
return (result,
blank_finish or self.state_machine.is_next_line_blank())
def parse_directive_block(self, indented, line_offset, directive_fn,
option_presets):
arguments = []
options = {}
argument_spec = getattr(directive_fn, 'arguments', None)
if argument_spec and argument_spec[:2] == (0, 0):
argument_spec = None
option_spec = getattr(directive_fn, 'options', None)
content_spec = getattr(directive_fn, 'content', None)
if indented and not indented[0].strip():
indented.trim_start()
line_offset += 1
......@@ -2055,24 +1966,15 @@ class Body(RSTState):
while content and not content[0].strip():
content.trim_start()
content_offset += 1
try:
if option_spec:
options, arg_block = self.parse_directive_options(
option_presets, option_spec, arg_block)
if argument_spec:
arguments = self.parse_directive_arguments(argument_spec,
arg_block)
arguments = self.parse_directive_arguments(
argument_spec, arg_block)
if content and not content_spec:
raise MarkupError('no content permitted')
except MarkupError, detail:
error = self.reporter.error(
'Error in "%s" directive:\n%s.' % (type_name, detail),
nodes.literal_block(block_text, block_text), line=lineno)
return [error], blank_finish
result = directive_fn(
type_name, arguments, options, content, lineno, content_offset,
block_text, self, self.state_machine)
return result, blank_finish or self.state_machine.is_next_line_blank()
return (arguments, options, content, content_offset)
def parse_directive_options(self, option_presets, option_spec, arg_block):
options = option_presets.copy()
......@@ -2251,38 +2153,14 @@ class Body(RSTState):
return [], next_state, []
def anonymous_target(self, match):
lineno = self.state_machine.abs_line_number()
block, indent, offset, blank_finish \
= self.state_machine.get_first_known_indented(match.end(),
until_blank=1)
blocktext = match.string[:match.end()] + '\n'.join(block)
if block and block[-1].strip()[-1:] == '_': # possible indirect target
reference = escape2null(' '.join([line.strip()
for line in block]))
refname = self.is_reference(reference)
if refname:
target = nodes.target(blocktext, '', refname=refname,
anonymous=1)
self.document.note_anonymous_target(target)
self.document.note_indirect_target(target)
block = [escape2null(line) for line in block]
target = self.make_target(block, blocktext, lineno, '')
return [target], blank_finish
nodelist = []
reference = escape2null(''.join([line.strip() for line in block]))
if reference.find(' ') != -1:
lineno = self.state_machine.abs_line_number() - len(block) + 1
warning = self.reporter.warning(
'Anonymous hyperlink target contains whitespace. Perhaps a '
'footnote was intended?',
nodes.literal_block(blocktext, blocktext),
line=lineno)
nodelist.append(warning)
else:
target = nodes.target(blocktext, '', anonymous=1)
if reference:
unescaped = unescape(reference)
target['refuri'] = unescaped
self.document.note_anonymous_target(target)
nodelist.append(target)
return nodelist, blank_finish
def line(self, match, context, next_state):
"""Section title overline or transition marker."""
......@@ -2660,20 +2538,29 @@ class Text(RSTState):
"""Return a list of nodes."""
indented, indent, offset, blank_finish = \
self.state_machine.get_indented()
nodelist = []
while indented and not indented[-1].strip():
indented.trim_end()
if indented:
if not indented:
return self.quoted_literal_block()
nodelist = []
data = '\n'.join(indented)
nodelist.append(nodes.literal_block(data, data))
if not blank_finish:
nodelist.append(self.unindent_warning('Literal block'))
else:
nodelist.append(self.reporter.warning(
'Literal block expected; none found.',
line=self.state_machine.abs_line_number()))
return nodelist
def quoted_literal_block(self):
abs_line_offset = self.state_machine.abs_line_offset()
offset = self.state_machine.line_offset
parent_node = nodes.Element()
new_abs_offset = self.nested_parse(
self.state_machine.input_lines[offset:],
input_offset=abs_line_offset, node=parent_node, match_titles=0,
state_machine_kwargs={'state_classes': (QuotedLiteralBlock,),
'initial_state': 'QuotedLiteralBlock'})
self.goto_line(new_abs_offset)
return parent_node.children
def definition_list_item(self, termline):
indented, indent, line_offset, blank_finish = \
self.state_machine.get_indented()
......@@ -2687,8 +2574,8 @@ class Text(RSTState):
definitionlistitem += definition
if termline[0][-2:] == '::':
definition += self.reporter.info(
'Blank line missing before literal block? Interpreted as a '
'definition list item.', line=line_offset + 1)
'Blank line missing before literal block (after the "::")? '
'Interpreted as a definition list item.', line=line_offset+1)
self.nested_parse(indented, input_offset=line_offset, node=definition)
return definitionlistitem, blank_finish
......@@ -2890,6 +2777,77 @@ class Line(SpecializedText):
raise statemachine.StateCorrection('Body', 'text')
class QuotedLiteralBlock(RSTState):
"""
Nested parse handler for quoted (unindented) literal blocks.
Special-purpose. Not for inclusion in `state_classes`.
"""
patterns = {'initial_quoted': r'(%(nonalphanum7bit)s)' % Body.pats,
'text': r''}
initial_transitions = ('initial_quoted', 'text')
def __init__(self, state_machine, debug=0):
RSTState.__init__(self, state_machine, debug)
self.messages = []
self.initial_lineno = None
def blank(self, match, context, next_state):
if context:
raise EOFError
else:
return context, next_state, []
def eof(self, context):
if context:
text = '\n'.join(context)
literal_block = nodes.literal_block(text, text)
literal_block.line = self.initial_lineno
self.parent += literal_block
else:
self.parent += self.reporter.warning(
'Literal block expected; none found.',
line=self.state_machine.abs_line_number())
self.state_machine.previous_line()
self.parent += self.messages
return []
def indent(self, match, context, next_state):
assert context, ('QuotedLiteralBlock.indent: context should not '
'be empty!')
self.messages.append(
self.reporter.error('Unexpected indentation.',
line=self.state_machine.abs_line_number()))
self.state_machine.previous_line()
raise EOFError
def initial_quoted(self, match, context, next_state):
"""Match arbitrary quote character on the first line only."""
self.remove_transition('initial_quoted')
quote = match.string[0]
pattern = re.compile(re.escape(quote))
# New transition matches consistent quotes only:
self.add_transition('quoted',
(pattern, self.quoted, self.__class__.__name__))
self.initial_lineno = self.state_machine.abs_line_number()
return [match.string], next_state, []
def quoted(self, match, context, next_state):
"""Match consistent quotes on subsequent lines."""
context.append(match.string)
return context, next_state, []
def text(self, match, context, next_state):
if context:
self.messages.append(
self.reporter.error('Inconsistent literal block quoting.',
line=self.state_machine.abs_line_number()))
self.state_machine.previous_line()
raise EOFError
state_classes = (Body, BulletList, DefinitionList, EnumeratedList, FieldList,
OptionList, ExtensionOptions, Explicit, Text, Definition,
Line, SubstitutionDef, RFC2822Body, RFC2822List)
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:07:46 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:47 $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger; Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:08 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:08 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:08 $
# Revision: $Revision: 1.3.2.1.8.1 $
# Date: $Date: 2004/05/12 19:57:53 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -13,9 +13,117 @@ __docformat__ = 'reStructuredText'
import sys
import docutils.readers
from docutils.readers.python import moduleparser
from docutils import parsers
from docutils import nodes
from docutils.readers.python import pynodes
from docutils import readers
class Reader(docutils.readers.Reader):
config_section = 'python reader'
config_section_dependencies = ('readers',)
default_parser = 'restructuredtext'
def parse(self):
"""Parse `self.input` into a document tree."""
self.document = document = self.new_document()
module_section = moduleparser.parse_module(self.input,
self.source.source_path)
module_section.walk(DocformatVisitor(self.document))
visitor = DocstringFormattingVisitor(
document=document,
default_parser=self.default_parser)
module_section.walk(visitor)
self.document.append(module_section)
class DocformatVisitor(nodes.SparseNodeVisitor):
"""
This sets docformat attributes in a module. Wherever an assignment
to __docformat__ is found, we look for the enclosing scope -- a class,
a module, or a function -- and set the docformat attribute there.
We can't do this during the DocstringFormattingVisitor walking,
because __docformat__ may appear below a docstring in that format
(typically below the module docstring).
"""
def visit_attribute(self, node):
assert isinstance(node[0], pynodes.object_name)
name = node[0][0].data
if name != '__docformat__':
return
value = None
for child in children:
if isinstance(child, pynodes.expression_value):
value = child[0].data
break
assert value.startswith("'") or value.startswith('"'), "__docformat__ must be assigned a string literal (not %s); line: %s" % (value, node['lineno'])
name = name[1:-1]
looking_in = node.parent
while not isinstance(looking_in, (pynodes.module_section,
pynodes.function_section,
pynodes.class_section)):
looking_in = looking_in.parent
looking_in['docformat'] = name
class DocstringFormattingVisitor(nodes.SparseNodeVisitor):
def __init__(self, document, default_parser):
self.document = document
self.default_parser = default_parser
self.parsers = {}
def visit_docstring(self, node):
text = node[0].data
docformat = self.find_docformat(node)
del node[0]
node['docformat'] = docformat
parser = self.get_parser(docformat)
parser.parse(text, self.document)
for child in self.document.get_children():
node.append(child)
self.document.current_source = self.document.current_line = None
del self.document[:]
def get_parser(self, parser_name):
"""
Get a parser based on its name. We reuse parsers during this
visitation, so parser instances are cached.
"""
parser_name = parsers._parser_aliases.get(parser_name, parser_name)
if not self.parsers.has_key(parser_name):
cls = parsers.get_parser_class(parser_name)
self.parsers[parser_name] = cls()
return self.parsers[parser_name]
def find_docformat(self, node):
"""
Find the __docformat__ closest to this node (i.e., look in the
class or module)
"""
while node:
if node.get('docformat'):
return node['docformat']
node = node.parent
return self.default_parser
if __name__ == '__main__':
try:
import locale
locale.setlocale(locale.LC_ALL, '')
except:
pass
from docutils.core import publish_cmdline, default_description
description = ('Generates pseudo-XML from Python modules '
'(for testing purposes). ' + default_description)
publish_cmdline(description=description,
reader=Reader())
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3 $
# Date: $Date: 2003/11/30 15:06:08 $
# Revision: $Revision: 1.3.2.1.8.1 $
# Date: $Date: 2004/05/12 19:57:53 $
# Copyright: This module has been placed in the public domain.
"""
Parser for Python modules.
The `parse_module()` function takes a module's text and file name, runs it
through the module parser (using compiler.py and tokenize.py) and produces a
"module documentation tree": a high-level AST full of nodes that are
interesting from an auto-documentation standpoint. For example, given this
module (x.py)::
The `parse_module()` function takes a module's text and file name,
runs it through the module parser (using compiler.py and tokenize.py)
and produces a parse tree of the source code, using the nodes as found
in pynodes.py. For example, given this module (x.py)::
# comment
......@@ -50,69 +49,95 @@ module (x.py)::
The module parser will produce this module documentation tree::
<Module filename="test data">
<Comment lineno=1>
comment
<Docstring>
<module_section filename="test data">
<docstring>
Docstring
<Docstring lineno="5">
<docstring lineno="5">
Additional docstring
<Attribute lineno="7" name="__docformat__">
<Expression lineno="7">
<attribute lineno="7">
<object_name>
__docformat__
<expression_value lineno="7">
'reStructuredText'
<Attribute lineno="9" name="a">
<Expression lineno="9">
<attribute lineno="9">
<object_name>
a
<expression_value lineno="9">
1
<Docstring lineno="10">
<docstring lineno="10">
Attribute docstring
<Class bases="Super" lineno="12" name="C">
<Docstring lineno="12">
<class_section lineno="12">
<object_name>
C
<class_base>
Super
<docstring lineno="12">
C's docstring
<Attribute lineno="16" name="class_attribute">
<Expression lineno="16">
<attribute lineno="16">
<object_name>
class_attribute
<expression_value lineno="16">
1
<Docstring lineno="17">
<docstring lineno="17">
class_attribute's docstring
<Method lineno="19" name="__init__">
<Docstring lineno="19">
<method_section lineno="19">
<object_name>
__init__
<docstring lineno="19">
__init__'s docstring
<ParameterList lineno="19">
<Parameter lineno="19" name="self">
<Parameter lineno="19" name="text">
<Default lineno="19">
<parameter_list lineno="19">
<parameter lineno="19">
<object_name>
self
<parameter lineno="19">
<object_name>
text
<parameter_default lineno="19">
None
<Attribute lineno="22" name="self.instance_attribute">
<Expression lineno="22">
<attribute lineno="22">
<object_name>
self.instance_attribute
<expression_value lineno="22">
(text * 7 + ' whaddyaknow')
<Docstring lineno="24">
<docstring lineno="24">
instance_attribute's docstring
<Function lineno="27" name="f">
<Docstring lineno="27">
<function_section lineno="27">
<object_name>
f
<docstring lineno="27">
f's docstring
<ParameterList lineno="27">
<Parameter lineno="27" name="x">
<Comment>
<parameter_list lineno="27">
<parameter lineno="27">
<object_name>
x
<comment>
# parameter x
<Parameter lineno="27" name="y">
<Default lineno="27">
<parameter lineno="27">
<object_name>
y
<parameter_default lineno="27">
a * 5
<Comment>
<comment>
# parameter y
<ExcessPositionalArguments lineno="27" name="args">
<Comment>
<parameter excess_positional="1" lineno="27">
<object_name>
args
<comment>
# parameter args
<Attribute lineno="33" name="f.function_attribute">
<Expression lineno="33">
<attribute lineno="33">
<object_name>
f.function_attribute
<expression_value lineno="33">
1
<Docstring lineno="34">
<docstring lineno="34">
f.function_attribute's docstring
(Comments are not implemented yet.)
compiler.parse() provides most of what's needed for this doctree, and
"tokenize" can be used to get the rest. We can determine the line number from
the compiler.parse() AST, and the TokenParser.rhs(lineno) method provides the
rest.
"tokenize" can be used to get the rest. We can determine the line
number from the compiler.parse() AST, and the TokenParser.rhs(lineno)
method provides the rest.
The Docutils Python reader component will transform this module doctree into a
Python-specific Docutils doctree, and then a `stylist transform`_ will
......@@ -190,6 +215,8 @@ import token
from compiler.consts import OP_ASSIGN
from compiler.visitor import ASTVisitor
from types import StringType, UnicodeType, TupleType
from docutils.readers.python import pynodes
from docutils.nodes import Text
def parse_module(module_text, filename):
......@@ -200,168 +227,6 @@ def parse_module(module_text, filename):
compiler.walk(ast, visitor, walker=visitor)
return visitor.module
class Node:
"""
Base class for module documentation tree nodes.
"""
def __init__(self, node):
self.children = []
"""List of child nodes."""
self.lineno = node.lineno
"""Line number of this node (or ``None``)."""
def __str__(self, indent=' ', level=0):
return ''.join(['%s%s\n' % (indent * level, repr(self))] +
[child.__str__(indent, level+1)
for child in self.children])
def __repr__(self):
parts = [self.__class__.__name__]
for name, value in self.attlist():
parts.append('%s="%s"' % (name, value))
return '<%s>' % ' '.join(parts)
def attlist(self, **atts):
if self.lineno is not None:
atts['lineno'] = self.lineno
attlist = atts.items()
attlist.sort()
return attlist
def append(self, node):
self.children.append(node)
def extend(self, node_list):
self.children.extend(node_list)
class TextNode(Node):
def __init__(self, node, text):
Node.__init__(self, node)
self.text = trim_docstring(text)
def __str__(self, indent=' ', level=0):
prefix = indent * (level + 1)
text = '\n'.join([prefix + line for line in self.text.splitlines()])
return Node.__str__(self, indent, level) + text + '\n'
class Module(Node):
def __init__(self, node, filename):
Node.__init__(self, node)
self.filename = filename
def attlist(self):
return Node.attlist(self, filename=self.filename)
class Docstring(TextNode): pass
class Comment(TextNode): pass
class Import(Node):
def __init__(self, node, names, from_name=None):
Node.__init__(self, node)
self.names = names
self.from_name = from_name
def __str__(self, indent=' ', level=0):
prefix = indent * (level + 1)
lines = []
for name, as in self.names:
if as:
lines.append('%s%s as %s' % (prefix, name, as))
else:
lines.append('%s%s' % (prefix, name))
text = '\n'.join(lines)
return Node.__str__(self, indent, level) + text + '\n'
def attlist(self):
if self.from_name:
atts = {'from': self.from_name}
else:
atts = {}
return Node.attlist(self, **atts)
class Attribute(Node):
def __init__(self, node, name):
Node.__init__(self, node)
self.name = name
def attlist(self):
return Node.attlist(self, name=self.name)
class AttributeTuple(Node):
def __init__(self, node, names):
Node.__init__(self, node)
self.names = names
def attlist(self):
return Node.attlist(self, names=' '.join(self.names))
class Expression(TextNode):
def __str__(self, indent=' ', level=0):
prefix = indent * (level + 1)
return '%s%s%s\n' % (Node.__str__(self, indent, level),
prefix, self.text.encode('unicode-escape'))
class Function(Attribute): pass
class ParameterList(Node): pass
class Parameter(Attribute): pass
class ParameterTuple(AttributeTuple):
def attlist(self):
return Node.attlist(self, names=normalize_parameter_name(self.names))
class ExcessPositionalArguments(Parameter): pass
class ExcessKeywordArguments(Parameter): pass
class Default(Expression): pass
class Class(Node):
def __init__(self, node, name, bases=None):
Node.__init__(self, node)
self.name = name
self.bases = bases or []
def attlist(self):
atts = {'name': self.name}
if self.bases:
atts['bases'] = ' '.join(self.bases)
return Node.attlist(self, **atts)
class Method(Function): pass
class BaseVisitor(ASTVisitor):
def __init__(self, token_parser):
......@@ -389,7 +254,7 @@ class DocstringVisitor(BaseVisitor):
def visitConst(self, node):
if self.documentable:
if type(node.value) in (StringType, UnicodeType):
self.documentable.append(Docstring(node, node.value))
self.documentable.append(make_docstring(node.value, node.lineno))
else:
self.documentable = None
......@@ -418,25 +283,28 @@ class ModuleVisitor(AssignmentVisitor):
self.module = None
def visitModule(self, node):
self.module = module = Module(node, self.filename)
if node.doc is not None:
module.append(Docstring(node, node.doc))
self.module = module = pynodes.module_section()
module['filename'] = self.filename
append_docstring(module, node.doc, node.lineno)
self.context.append(module)
self.documentable = module
self.visit(node.node)
self.context.pop()
def visitImport(self, node):
self.context[-1].append(Import(node, node.names))
self.context[-1] += make_import_group(names=node.names,
lineno=node.lineno)
self.documentable = None
def visitFrom(self, node):
self.context[-1].append(
Import(node, node.names, from_name=node.modname))
make_import_group(names=node.names, from_name=node.modname,
lineno=node.lineno))
self.documentable = None
def visitFunction(self, node):
visitor = FunctionVisitor(self.token_parser)
visitor = FunctionVisitor(self.token_parser,
function_class=pynodes.function_section)
compiler.walk(node, visitor, walker=visitor)
self.context[-1].append(visitor.function)
......@@ -450,29 +318,32 @@ class AttributeVisitor(BaseVisitor):
def __init__(self, token_parser):
BaseVisitor.__init__(self, token_parser)
self.attributes = []
self.attributes = pynodes.class_attribute_section()
def visitAssign(self, node):
# Don't visit the expression itself, just the attribute nodes:
for child in node.nodes:
self.dispatch(child)
expression_text = self.token_parser.rhs(node.lineno)
expression = Expression(node, expression_text)
expression = pynodes.expression_value()
expression.append(Text(expression_text))
for attribute in self.attributes:
attribute.append(expression)
def visitAssName(self, node):
self.attributes.append(Attribute(node, node.name))
self.attributes.append(make_attribute(node.name,
lineno=node.lineno))
def visitAssTuple(self, node):
attributes = self.attributes
self.attributes = []
self.default_visit(node)
names = [attribute.name for attribute in self.attributes]
att_tuple = AttributeTuple(node, names)
att_tuple.lineno = self.attributes[0].lineno
n = pynodes.attribute_tuple()
n.extend(self.attributes)
n['lineno'] = self.attributes[0]['lineno']
attributes.append(n)
self.attributes = attributes
self.attributes.append(att_tuple)
#self.attributes.append(att_tuple)
def visitAssAttr(self, node):
self.default_visit(node, node.attrname)
......@@ -481,13 +352,17 @@ class AttributeVisitor(BaseVisitor):
self.default_visit(node, node.attrname + '.' + suffix)
def visitName(self, node, suffix):
self.attributes.append(Attribute(node, node.name + '.' + suffix))
self.attributes.append(make_attribute(node.name + '.' + suffix,
lineno=node.lineno))
class FunctionVisitor(DocstringVisitor):
in_function = 0
function_class = Function
def __init__(self, token_parser, function_class):
DocstringVisitor.__init__(self, token_parser)
self.function_class = function_class
def visitFunction(self, node):
if self.in_function:
......@@ -495,9 +370,11 @@ class FunctionVisitor(DocstringVisitor):
# Don't bother with nested function definitions.
return
self.in_function = 1
self.function = function = self.function_class(node, node.name)
if node.doc is not None:
function.append(Docstring(node, node.doc))
self.function = function = make_function_like_section(
name=node.name,
lineno=node.lineno,
doc=node.doc,
function_class=self.function_class)
self.context.append(function)
self.documentable = function
self.parse_parameter_list(node)
......@@ -509,10 +386,11 @@ class FunctionVisitor(DocstringVisitor):
special = []
argnames = list(node.argnames)
if node.kwargs:
special.append(ExcessKeywordArguments(node, argnames[-1]))
special.append(make_parameter(argnames[-1], excess_keyword=1))
argnames.pop()
if node.varargs:
special.append(ExcessPositionalArguments(node, argnames[-1]))
special.append(make_parameter(argnames[-1],
excess_positional=1))
argnames.pop()
defaults = list(node.defaults)
defaults = [None] * (len(argnames) - len(defaults)) + defaults
......@@ -521,17 +399,21 @@ class FunctionVisitor(DocstringVisitor):
#print >>sys.stderr, function_parameters
for argname, default in zip(argnames, defaults):
if type(argname) is TupleType:
parameter = ParameterTuple(node, argname)
parameter = pynodes.parameter_tuple()
for tuplearg in argname:
parameter.append(make_parameter(tuplearg))
argname = normalize_parameter_name(argname)
else:
parameter = Parameter(node, argname)
parameter = make_parameter(argname)
if default:
parameter.append(Default(node, function_parameters[argname]))
n_default = pynodes.parameter_default()
n_default.append(Text(function_parameters[argname]))
parameter.append(n_default)
parameters.append(parameter)
if parameters or special:
special.reverse()
parameters.extend(special)
parameter_list = ParameterList(node)
parameter_list = pynodes.parameter_list()
parameter_list.extend(parameters)
self.function.append(parameter_list)
......@@ -554,9 +436,9 @@ class ClassVisitor(AssignmentVisitor):
#pdb.set_trace()
for base in node.bases:
self.visit(base)
self.klass = klass = Class(node, node.name, self.bases)
if node.doc is not None:
klass.append(Docstring(node, node.doc))
self.klass = klass = make_class_section(node.name, self.bases,
doc=node.doc,
lineno=node.lineno)
self.context.append(klass)
self.documentable = klass
self.visit(node.code)
......@@ -578,19 +460,17 @@ class ClassVisitor(AssignmentVisitor):
def visitFunction(self, node):
if node.name == '__init__':
visitor = InitMethodVisitor(self.token_parser)
visitor = InitMethodVisitor(self.token_parser,
function_class=pynodes.method_section)
compiler.walk(node, visitor, walker=visitor)
else:
visitor = MethodVisitor(self.token_parser)
visitor = FunctionVisitor(self.token_parser,
function_class=pynodes.method_section)
compiler.walk(node, visitor, walker=visitor)
self.context[-1].append(visitor.function)
class MethodVisitor(FunctionVisitor):
function_class = Method
class InitMethodVisitor(MethodVisitor, AssignmentVisitor): pass
class InitMethodVisitor(FunctionVisitor, AssignmentVisitor): pass
class TokenParser:
......@@ -744,6 +624,85 @@ class TokenParser:
return parameters
def make_docstring(doc, lineno):
n = pynodes.docstring()
if lineno:
# Really, only module docstrings don't have a line
# (@@: but maybe they should)
n['lineno'] = lineno
n.append(Text(doc))
return n
def append_docstring(node, doc, lineno):
if doc:
node.append(make_docstring(doc, lineno))
def make_class_section(name, bases, lineno, doc):
n = pynodes.class_section()
n['lineno'] = lineno
n.append(make_object_name(name))
for base in bases:
b = pynodes.class_base()
b.append(make_object_name(base))
n.append(b)
append_docstring(n, doc, lineno)
return n
def make_object_name(name):
n = pynodes.object_name()
n.append(Text(name))
return n
def make_function_like_section(name, lineno, doc, function_class):
n = function_class()
n['lineno'] = lineno
n.append(make_object_name(name))
append_docstring(n, doc, lineno)
return n
def make_import_group(names, lineno, from_name=None):
n = pynodes.import_group()
n['lineno'] = lineno
if from_name:
n_from = pynodes.import_from()
n_from.append(Text(from_name))
n.append(n_from)
for name, alias in names:
n_name = pynodes.import_name()
n_name.append(Text(name))
if alias:
n_alias = pynodes.import_alias()
n_alias.append(Text(alias))
n_name.append(n_alias)
n.append(n_name)
return n
def make_class_attribute(name, lineno):
n = pynodes.class_attribute()
n['lineno'] = lineno
n.append(Text(name))
return n
def make_attribute(name, lineno):
n = pynodes.attribute()
n['lineno'] = lineno
n.append(make_object_name(name))
return n
def make_parameter(name, excess_keyword=0, excess_positional=0):
"""
excess_keyword and excess_positional must be either 1 or 0, and
not both of them can be 1.
"""
n = pynodes.parameter()
n.append(make_object_name(name))
assert not excess_keyword or not excess_positional
if excess_keyword:
n['excess_keyword'] = 1
if excess_positional:
n['excess_positional'] = 1
return n
def trim_docstring(text):
"""
Trim indentation and blank lines from docstring text & return it.
......@@ -782,3 +741,18 @@ def normalize_parameter_name(name):
return '(%s)' % ', '.join([normalize_parameter_name(n) for n in name])
else:
return name
if __name__ == '__main__':
import sys
args = sys.argv[1:]
if args[0] == '-v':
filename = args[1]
module_text = open(filename).read()
ast = compiler.parse(module_text)
visitor = compiler.visitor.ExampleASTVisitor()
compiler.walk(ast, visitor, walker=visitor, verbose=1)
else:
filename = args[0]
content = open(filename).read()
print parse_module(content, filename).pformat()
#! /usr/bin/env python
"""
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Revision: $Revision: 1.1.2.1 $
:Date: $Date: 2004/05/12 19:57:53 $
:Copyright: This module has been placed in the public domain.
"""
from docutils import nodes
from docutils.nodes import Element, TextElement, Structural, Inline, Part, \
Text
import types
# This is the parent class of all the other pynode classes:
class PythonStructural(Structural): pass
# =====================
# Structural Elements
# =====================
class module_section(PythonStructural, Element): pass
class class_section(PythonStructural, Element): pass
class class_base(PythonStructural, Element): pass
class method_section(PythonStructural, Element): pass
class attribute(PythonStructural, Element): pass
class function_section(PythonStructural, Element): pass
class class_attribute_section(PythonStructural, Element): pass
class class_attribute(PythonStructural, Element): pass
class expression_value(PythonStructural, Element): pass
class attribute(PythonStructural, Element): pass
# Structural Support Elements
# ---------------------------
class parameter_list(PythonStructural, Element): pass
class parameter_tuple(PythonStructural, Element): pass
class parameter_default(PythonStructural, TextElement): pass
class import_group(PythonStructural, TextElement): pass
class import_from(PythonStructural, TextElement): pass
class import_name(PythonStructural, TextElement): pass
class import_alias(PythonStructural, TextElement): pass
class docstring(PythonStructural, Element): pass
# =================
# Inline Elements
# =================
# These elements cannot become references until the second
# pass. Initially, we'll use "reference" or "name".
class object_name(PythonStructural, TextElement): pass
class parameter_list(PythonStructural, TextElement): pass
class parameter(PythonStructural, TextElement): pass
class parameter_default(PythonStructural, TextElement): pass
class class_attribute(PythonStructural, TextElement): pass
class attribute_tuple(PythonStructural, TextElement): pass
# =================
# Unused Elements
# =================
# These were part of the model, and maybe should be in the future, but
# aren't now.
#class package_section(PythonStructural, Element): pass
#class module_attribute_section(PythonStructural, Element): pass
#class instance_attribute_section(PythonStructural, Element): pass
#class module_attribute(PythonStructural, TextElement): pass
#class instance_attribute(PythonStructural, TextElement): pass
#class exception_class(PythonStructural, TextElement): pass
#class warning_class(PythonStructural, TextElement): pass
# Collect all the classes we've written above
def install_node_class_names():
node_class_names = []
for name, var in globals().items():
if (type(var) is types.ClassType
and issubclass(var, PythonStructural) \
and name.lower() == name):
node_class_names.append(var.tagname or name)
# Register the new node names with GenericNodeVisitor and
# SpecificNodeVisitor:
nodes._add_node_class_names(node_class_names)
install_node_class_names()
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:08 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -14,7 +14,6 @@ __docformat__ = 'reStructuredText'
import sys
from docutils import frontend, readers
from docutils.transforms import frontmatter, references
from docutils.parsers.rst import Parser
class Reader(readers.Reader):
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:39 $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -60,6 +60,7 @@ class Transform:
document.settings.language_code)
"""Language module local to this document."""
def apply(self):
"""Override to apply the transform to the document tree."""
raise NotImplementedError('subclass must override this method')
......@@ -76,7 +77,8 @@ class Transformer(TransformSpec):
default_transforms = (universal.Decorations,
universal.FinalChecks,
universal.Messages)
universal.Messages,
universal.FilterMessages)
"""These transforms are applied to all document trees."""
def __init__(self, document):
......@@ -84,6 +86,9 @@ class Transformer(TransformSpec):
"""List of transforms to apply. Each item is a 3-tuple:
``(priority string, transform class, pending node or None)``."""
self.unknown_reference_resolvers = []
"""List of hook functions which assist in resolving references"""
self.document = document
"""The `nodes.document` object this Transformer is attached to."""
......@@ -149,6 +154,16 @@ class Transformer(TransformSpec):
self.add_transforms(component.default_transforms)
self.components[component.component_type] = component
self.sorted = 0
# Setup all of the reference resolvers for this transformer. Each
# component of this transformer is able to register its own helper
# functions to help resolve references.
unknown_reference_resolvers = []
for i in components:
unknown_reference_resolvers.extend(i.unknown_reference_resolvers)
decorated_list = [(f.priority, f) for f in unknown_reference_resolvers]
decorated_list.sort()
self.unknown_reference_resolvers.extend([f[1] for f in decorated_list])
def apply_transforms(self):
"""Apply all of the stored transforms, in priority order."""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -334,10 +334,10 @@ class DocInfo(Transform):
return 1
rcs_keyword_substitutions = [
(re.compile(r'\$' r'Date: (\d\d\d\d)/(\d\d)/(\d\d) [\d:]+ \$$',
(re.compile(r'\$' r'Date: (\d\d\d\d)/(\d\d)/(\d\d) [\d:]+ \$',
re.IGNORECASE), r'\1-\2-\3'),
(re.compile(r'\$' r'RCSfile: (.+),v \$$', re.IGNORECASE), r'\1'),
(re.compile(r'\$[a-zA-Z]+: (.+) \$$'), r'\1'),]
(re.compile(r'\$' r'RCSfile: (.+),v \$', re.IGNORECASE), r'\1'),
(re.compile(r'\$[a-zA-Z]+: (.+) \$'), r'\1'),]
def extract_authors(self, field, name, docinfo):
try:
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......
# Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -34,6 +34,7 @@ class SectNum(Transform):
def apply(self):
self.maxdepth = self.startnode.details.get('depth', sys.maxint)
self.startnode.parent.remove(self.startnode)
if self.document.settings.sectnum_xform:
self.update_section_numbers(self.document)
def update_section_numbers(self, node, prefix=(), depth=0):
......@@ -58,11 +59,11 @@ class Contents(Transform):
"""
This transform generates a table of contents from the entire document tree
or from a single branch. It locates "section" elements and builds them
into a nested bullet list, which is placed within a "topic". A title is
either explicitly specified, taken from the appropriate language module,
or omitted (local table of contents). The depth may be specified.
Two-way references between the table of contents and section titles are
generated (requires Writer support).
into a nested bullet list, which is placed within a "topic" created by the
contents directive. A title is either explicitly specified, taken from
the appropriate language module, or omitted (local table of contents).
The depth may be specified. Two-way references between the table of
contents and section titles are generated (requires Writer support).
This transform requires a startnode, which which contains generation
options and provides the location for the generated table of contents (the
......@@ -72,41 +73,26 @@ class Contents(Transform):
default_priority = 720
def apply(self):
topic = nodes.topic(CLASS='contents')
details = self.startnode.details
if details.has_key('class'):
topic.set_class(details['class'])
title = details['title']
if details.has_key('local'):
startnode = self.startnode.parent
startnode = self.startnode.parent.parent
# @@@ generate an error if the startnode (directive) not at
# section/document top-level? Drag it up until it is?
while not isinstance(startnode, nodes.Structural):
startnode = startnode.parent
else:
startnode = self.document
if not title:
title = nodes.title('', self.language.labels['contents'])
if title:
name = title.astext()
topic += title
else:
name = self.language.labels['contents']
name = nodes.fully_normalize_name(name)
if not self.document.has_name(name):
topic['name'] = name
self.document.note_implicit_target(topic)
self.toc_id = topic['id']
self.toc_id = self.startnode.parent['id']
if details.has_key('backlinks'):
self.backlinks = details['backlinks']
else:
self.backlinks = self.document.settings.toc_backlinks
contents = self.build_contents(startnode)
if len(contents):
topic += contents
self.startnode.parent.replace(self.startnode, topic)
self.startnode.parent.replace(self.startnode, contents)
else:
self.startnode.parent.remove(self.startnode)
self.startnode.parent.parent.remove(self.startnode.parent)
def build_contents(self, node, level=0):
level += 1
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -19,7 +19,7 @@ import sys
import os
import re
import time
from docutils import nodes, utils
from docutils import nodes, utils, languages
from docutils import ApplicationError, DataError
from docutils.transforms import Transform, TransformError
from docutils.transforms import parts, references, misc
......@@ -137,15 +137,24 @@ class Headers(Transform):
class Contents(Transform):
"""
Insert a table of contents transform placeholder into the document after
the RFC 2822 header.
Insert an empty table of contents topic and a transform placeholder into
the document after the RFC 2822 header.
"""
default_priority = 380
def apply(self):
pending = nodes.pending(parts.Contents, {'title': None})
self.document.insert(1, pending)
language = languages.get_language(self.document.settings.language_code)
name = language.labels['contents']
title = nodes.title('', name)
topic = nodes.topic('', title, CLASS='contents')
name = nodes.fully_normalize_name(name)
if not self.document.has_name(name):
topic['name'] = name
self.document.note_implicit_target(topic)
pending = nodes.pending(parts.Contents)
topic += pending
self.document.insert(1, topic)
self.document.note_pending(pending)
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -216,6 +216,12 @@ class IndirectHyperlinks(Transform):
refname = target['refname']
reftarget_id = self.document.nameids.get(refname)
if not reftarget_id:
# Check the unknown_reference_resolvers
for resolver_function in (self.document.transformer
.unknown_reference_resolvers):
if resolver_function(target):
break
else:
self.nonexistent_indirect_target(target)
return
reftarget = self.document.ids[reftarget_id]
......@@ -248,6 +254,10 @@ class IndirectHyperlinks(Transform):
reftarget.referenced = 1
def nonexistent_indirect_target(self, target):
if self.document.nameids.has_key(target['refname']):
self.indirect_target_error(target, 'which is a duplicate, and '
'cannot be used as a unique reference')
else:
self.indirect_target_error(target, 'which does not exist')
def circular_indirect_reference(self, target):
......@@ -353,7 +363,8 @@ class ExternalTargets(Transform):
try:
reflist = self.document.refnames[name]
except KeyError, instance:
if target.referenced:
# @@@ First clause correct???
if not isinstance(target, nodes.target) or target.referenced:
continue
msg = self.document.reporter.info(
'External hyperlink target "%s" is not referenced.'
......
# Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -111,6 +111,29 @@ class Messages(Transform):
self.document += section
class FilterMessages(Transform):
"""
Remove system messages below verbosity threshold.
"""
default_priority = 870
def apply(self):
visitor = SystemMessageFilterVisitor(self.document)
self.document.walk(visitor)
class SystemMessageFilterVisitor(nodes.SparseNodeVisitor):
def unknown_visit(self, node):
pass
def visit_system_message(self, node):
if node['level'] < self.document.reporter['writer'].report_level:
node.parent.remove(node)
class TestMessages(Transform):
"""
......@@ -136,7 +159,9 @@ class FinalChecks(Transform):
default_priority = 840
def apply(self):
visitor = FinalCheckVisitor(self.document)
visitor = FinalCheckVisitor(
self.document,
self.document.transformer.unknown_reference_resolvers)
self.document.walk(visitor)
if self.document.settings.expose_internals:
visitor = InternalAttributeExposer(self.document)
......@@ -145,6 +170,11 @@ class FinalChecks(Transform):
class FinalCheckVisitor(nodes.SparseNodeVisitor):
def __init__(self, document, unknown_reference_resolvers):
nodes.SparseNodeVisitor.__init__(self, document)
self.document = document
self.unknown_reference_resolvers = unknown_reference_resolvers
def unknown_visit(self, node):
pass
......@@ -154,6 +184,15 @@ class FinalCheckVisitor(nodes.SparseNodeVisitor):
refname = node['refname']
id = self.document.nameids.get(refname)
if id is None:
for resolver_function in self.unknown_reference_resolvers:
if resolver_function(node):
break
else:
if self.document.nameids.has_key(refname):
msg = self.document.reporter.error(
'Duplicate target name, cannot be used as a unique '
'reference: "%s".' % (node['refname']), base_node=node)
else:
msg = self.document.reporter.error(
'Unknown target name: "%s".' % (node['refname']),
base_node=node)
......
"""
`schemes` is a dictionary with lowercase URI addressing schemes as
keys and descriptions as values. It was compiled from the index at
http://www.w3.org/Addressing/schemes.html (revised 2001-08-20).
http://www.iana.org/assignments/uri-schemes (revised 2003-11-26)
and an older list at http://www.w3.org/Addressing/schemes.html.
"""
# Many values are blank and should be filled in with useful descriptions.
......@@ -26,10 +27,12 @@ schemes = {
'specialized to justify their own schemes'),
'fax': ('a connection to a terminal that can handle telefaxes '
'(facsimiles); RFC 2806'),
'feed' : 'NetNewsWire feed',
'file': 'Host-specific file names',
'finger': '',
'freenet': '',
'ftp': 'File Transfer Protocol',
'go': 'go; RFC3368',
'gopher': 'The Gopher Protocol',
'gsm-sms': ('Global System for Mobile Communications Short Message '
'Service'),
......@@ -40,16 +43,19 @@ schemes = {
'hnews': 'an HTTP-tunneling variant of the NNTP news protocol',
'http': 'Hypertext Transfer Protocol',
'https': 'HTTP over SSL',
'hydra': 'SubEthaEdit URI. See http://www.codingmonkeys.de/subethaedit.',
'iioploc': 'Internet Inter-ORB Protocol Location?',
'ilu': 'Inter-Language Unification',
'im': 'Instant Messaging',
'imap': 'Internet Message Access Protocol',
'ior': 'CORBA interoperable object reference',
'ipp': 'Internet Printing Protocol',
'irc': 'Internet Relay Chat',
'iseek' : 'See www.ambrosiasw.com; a little util for OS X.',
'jar': 'Java archive',
'javascript': ('JavaScript code; evaluates the expression after the '
'colon'),
'jdbc': '',
'jdbc': 'JDBC connection URI.',
'ldap': 'Lightweight Directory Access Protocol',
'lifn': '',
'livescript': '',
......@@ -62,6 +68,7 @@ schemes = {
'mocha': '',
'modem': ('a connection to a terminal that can handle incoming data '
'calls; RFC 2806'),
'mupdate': 'Mailbox Update (MUPDATE) Protocol',
'news': 'USENET news',
'nfs': 'Network File System protocol',
'nntp': 'USENET news using NNTP access',
......@@ -69,8 +76,10 @@ schemes = {
'phone': '',
'pop': 'Post Office Protocol',
'pop3': 'Post Office Protocol v3',
'pres': 'Presence',
'printer': '',
'prospero': 'Prospero Directory Service',
'rdar' : 'URLs found in Darwin source (http://www.opensource.apple.com/darwinsource/).',
'res': '',
'rtsp': 'real time streaming protocol',
'rvp': '',
......@@ -80,8 +89,12 @@ schemes = {
'service': 'service location',
'shttp': 'secure hypertext transfer protocol',
'sip': 'Session Initiation Protocol',
'smb': '',
'sips': 'secure session intitiaion protocol',
'smb': 'SAMBA filesystems.',
'snews': 'For NNTP postings via SSL',
'soap.beep': '',
'soap.beeps': '',
'ssh': 'Reference to interactive sessions via ssh.',
't120': 'real time data conferencing (audiographics)',
'tcp': '',
'tel': ('a connection to a terminal that handles normal voice '
......@@ -90,6 +103,7 @@ schemes = {
'RFC 2806.'),
'telephone': 'telephone',
'telnet': 'Reference to interactive sessions',
'tftp': 'Trivial File Transfer Protocol',
'tip': 'Transaction Internet Protocol',
'tn3270': 'Interactive 3270 emulation sessions',
'tv': '',
......@@ -101,5 +115,8 @@ schemes = {
'wais': 'Wide Area Information Servers',
'whodp': '',
'whois++': 'Distributed directory service.',
'x-man-page': 'Opens man page in Terminal.app on OS X (see macosxhints.com)',
'xmlrpc.beep': '',
'xmlrpc.beeps': '',
'z39.50r': 'Z39.50 Retrieval',
'z39.50s': 'Z39.50 Session',}
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:04 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:39 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -325,6 +325,8 @@ def assemble_option_dict(option_list, options_spec):
options = {}
for name, value in option_list:
convertor = options_spec[name] # raises KeyError if unknown
if convertor is None:
raise KeyError(name) # or if explicitly disabled
if options.has_key(name):
raise DuplicateOptionError('duplicate option "%s"' % name)
try:
......@@ -406,7 +408,7 @@ def clean_rcs_keywords(paragraph, keyword_substitutions):
if len(paragraph) == 1 and isinstance(paragraph[0], nodes.Text):
textnode = paragraph[0]
for pattern, substitution in keyword_substitutions:
match = pattern.match(textnode.data)
match = pattern.search(textnode.data)
if match:
textnode.data = pattern.sub(substitution, textnode.data)
return
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -26,25 +26,43 @@ class Writer(Component):
Each writer must support all standard node types listed in
`docutils.nodes.node_class_names`.
Call `write()` to process a document.
The `write()` method is the main entry point.
"""
component_type = 'writer'
config_section = 'writers'
document = None
"""The document to write."""
"""The document to write (Docutils doctree); set by `write`."""
output = None
"""Final translated form of `document`; set by `translate`."""
language = None
"""Language module for the document."""
"""Language module for the document; set by `write`."""
destination = None
"""`docutils.io` IO object; where to write the document."""
"""`docutils.io` IO object; where to write the document. Set by `write`."""
def __init__(self):
"""Initialize the Writer instance."""
# Currently only used by HTML writer for output fragments:
self.parts = {}
"""Mapping of document part names to fragments of `self.output`.
Values are Unicode strings; encoding is up to the client. The 'whole'
key should contain the entire document output.
"""
def write(self, document, destination):
"""
Process a document into its final form.
Translate `document` (a Docutils document tree) into the Writer's
native format, and write it out to its `destination` (a
`docutils.io.Output` subclass object).
Normally not overridden or extended in subclasses.
"""
self.document = document
self.language = languages.get_language(
document.settings.language_code)
......@@ -55,9 +73,10 @@ class Writer(Component):
def translate(self):
"""
Override to do final document tree translation.
Do final translation of `self.document` into `self.output`.
Called from `write`. Override in subclasses.
This is usually done with a `docutils.nodes.NodeVisitor` subclass, in
Usually done with a `docutils.nodes.NodeVisitor` subclass, in
combination with a call to `docutils.nodes.Node.walk()` or
`docutils.nodes.Node.walkabout()`. The ``NodeVisitor`` subclass must
support all standard elements (listed in
......@@ -66,6 +85,10 @@ class Writer(Component):
"""
raise NotImplementedError('subclass must override this method')
def assemble_parts(self):
"""Assemble the `self.parts` dictionary. Extend in subclasses."""
self.parts['whole'] = self.output
_writer_aliases = {
'html': 'html4css1',
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain.
"""
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -22,6 +22,10 @@ import os.path
import time
import re
from types import ListType
try:
import Image # check for the Python Imaging Library
except ImportError:
Image = None
import docutils
from docutils import frontend, nodes, utils, writers, languages
......@@ -55,6 +59,11 @@ class Writer(writers.Writer):
'stylesheet, do not embed it.',
['--embed-stylesheet'],
{'action': 'store_true', 'validator': frontend.validate_boolean}),
('Specify the initial header level. Default is 1 for "<h1>". '
'Does not affect document title & subtitle (see --no-doc-title).',
['--initial-header-level'],
{'choices': '1 2 3 4 5 6'.split(), 'default': '1',
'metavar': '<level>'}),
('Format for footnote references: one of "superscript" or '
'"brackets". Default is "superscript".',
['--footnote-references'],
......@@ -85,9 +94,6 @@ class Writer(writers.Writer):
config_section = 'html4css1 writer'
config_section_dependencies = ('writers',)
output = None
"""Final translated form of `document`."""
def __init__(self):
writers.Writer.__init__(self)
self.translator_class = HTMLTranslator
......@@ -96,14 +102,17 @@ class Writer(writers.Writer):
visitor = self.translator_class(self.document)
self.document.walkabout(visitor)
self.output = visitor.astext()
self.head_prefix = visitor.head_prefix
self.stylesheet = visitor.stylesheet
self.head = visitor.head
self.body_prefix = visitor.body_prefix
self.body_pre_docinfo = visitor.body_pre_docinfo
self.docinfo = visitor.docinfo
self.body = visitor.body
self.body_suffix = visitor.body_suffix
self.visitor = visitor
for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
'body_pre_docinfo', 'docinfo', 'body', 'fragment',
'body_suffix'):
setattr(self, attr, getattr(visitor, attr))
def assemble_parts(self):
writers.Writer.assemble_parts(self)
for part in ('title', 'subtitle', 'docinfo', 'body', 'header',
'footer', 'meta', 'stylesheet', 'fragment'):
self.parts[part] = ''.join(getattr(self.visitor, part))
class HTMLTranslator(nodes.NodeVisitor):
......@@ -143,7 +152,7 @@ class HTMLTranslator(nodes.NodeVisitor):
- Regardless of the above, in definitions, table cells, field bodies,
option descriptions, and list items, mark the first child with
'class="first"' and the last child with 'class="last"'. The stylesheet
sets the margins (top & bottom respecively) to 0 for these elements.
sets the margins (top & bottom respectively) to 0 for these elements.
The ``no_compact_lists`` setting (``--no-compact-lists`` command-line
option) disables list whitespace optimization.
......@@ -171,11 +180,12 @@ class HTMLTranslator(nodes.NodeVisitor):
self.settings = settings = document.settings
lcode = settings.language_code
self.language = languages.get_language(lcode)
self.meta = [self.content_type % settings.output_encoding,
self.generator % docutils.__version__]
self.head_prefix = [
self.doctype,
self.html_head % (lcode, lcode),
self.content_type % settings.output_encoding,
self.generator % docutils.__version__]
self.html_head % (lcode, lcode)]
self.head_prefix.extend(self.meta)
if settings.xml_declaration:
self.head_prefix.insert(0, self.xml_declaration
% settings.output_encoding)
......@@ -192,11 +202,17 @@ class HTMLTranslator(nodes.NodeVisitor):
else:
self.stylesheet = []
self.body_prefix = ['</head>\n<body>\n']
# document title, subtitle display
self.body_pre_docinfo = []
# author, date, etc.
self.docinfo = []
self.body = []
self.fragment = []
self.body_suffix = ['</body>\n</html>\n']
self.section_level = 0
self.initial_header_level = int(settings.initial_header_level)
# A heterogenous stack used in conjunction with the tree traversal.
# Make sure that the pops correspond to the pushes:
self.context = []
self.topic_class = ''
self.colspecs = []
......@@ -204,6 +220,11 @@ class HTMLTranslator(nodes.NodeVisitor):
self.compact_simple = None
self.in_docinfo = None
self.in_sidebar = None
self.title = []
self.subtitle = []
self.header = []
self.footer = []
self.in_document_title = 0
def get_stylesheet_reference(self, relative_to=None):
settings = self.settings
......@@ -267,8 +288,11 @@ class HTMLTranslator(nodes.NodeVisitor):
parts.append('%s="%s"' % (name.lower(),
self.attval(' '.join(values))))
else:
parts.append('%s="%s"' % (name.lower(),
self.attval(unicode(value))))
try:
uval = unicode(value)
except TypeError: # for Python 2.1 compatibility:
uval = unicode(str(value))
parts.append('%s="%s"' % (name.lower(), self.attval(uval)))
return '<%s%s>%s' % (' '.join(parts), infix, suffix)
def emptytag(self, node, tagname, suffix='\n', **attributes):
......@@ -307,7 +331,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'div',
CLASS=(name or 'admonition')))
if name:
self.body.append('<p class="admonition-title">'
self.body.append('<p class="admonition-title first">'
+ self.language.labels[name] + '</p>\n')
def depart_admonition(self, node=None):
......@@ -519,14 +543,14 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('</tbody>\n</table>\n')
self.in_docinfo = None
start = self.context.pop()
self.body_pre_docinfo = self.body[:start]
self.docinfo = self.body[start:]
self.body = []
def visit_docinfo_item(self, node, name, meta=1):
if meta:
self.head.append('<meta name="%s" content="%s" />\n'
% (name, self.attval(node.astext())))
meta_tag = '<meta name="%s" content="%s" />\n' \
% (name, self.attval(node.astext()))
self.add_meta(meta_tag)
self.body.append(self.starttag(node, 'tr', ''))
self.body.append('<th class="docinfo-name">%s:</th>\n<td>'
% self.language.labels[name])
......@@ -546,9 +570,14 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('\n</pre>\n')
def visit_document(self, node):
self.body.append(self.starttag(node, 'div', CLASS='document'))
# empty or untitled document?
if not len(node) or not isinstance(node[0], nodes.title):
# for XHTML conformance, modulo IE6 appeasement:
self.head.insert(0, '<title></title>\n')
def depart_document(self, node):
self.fragment.extend(self.body)
self.body.insert(0, self.starttag(node, 'div', CLASS='document'))
self.body.append('</div>\n')
def visit_emphasis(self, node):
......@@ -671,6 +700,7 @@ class HTMLTranslator(nodes.NodeVisitor):
footer = (['<hr class="footer" />\n',
self.starttag(node, 'div', CLASS='footer')]
+ self.body[start:] + ['</div>\n'])
self.footer.extend(footer)
self.body_suffix[:0] = footer
del self.body[start:]
......@@ -739,9 +769,11 @@ class HTMLTranslator(nodes.NodeVisitor):
def depart_header(self, node):
start = self.context.pop()
self.body_prefix.append(self.starttag(node, 'div', CLASS='header'))
self.body_prefix.extend(self.body[start:])
self.body_prefix.append('<hr />\n</div>\n')
header = [self.starttag(node, 'div', CLASS='header')]
header.extend(self.body[start:])
header.append('<hr />\n</div>\n')
self.body_prefix.extend(header)
self.header = header
del self.body[start:]
def visit_hint(self, node):
......@@ -756,6 +788,27 @@ class HTMLTranslator(nodes.NodeVisitor):
del atts['class'] # prevent duplication with node attrs
atts['src'] = atts['uri']
del atts['uri']
if atts.has_key('scale'):
if Image and not (atts.has_key('width')
and atts.has_key('height')):
try:
im = Image.open(str(atts['src']))
except (IOError, # Source image can't be found or opened
UnicodeError): # PIL doesn't like Unicode paths.
pass
else:
if not atts.has_key('width'):
atts['width'] = im.size[0]
if not atts.has_key('height'):
atts['height'] = im.size[1]
del im
if atts.has_key('width'):
atts['width'] = int(round(atts['width']
* (float(atts['scale']) / 100)))
if atts.has_key('height'):
atts['height'] = int(round(atts['height']
* (float(atts['scale']) / 100)))
del atts['scale']
if not atts.has_key('alt'):
atts['alt'] = atts['src']
if isinstance(node.parent, nodes.TextElement):
......@@ -837,11 +890,16 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('\n</pre>\n')
def visit_meta(self, node):
self.head.append(self.emptytag(node, 'meta', **node.attributes))
meta = self.emptytag(node, 'meta', **node.attributes)
self.add_meta(meta)
def depart_meta(self, node):
pass
def add_meta(self, tag):
self.meta.append(tag)
self.head.append(tag)
def visit_note(self, node):
self.visit_admonition(node, 'note')
......@@ -941,6 +999,11 @@ class HTMLTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_reference(self, node):
if isinstance(node.parent, nodes.TextElement):
self.context.append('')
else:
self.body.append('<p>')
self.context.append('</p>\n')
if node.has_key('refuri'):
href = node['refuri']
elif node.has_key('refid'):
......@@ -952,6 +1015,7 @@ class HTMLTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
self.body.append('</a>')
self.body.append(self.context.pop())
def visit_revision(self, node):
self.visit_docinfo_item(node, 'revision', meta=None)
......@@ -1017,12 +1081,18 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'p', '',
CLASS='sidebar-subtitle'))
self.context.append('</p>\n')
else:
elif isinstance(node.parent, nodes.document):
self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
self.context.append('</h2>\n')
self.in_document_title = len(self.body)
def depart_subtitle(self, node):
self.body.append(self.context.pop())
if self.in_document_title:
self.subtitle = self.body[self.in_document_title:-1]
self.in_document_title = 0
self.body_pre_docinfo.extend(self.body)
del self.body[:]
def visit_superscript(self, node):
self.body.append(self.starttag(node, 'sup', ''))
......@@ -1135,27 +1205,35 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_title(self, node):
"""Only 6 section levels are supported by HTML."""
check_id = 0
close_tag = '</p>\n'
if isinstance(node.parent, nodes.topic):
self.body.append(
self.starttag(node, 'p', '', CLASS='topic-title'))
self.starttag(node, 'p', '', CLASS='topic-title first'))
check_id = 1
elif isinstance(node.parent, nodes.sidebar):
self.body.append(
self.starttag(node, 'p', '', CLASS='sidebar-title'))
self.starttag(node, 'p', '', CLASS='sidebar-title first'))
check_id = 1
elif isinstance(node.parent, nodes.admonition):
self.body.append(
self.starttag(node, 'p', '', CLASS='admonition-title'))
self.starttag(node, 'p', '', CLASS='admonition-title first'))
check_id = 1
elif isinstance(node.parent, nodes.table):
self.body.append(
self.starttag(node, 'caption', ''))
check_id = 1
close_tag = '</caption>\n'
elif self.section_level == 0:
# document title
self.head.append('<title>%s</title>\n'
% self.encode(node.astext()))
self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
self.context.append('</h1>\n')
self.in_document_title = len(self.body)
else:
h_level = self.section_level + self.initial_header_level - 1
self.body.append(
self.starttag(node, 'h%s' % self.section_level, ''))
self.starttag(node, 'h%s' % h_level, ''))
atts = {}
if node.parent.hasattr('id'):
atts['name'] = node.parent['id']
......@@ -1163,17 +1241,22 @@ class HTMLTranslator(nodes.NodeVisitor):
atts['class'] = 'toc-backref'
atts['href'] = '#' + node['refid']
self.body.append(self.starttag({}, 'a', '', **atts))
self.context.append('</a></h%s>\n' % (self.section_level))
self.context.append('</a></h%s>\n' % (h_level))
if check_id:
if node.parent.hasattr('id'):
self.body.append(
self.starttag({}, 'a', '', name=node.parent['id']))
self.context.append('</a></p>\n')
self.context.append('</a>' + close_tag)
else:
self.context.append('</p>\n')
self.context.append(close_tag)
def depart_title(self, node):
self.body.append(self.context.pop())
if self.in_document_title:
self.title = self.body[self.in_document_title:-1]
self.in_document_title = 0
self.body_pre_docinfo.extend(self.body)
del self.body[:]
def visit_title_reference(self, node):
self.body.append(self.starttag(node, 'cite', ''))
......
"""
:Author: Engelbert Gruber
:Contact: grubert@users.sourceforge.net
:Revision: $Revision: 1.3 $
:Date: $Date: 2003/11/30 15:06:09 $
:Revision: $Revision: 1.1.2.3.8.1 $
:Date: $Date: 2004/05/12 19:57:57 $
:Copyright: This module has been placed in the public domain.
LaTeX2e document tree Writer.
......@@ -11,7 +11,7 @@ LaTeX2e document tree Writer.
__docformat__ = 'reStructuredText'
# code contributions from several people included, thanks too all.
# some named: David Abrahams, Julien Letessier, who is missing.
# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others.
#
# convention deactivate code by two # e.g. ##.
......@@ -76,13 +76,43 @@ class Writer(writers.Writer):
['--use-latex-toc'],
{'default': 0, 'action': 'store_true',
'validator': frontend.validate_boolean}),
('Let LaTeX print author and date, donot show it in docutils document info.',
('Let LaTeX print author and date, do not show it in docutils '
'document info.',
['--use-latex-docinfo'],
{'default': 0, 'action': 'store_true',
'validator': frontend.validate_boolean}),
('Color of any hyperlinks embedded in text '
'(default: "blue", "0" to disable).',
['--hyperlink-color'], {'default': 'blue'}),))
['--hyperlink-color'], {'default': 'blue'}),
('Enable compound enumerators for nested enumerated lists '
'(e.g. "1.2.a.ii"). Default: disabled.',
['--compound-enumerators'],
{'default': None, 'action': 'store_true',
'validator': frontend.validate_boolean}),
('Disable compound enumerators for nested enumerated lists. This is '
'the default.',
['--no-compound-enumerators'],
{'action': 'store_false', 'dest': 'compound_enumerators'}),
('Enable section ("." subsection ...) prefixes for compound '
'enumerators. This has no effect without --compound-enumerators. '
'Default: disabled.',
['--section-prefix-for-enumerators'],
{'default': None, 'action': 'store_true',
'validator': frontend.validate_boolean}),
('Disable section prefixes for compound enumerators. '
'This is the default.',
['--no-section-prefix-for-enumerators'],
{'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}),
('Set the separator between section number and enumerator '
'for compound enumerated lists. Default is "-".',
['--section-enumerator-separator'],
{'default': '-', 'metavar': '<char>'}),
('When possibile, use verbatim for literal-blocks.'
'Default is to always use the mbox environment.',
['--use-verbatim-when-possible'],
{'default': 0, 'action': 'store_true',
'validator': frontend.validate_boolean}),
))
settings_defaults = {'output_encoding': 'latin-1'}
......@@ -245,10 +275,38 @@ latex_headings = {
'% some commands, that could be overwritten in the style file.\n'
'\\newcommand{\\rubric}[1]'
'{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
'\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
'% end of "some commands"\n',
]
}
class DocumentClass:
"""Details of a LaTeX document class."""
# BUG: LaTeX has no deeper sections (actually paragrah is no
# section either).
_class_sections = {
'book': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
'report': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
'article': ( 'section', 'subsection', 'subsubsection' ),
}
_deepest_section = 'subsubsection'
def __init__(self, document_class):
self.document_class = document_class
def section(self, level):
""" Return the section name at the given level for the specific
document class.
Level is 1,2,3..., as level 0 is the title."""
sections = self._class_sections[self.document_class]
if level <= len(sections):
return sections[level-1]
else:
return self._deepest_section
class LaTeXTranslator(nodes.NodeVisitor):
# When options are given to the documentclass, latex will pass them
......@@ -277,6 +335,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
# list environment for docinfo. else tabularx
use_optionlist_for_docinfo = 0 # NOT YET IN USE
# Use compound enumerations (1.A.1.)
compound_enumerators = 0
# If using compound enumerations, include section information.
section_prefix_for_enumerators = 0
# This is the character that separates the section ("." subsection ...)
# prefix from the regular list enumerator.
section_enumerator_separator = '-'
# default link color
hyperlink_color = "blue"
......@@ -287,6 +355,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.use_latex_docinfo = settings.use_latex_docinfo
self.use_latex_footnotes = settings.use_latex_footnotes
self.hyperlink_color = settings.hyperlink_color
self.compound_enumerators = settings.compound_enumerators
self.section_prefix_for_enumerators = (
settings.section_prefix_for_enumerators)
self.section_enumerator_separator = (
settings.section_enumerator_separator.replace('_', '\\_'))
if self.hyperlink_color == '0':
self.hyperlink_color = 'black'
self.colorlinks = 'false'
......@@ -302,6 +375,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.babel.get_language():
self.d_options += ',%s' % \
self.babel.get_language()
self.d_class = DocumentClass(settings.documentclass)
self.head_prefix = [
self.latex_head % (self.d_options,self.settings.documentclass),
'\\usepackage{babel}\n', # language is in documents settings.
......@@ -333,7 +409,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'\\newlength{\\docinfowidth}\n',
'\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
# linewidth of current environment, so tables are not wider
# than the sidebar: using locallinewidth seams to defer evaluation
# than the sidebar: using locallinewidth seems to defer evaluation
# of linewidth, this is fixing it.
'\\newlength{\\locallinewidth}\n',
# will be set later.
......@@ -368,6 +444,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.topic_class = ''
# column specification for tables
self.colspecs = []
self.table_caption = None
# do we have one or more authors
self.author_stack = None
# Flags to encode
......@@ -383,11 +460,24 @@ class LaTeXTranslator(nodes.NodeVisitor):
# enumeration is done by list environment.
self._enum_cnt = 0
# Stack of section counters so that we don't have to use_latex_toc.
# This will grow and shrink as processing occurs.
# Initialized for potential first-level sections.
self._section_number = [0]
# The current stack of enumerations so that we can expand
# them into a compound enumeration
self._enumeration_counters = []
# docinfo.
self.docinfo = None
# inside literal block: no quote mangling.
self.literal_block = 0
self.literal_block_stack = []
self.literal = 0
# true when encoding in math mode
self.mathmode = 0
def get_stylesheet_reference(self):
if self.settings.stylesheet_path:
......@@ -465,7 +555,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# then dollar
text = text.replace("$", '{\\$}')
if not ( self.literal_block or self.literal ):
if not ( self.literal_block or self.literal or self.mathmode ):
# the vertical bar: in mathmode |,\vert or \mid
# in textmode \textbar
text = text.replace("|", '{\\textbar}')
......@@ -492,7 +582,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
# ! LaTeX Error: There's no line here to end.
text = text.replace("\n", '~\\\\\n')
elif self.mbox_newline:
text = text.replace("\n", '}\\\\\n\\mbox{')
if self.literal_block:
closings = "}" * len(self.literal_block_stack)
openings = "".join(self.literal_block_stack)
else:
closings = ""
openings = ""
text = text.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings,openings))
if self.insert_none_breaking_blanks:
text = text.replace(' ', '~')
# unicode !!!
......@@ -528,14 +624,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_address(self, node):
self.depart_docinfo_item(node)
def visit_admonition(self, node, name):
def visit_admonition(self, node, name=''):
self.body.append('\\begin{center}\\begin{sffamily}\n')
self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
if name:
self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n');
self.body.append('\\vspace{2mm}\n')
def depart_admonition(self):
def depart_admonition(self, node=None):
self.body.append('}}\n') # end parbox fbox
self.body.append('\\end{sffamily}\n\\end{center}\n');
......@@ -582,6 +679,24 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.body.append( '\\end{itemize}\n' )
# Imperfect superscript/subscript handling: mathmode italicizes
# all letters by default.
def visit_superscript(self, node):
self.body.append('$^{')
self.mathmode = 1
def depart_superscript(self, node):
self.body.append('}$')
self.mathmode = 0
def visit_subscript(self, node):
self.body.append('$_{')
self.mathmode = 1
def depart_subscript(self, node):
self.body.append('}$')
self.mathmode = 0
def visit_caption(self, node):
self.body.append( '\\caption{' )
......@@ -601,11 +716,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.depart_footnote(node)
def visit_title_reference(self, node):
# BUG title-references are what?
pass
self.body.append( '\\titlereference{' )
def depart_title_reference(self, node):
pass
self.body.append( '}' )
def visit_citation_reference(self, node):
href = ''
......@@ -767,9 +881,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_emphasis(self, node):
self.body.append('\\emph{')
self.literal_block_stack.append('\\emph{')
def depart_emphasis(self, node):
self.body.append('}')
self.literal_block_stack.pop()
def visit_entry(self, node):
# cell separation
......@@ -781,13 +897,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
# multi{row,column}
if node.has_key('morerows') and node.has_key('morecols'):
raise NotImplementedError('LaTeX can\'t handle cells that'
raise NotImplementedError('LaTeX can\'t handle cells that '
'span multiple rows *and* columns, sorry.')
atts = {}
if node.has_key('morerows'):
raise NotImplementedError('multiple rows are not working (yet), sorry.')
count = node['morerows'] + 1
self.body.append('\\multirow{%d}*{' % count)
self.context.append('}')
# BUG following rows must have empty cells.
elif node.has_key('morecols'):
# the vertical bar before column is missing if it is the first column.
# the one after always.
......@@ -830,13 +948,22 @@ class LaTeXTranslator(nodes.NodeVisitor):
enum_prefix = ""
if node.has_key('prefix'):
enum_prefix = node['prefix']
if self.compound_enumerators:
pref = ""
if self.section_prefix_for_enumerators and self.section_level:
for i in range(self.section_level):
pref += '%d.' % self._section_number[i]
pref = pref[:-1] + self.section_enumerator_separator
enum_prefix += pref
for counter in self._enumeration_counters:
enum_prefix += counter + '.'
enum_type = "arabic"
if node.has_key('enumtype'):
enum_type = node['enumtype']
if enum_style.has_key(enum_type):
enum_type = enum_style[enum_type]
counter_name = "listcnt%d" % self._enum_cnt;
self._enumeration_counters.append("\\%s{%s}" % (enum_type,counter_name))
self.body.append('\\newcounter{%s}\n' % counter_name)
self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
(enum_prefix,enum_type,counter_name,enum_suffix))
......@@ -852,6 +979,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_enumerated_list(self, node):
self.body.append('\\end{list}\n')
self._enumeration_counters.pop()
def visit_error(self, node):
self.visit_admonition(node, 'error')
......@@ -964,6 +1092,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
return
self.body.append('}%s' % self.context.pop())
# elements generated by the framework e.g. section numbers.
def visit_generated(self, node):
pass
......@@ -987,17 +1116,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.depart_admonition()
def visit_image(self, node):
attrs = node.attributes.copy()
attrs = node.attributes
pre = [] # in reverse order
post = ['\\includegraphics{%s}' % attrs['uri']]
def prepost(pre_append, post_append):
pre.append(pre_append)
post.append(post_append)
inline = isinstance(node.parent, nodes.TextElement)
if 'scale' in attrs:
# Could also be done with ``scale`` option to
# ``\includegraphics``; doing it this way for consistency.
prepost('\\scalebox{%f}{' % (attrs['scale'] / 100.0,), '}')
pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
post.append('}')
if 'align' in attrs:
align_prepost = {
# By default latex aligns the top of an image.
......@@ -1011,11 +1138,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
(0, 'left'): ('{', '\\hfill}'),
(0, 'right'): ('{\\hfill', '}'),}
try:
prepost(*align_prepost[inline, attrs['align']])
pre.append(align_prepost[inline, attrs['align']][0])
post.append(align_prepost[inline, attrs['align']][1])
except KeyError:
pass # complain here?
pass # XXX complain here?
if not inline:
prepost('\n', '\n')
pre.append('\n')
post.append('\n')
pre.reverse()
self.body.extend(pre + post)
......@@ -1054,6 +1183,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
* whitespace (including linebreaks) is significant
* inline markup is supported.
* serif typeface
mbox would stop LaTeX from wrapping long lines.
"""
self.body.append('\\begin{flushleft}\n')
self.insert_none_breaking_blanks = 1
......@@ -1074,7 +1205,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\n\\end{flushleft}\n')
def visit_list_item(self, node):
self.body.append('\\item ')
# HACK append "{}" in case the next character is "[", which would break
# LaTeX's list environment (no numbering and the "[" is not printed).
self.body.append('\\item {} ')
def depart_list_item(self, node):
self.body.append('\n')
......@@ -1089,30 +1222,50 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_literal_block(self, node):
"""
.. parsed-literal::
Render a literal-block.
Literal blocks are used for "::"-prefixed literal-indented
blocks of text, where the inline markup is not recognized,
but are also the product of the parsed-literal directive,
where the markup is respected.
mbox stops LaTeX from wrapping long lines.
"""
# typically in a typewriter/monospaced typeface.
# care must be taken with the text, because inline markup is recognized.
# In both cases, we want to use a typewriter/monospaced typeface.
# For "real" literal-blocks, we can use \verbatim, while for all
# the others we must use \mbox.
#
# possibilities:
# * verbatim: is no possibility, as inline markup does not work.
# * obey..: is from julien and never worked for me (grubert).
self.use_for_literal_block = "mbox"
# We can distinguish between the two kinds by the number of
# siblings the compose this node: if it is composed by a
# single element, it's surely is either a real one, otherwise
# it's a parsed-literal that does not contain any markup.
#
if (self.settings.use_verbatim_when_possible and (len(node) == 1)
# in case of a parsed-literal containing just a "**bold**" word:
and isinstance(node[0], nodes.Text)):
self.verbatim = 1
self.body.append('\\begin{verbatim}\n')
else:
self.literal_block = 1
if (self.use_for_literal_block == "mbox"):
self.mbox_newline = 1
self.insert_none_breaking_blanks = 1
self.body.append('\\begin{ttfamily}\\begin{flushleft}\n\\mbox{')
else:
self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
self.body.append('\\begin{ttfamily}\\begin{flushleft}\n')
self.mbox_newline = 1
if self.mbox_newline:
self.body.append('\\mbox{')
# * obey..: is from julien and never worked for me (grubert).
# self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
def depart_literal_block(self, node):
if (self.use_for_literal_block == "mbox"):
self.body.append('}\n\\end{flushleft}\\end{ttfamily}\n')
if self.verbatim:
self.body.append('\n\\end{verbatim}\n')
self.verbatim = 0
else:
if self.mbox_newline:
self.body.append('}')
self.body.append('\n\\end{flushleft}\\end{ttfamily}\n')
self.insert_none_breaking_blanks = 0
self.mbox_newline = 0
else:
self.body.append('}\n')
# obey end: self.body.append('}\n')
self.literal_block = 0
def visit_meta(self, node):
......@@ -1226,13 +1379,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# BUG: hash_char "#" is trouble some in LaTeX.
# mbox and other environment do not like the '#'.
hash_char = '\\#'
if node.has_key('refuri'):
href = node['refuri']
href = node['refuri'].replace('#',hash_char)
elif node.has_key('refid'):
href = hash_char + node['refid']
elif node.has_key('refname'):
href = hash_char + self.document.nameids[node['refname']]
else:
raise AssertionError('Unknown reference.')
self.body.append('\\href{%s}{' % href)
def depart_reference(self, node):
......@@ -1250,11 +1404,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_row(self, node):
self.context.pop() # remove cell counter
self.body.append(' \\\\ \\hline\n')
# BUG if multirow cells \cline{x-y}
def visit_section(self, node):
self.section_level += 1
# Initialize counter for potential subsections:
self._section_number.append(0)
# Counter for this section's level (initialized by parent section):
self._section_number[self.section_level - 1] += 1
def depart_section(self, node):
# Remove counter for potential subsections:
self._section_number.pop()
self.section_level -= 1
def visit_sidebar(self, node):
......@@ -1292,9 +1453,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_strong(self, node):
self.body.append('\\textbf{')
self.literal_block_stack.append('\\textbf{')
def depart_strong(self, node):
self.body.append('}')
self.literal_block_stack.pop()
def visit_substitution_definition(self, node):
raise nodes.SkipNode
......@@ -1328,13 +1491,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
Return column specification for longtable.
Assumes reST line length being 80 characters.
Table width is hairy.
=== ===
ABC DEF
=== ===
usually gets to narrow, therefore we add 1 (fiddlefactor).
"""
width = 80
total_width = 0.0
# first see if we get too wide.
for node in self.colspecs:
colwidth = float(node['colwidth']) / width
colwidth = float(node['colwidth']+1) / width
total_width += colwidth
# donot make it full linewidth
factor = 0.93
......@@ -1343,8 +1513,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
latex_table_spec = ""
for node in self.colspecs:
colwidth = factor * float(node['colwidth']) / width
latex_table_spec += "|p{%.2f\\locallinewidth}" % colwidth
colwidth = factor * float(node['colwidth']+1) / width
latex_table_spec += "|p{%.2f\\locallinewidth}" % (colwidth+0.005)
self.colspecs = []
return latex_table_spec+"|"
......@@ -1369,6 +1539,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
def table_preamble(self):
if self.use_longtable:
self.body.append('{%s}\n' % self.get_colspecs())
if self.table_caption:
self.body.append('\\caption{%s}\\\\\n' % self.table_caption)
self.table_caption = None
else:
if self.context[-1] != 'table_sentinel':
self.body.append('{%s}' % ('|X' * self.context.pop() + '|'))
......@@ -1476,6 +1649,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
elif isinstance(node.parent, nodes.sidebar):
self.body.append('\\textbf{\\large ')
self.context.append('}\n\\smallskip\n')
elif isinstance(node.parent, nodes.table):
# caption must be written after column spec
self.table_caption = node.astext()
raise nodes.SkipNode
elif self.section_level == 0:
# document title
self.title = self.encode(node.astext())
......@@ -1487,21 +1664,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('%' + '_' * 75)
self.body.append('\n\n')
self.bookmark(node)
# section_level 0 is title and handled above.
# BUG: latex has no deeper sections (actually paragrah is no section either).
if self.use_latex_toc:
section_star = ""
else:
section_star = "*"
if (self.section_level<=3): # 1,2,3
self.body.append('\\%ssection%s{' % ('sub'*(self.section_level-1),section_star))
elif (self.section_level==4):
#self.body.append('\\paragraph*{')
self.body.append('\\subsubsection%s{' % (section_star))
else:
#self.body.append('\\subparagraph*{')
self.body.append('\\subsubsection%s{' % (section_star))
# BUG: self.body.append( '\\label{%s}\n' % name)
section_name = self.d_class.section(self.section_level)
self.body.append('\\%s%s{' % (section_name, section_star))
self.context.append('}\n')
def depart_title(self, node):
......
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain.
"""
......@@ -88,15 +88,6 @@ class Writer(html4css1.Writer):
class HTMLTranslator(html4css1.HTMLTranslator):
def get_stylesheet_reference(self, relative_to=None):
settings = self.settings
if relative_to == None:
relative_to = settings._destination
if settings.stylesheet_path:
return utils.relative_path(relative_to, settings.stylesheet_path)
else:
return settings.stylesheet
def depart_field_list(self, node):
html4css1.HTMLTranslator.depart_field_list(self, node)
if node.get('class') == 'rfc2822':
......
# Authors: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $
# Date: $Date: 2003/11/30 15:06:09 $
# Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2004/05/12 19:57:57 $
# 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