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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:37 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -51,12 +51,14 @@ Subpackages: ...@@ -51,12 +51,14 @@ Subpackages:
__docformat__ = 'reStructuredText' __docformat__ = 'reStructuredText'
__version__ = '0.3.1' __version__ = '0.3.4'
"""``major.minor.micro`` version number. The micro number is bumped any time """``major.minor.micro`` version number. The micro number is bumped
there's a change in the API incompatible with one of the front ends or any time there's a change in the API incompatible with one of the
significant new functionality. The minor number is bumped whenever there is a 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 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 class ApplicationError(StandardError): pass
...@@ -122,6 +124,22 @@ class TransformSpec: ...@@ -122,6 +124,22 @@ class TransformSpec:
default_transforms = () default_transforms = ()
"""Transforms required by this class. Override in subclasses.""" """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): class Component(SettingsSpec, TransformSpec):
...@@ -129,11 +147,12 @@ class Component(SettingsSpec, TransformSpec): ...@@ -129,11 +147,12 @@ class Component(SettingsSpec, TransformSpec):
"""Base class for Docutils components.""" """Base class for Docutils components."""
component_type = None component_type = None
"""Override in subclasses.""" """Name of the component type ('reader', 'parser', 'writer'). Override in
subclasses."""
supported = () supported = ()
"""Names for this component. Override in subclasses.""" """Names for this component. Override in subclasses."""
def supports(self, format): def supports(self, format):
""" """
Is `format` supported by this component? Is `format` supported by this component?
......
# Authors: David Goodger # Authors: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -180,6 +180,7 @@ class Publisher: ...@@ -180,6 +180,7 @@ class Publisher:
self.settings) self.settings)
self.apply_transforms(document) self.apply_transforms(document)
output = self.writer.write(document, self.destination) output = self.writer.write(document, self.destination)
self.writer.assemble_parts()
except utils.SystemMessage, error: except utils.SystemMessage, error:
if self.settings.traceback: if self.settings.traceback:
raise raise
...@@ -376,3 +377,63 @@ def publish_string(source, source_path=None, destination_path=None, ...@@ -376,3 +377,63 @@ def publish_string(source, source_path=None, destination_path=None,
pub.set_source(source, source_path) pub.set_source(source, source_path)
pub.set_destination(destination_path=destination_path) pub.set_destination(destination_path=destination_path)
return pub.publish(enable_exit=enable_exit) 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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.7 $ # Revision: $Revision: 1.2.10.4.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -126,8 +126,7 @@ def validate_boolean(setting, value, option_parser, ...@@ -126,8 +126,7 @@ def validate_boolean(setting, value, option_parser,
def validate_threshold(setting, value, option_parser, def validate_threshold(setting, value, option_parser,
config_parser=None, config_section=None): config_parser=None, config_section=None):
try: try:
int(value) return int(value)
return value
except ValueError: except ValueError:
try: try:
return option_parser.thresholds[value.lower()] return option_parser.thresholds[value.lower()]
...@@ -294,6 +293,10 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec): ...@@ -294,6 +293,10 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
('Disable backlinks from footnotes and citations.', ('Disable backlinks from footnotes and citations.',
['--no-footnote-backlinks'], ['--no-footnote-backlinks'],
{'dest': 'footnote_backlinks', 'action': 'store_false'}), {'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 ' ('Set verbosity threshold; report system messages at or higher than '
'<level> (by name or number: "info" or "1", warning/2, error/3, ' '<level> (by name or number: "info" or "1", warning/2, error/3, '
'severe/4; also, "none" or "5"). Default is 2 (warning).', 'severe/4; also, "none" or "5"). Default is 2 (warning).',
...@@ -346,7 +349,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec): ...@@ -346,7 +349,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
{'metavar': '<name[:handler]>', 'default': 'utf-8', {'metavar': '<name[:handler]>', 'default': 'utf-8',
'validator': validate_encoding_and_error_handler}), 'validator': validate_encoding_and_error_handler}),
(SUPPRESS_HELP, # usually handled by --output-encoding (SUPPRESS_HELP, # usually handled by --output-encoding
['--output_encoding_error_handler'], ['--output-encoding-error-handler'],
{'default': 'strict', 'validator': validate_encoding_error_handler}), {'default': 'strict', 'validator': validate_encoding_error_handler}),
('Specify the text encoding for error output. Default is ASCII. ' ('Specify the text encoding for error output. Default is ASCII. '
'Optionally also specify the encoding error handler for unencodable ' 'Optionally also specify the encoding error handler for unencodable '
...@@ -357,7 +360,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec): ...@@ -357,7 +360,7 @@ class OptionParser(optparse.OptionParser, docutils.SettingsSpec):
{'metavar': '<name[:handler]>', 'default': 'ascii', {'metavar': '<name[:handler]>', 'default': 'ascii',
'validator': validate_encoding_and_error_handler}), 'validator': validate_encoding_and_error_handler}),
(SUPPRESS_HELP, # usually handled by --error-encoding (SUPPRESS_HELP, # usually handled by --error-encoding
['--error_encoding_error_handler'], ['--error-encoding-error-handler'],
{'default': default_error_encoding_error_handler, {'default': default_error_encoding_error_handler,
'validator': validate_encoding_error_handler}), 'validator': validate_encoding_error_handler}),
('Specify the language of input text (ISO 639 2-letter identifier).' ('Specify the language of input text (ISO 639 2-letter identifier).'
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -12,7 +12,10 @@ will exist for a variety of input/output mechanisms. ...@@ -12,7 +12,10 @@ will exist for a variety of input/output mechanisms.
__docformat__ = 'reStructuredText' __docformat__ = 'reStructuredText'
import sys import sys
import locale try:
import locale
except:
pass
from types import UnicodeType from types import UnicodeType
from docutils import TransformSpec from docutils import TransformSpec
...@@ -156,7 +159,7 @@ class FileInput(Input): ...@@ -156,7 +159,7 @@ class FileInput(Input):
print >>sys.stderr, '%s: %s' % (error.__class__.__name__, print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
error) error)
print >>sys.stderr, ( print >>sys.stderr, (
'Unable to open source file for reading (%s). Exiting.' 'Unable to open source file for reading (%r). Exiting.'
% source_path) % source_path)
sys.exit(1) sys.exit(1)
else: else:
...@@ -224,7 +227,7 @@ class FileOutput(Output): ...@@ -224,7 +227,7 @@ class FileOutput(Output):
print >>sys.stderr, '%s: %s' % (error.__class__.__name__, print >>sys.stderr, '%s: %s' % (error.__class__.__name__,
error) error)
print >>sys.stderr, ('Unable to open destination file for writing ' print >>sys.stderr, ('Unable to open destination file for writing '
'(%s). Exiting.' % source_path) '(%r). Exiting.' % self.destination_path)
sys.exit(1) sys.exit(1)
self.opened = 1 self.opened = 1
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# Internationalization details are documented in # Internationalization details are documented in
......
# Author: Jannie Hofmeyr # Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za # Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: Marek Blaha
# 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 # Authors: David Goodger; Gunnar Schwant
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: Marcelo Huerta San Martin # Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net # Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1 $ # Revision: $Revision: 1.1.2.1.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# -*- coding: iso-8859-1 -*- # -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn # Author: Marcelo Huerta San Martn
# Contact: mghsm@uol.com.ar # Contact: mghsm@uol.com.ar
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: Stefane Fermigier # Author: Stefane Fermigier
# Contact: sf@fermigier.com # Contact: sf@fermigier.com
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: Nicola Larosa # Author: Nicola Larosa
# Contact: docutils@tekNico.net # Contact: docutils@tekNico.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# -*- coding: iso-8859-1 -*-
# 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 # Author: Roman Suzi
# Contact: rnd@onego.ru # Contact: rnd@onego.ru
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# :Author: Miroslav Vasko # :Author: Miroslav Vasko
# :Contact: zemiak@zoznam.sk # :Contact: zemiak@zoznam.sk
# :Revision: $Revision: 1.5 $ # :Revision: $Revision: 1.2.10.3.8.1 $
# :Date: $Date: 2003/11/30 15:06:05 $ # :Date: $Date: 2004/05/12 19:57:42 $
# :Copyright: This module has been placed in the public domain. # :Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: Adam Chodorowski # Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net # Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:05 $ # Date: $Date: 2004/05/12 19:57:42 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:38 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -517,6 +517,9 @@ class TextElement(Element): ...@@ -517,6 +517,9 @@ class TextElement(Element):
its immediate parent is a `TextElement` instance (including subclasses). its immediate parent is a `TextElement` instance (including subclasses).
This is handy for nodes like `image` that can appear both inline and as This is handy for nodes like `image` that can appear both inline and as
standalone body elements. standalone body elements.
If passing children to `__init__()`, make sure to set `text` to
``''`` or some other suitable value.
""" """
child_text_separator = '' child_text_separator = ''
...@@ -599,6 +602,9 @@ class Targetable(Resolvable): ...@@ -599,6 +602,9 @@ class Targetable(Resolvable):
referenced = 0 referenced = 0
indirect_reference_name = None
"""Holds the whitespace_normalized_name (contains mixed case) of a target"""
class Labeled: class Labeled:
"""Contains a `label` as its first element.""" """Contains a `label` as its first element."""
...@@ -820,6 +826,7 @@ class document(Root, Structural, Element): ...@@ -820,6 +826,7 @@ class document(Root, Structural, Element):
def has_name(self, name): def has_name(self, name):
return self.nameids.has_key(name) return self.nameids.has_key(name)
# "note" here is an imperative verb: "take note of".
def note_implicit_target(self, target, msgnode=None): def note_implicit_target(self, target, msgnode=None):
id = self.set_id(target, msgnode) id = self.set_id(target, msgnode)
self.set_name_id_map(target, id, msgnode, explicit=None) self.set_name_id_map(target, id, msgnode, explicit=None)
...@@ -939,6 +946,7 @@ class rubric(Titular, TextElement): pass ...@@ -939,6 +946,7 @@ class rubric(Titular, TextElement): pass
# ======================== # ========================
class docinfo(Bibliographic, Element): pass class docinfo(Bibliographic, Element): pass
class info(Bibliographic, Element): pass
class author(Bibliographic, TextElement): pass class author(Bibliographic, TextElement): pass
class authors(Bibliographic, Element): pass class authors(Bibliographic, Element): pass
class organization(Bibliographic, TextElement): pass class organization(Bibliographic, TextElement): pass
...@@ -1182,7 +1190,7 @@ class raw(Special, Inline, PreBibliographic, FixedTextElement): ...@@ -1182,7 +1190,7 @@ class raw(Special, Inline, PreBibliographic, FixedTextElement):
class emphasis(Inline, TextElement): pass class emphasis(Inline, TextElement): pass
class strong(Inline, TextElement): pass class strong(Inline, TextElement): pass
class literal(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 footnote_reference(Inline, Referential, TextElement): pass
class citation_reference(Inline, Referential, TextElement): pass class citation_reference(Inline, Referential, TextElement): pass
class substitution_reference(Inline, TextElement): pass class substitution_reference(Inline, TextElement): pass
...@@ -1222,7 +1230,7 @@ node_class_names = """ ...@@ -1222,7 +1230,7 @@ node_class_names = """
footnote footnote_reference footnote footnote_reference
generated generated
header hint header hint
image important inline image important info inline
label legend line_block list_item literal literal_block label legend line_block list_item literal literal_block
note note
option option_argument option_group option_list option_list_item option option_argument option_group option_list option_list_item
...@@ -1273,8 +1281,8 @@ class NodeVisitor: ...@@ -1273,8 +1281,8 @@ class NodeVisitor:
Raise an exception unless overridden. Raise an exception unless overridden.
""" """
raise NotImplementedError('visiting unknown node type: %s' raise NotImplementedError('%s visiting unknown node type: %s'
% node.__class__.__name__) % (self.__class__, node.__class__.__name__))
def unknown_departure(self, node): def unknown_departure(self, node):
""" """
...@@ -1282,8 +1290,8 @@ class NodeVisitor: ...@@ -1282,8 +1290,8 @@ class NodeVisitor:
Raise exception unless overridden. Raise exception unless overridden.
""" """
raise NotImplementedError('departing unknown node type: %s' raise NotImplementedError('%s departing unknown node type: %s'
% node.__class__.__name__) % (self.__class__, node.__class__.__name__))
class SparseNodeVisitor(NodeVisitor): class SparseNodeVisitor(NodeVisitor):
...@@ -1295,16 +1303,6 @@ class SparseNodeVisitor(NodeVisitor): ...@@ -1295,16 +1303,6 @@ class SparseNodeVisitor(NodeVisitor):
subclasses), subclass `NodeVisitor` instead. 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): class GenericNodeVisitor(NodeVisitor):
""" """
...@@ -1337,12 +1335,18 @@ def _call_default_visit(self, node): ...@@ -1337,12 +1335,18 @@ def _call_default_visit(self, node):
def _call_default_departure(self, node): def _call_default_departure(self, node):
self.default_departure(node) self.default_departure(node)
# Save typing with dynamic assignments: def _nop(self, node):
for _name in node_class_names: pass
setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure) def _add_node_class_names(names):
del _name, _call_default_visit, _call_default_departure """Save typing with dynamic assignments:"""
for _name in names:
setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit)
setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure)
setattr(SparseNodeVisitor, 'visit_' + _name, _nop)
setattr(SparseNodeVisitor, 'depart' + _name, _nop)
_add_node_class_names(node_class_names)
class TreeCopyVisitor(GenericNodeVisitor): class TreeCopyVisitor(GenericNodeVisitor):
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:45 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:07:46 $ # Date: $Date: 2004/05/12 19:57:46 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.6 $ # Revision: $Revision: 1.2.10.4.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -102,6 +102,7 @@ _directive_registry = { ...@@ -102,6 +102,7 @@ _directive_registry = {
'epigraph': ('body', 'epigraph'), 'epigraph': ('body', 'epigraph'),
'highlights': ('body', 'highlights'), 'highlights': ('body', 'highlights'),
'pull-quote': ('body', 'pull_quote'), 'pull-quote': ('body', 'pull_quote'),
'table': ('body', 'table'),
#'questions': ('body', 'question_list'), #'questions': ('body', 'question_list'),
'image': ('images', 'image'), 'image': ('images', 'image'),
'figure': ('images', 'figure'), 'figure': ('images', 'figure'),
...@@ -117,6 +118,7 @@ _directive_registry = { ...@@ -117,6 +118,7 @@ _directive_registry = {
'replace': ('misc', 'replace'), 'replace': ('misc', 'replace'),
'unicode': ('misc', 'unicode_directive'), 'unicode': ('misc', 'unicode_directive'),
'class': ('misc', 'class_directive'), 'class': ('misc', 'class_directive'),
'role': ('misc', 'role'),
'restructuredtext-test-directive': ('misc', 'directive_test_function'),} 'restructuredtext-test-directive': ('misc', 'directive_test_function'),}
"""Mapping of directive name to (module name, function name). The directive """Mapping of directive name to (module name, function name). The directive
name is canonical & must be lowercase. Language-dependent names are defined name is canonical & must be lowercase. Language-dependent names are defined
...@@ -165,23 +167,37 @@ def directive(directive_name, language_module, document): ...@@ -165,23 +167,37 @@ def directive(directive_name, language_module, document):
try: try:
modulename, functionname = _directive_registry[canonicalname] modulename, functionname = _directive_registry[canonicalname]
except KeyError: except KeyError:
messages.append(document.reporter.error(
'Directive "%s" not registered (canonical name "%s").'
% (directive_name, canonicalname), line=document.current_line))
return None, messages return None, messages
if _modules.has_key(modulename): if _modules.has_key(modulename):
module = _modules[modulename] module = _modules[modulename]
else: else:
try: try:
module = __import__(modulename, globals(), locals()) 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 return None, messages
try: try:
function = getattr(module, functionname) function = getattr(module, functionname)
_directives[normname] = function _directives[normname] = function
except AttributeError: 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 None, messages
return function, messages return function, messages
def register_directive(name, directive): 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 _directives[name] = directive
def flag(argument): def flag(argument):
...@@ -257,7 +273,10 @@ def class_option(argument): ...@@ -257,7 +273,10 @@ def class_option(argument):
""" """
if argument is None: if argument is None:
raise ValueError('argument required but none supplied') 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): def format_values(values):
return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]), return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]),
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -34,6 +34,7 @@ def topic(name, arguments, options, content, lineno, ...@@ -34,6 +34,7 @@ def topic(name, arguments, options, content, lineno,
title_text = arguments[0] title_text = arguments[0]
textnodes, messages = state.inline_text(title_text, lineno) textnodes, messages = state.inline_text(title_text, lineno)
titles = [nodes.title(title_text, '', *textnodes)] titles = [nodes.title(title_text, '', *textnodes)]
# sidebar uses this code
if options.has_key('subtitle'): if options.has_key('subtitle'):
textnodes, more_messages = state.inline_text(options['subtitle'], textnodes, more_messages = state.inline_text(options['subtitle'],
lineno) lineno)
...@@ -120,3 +121,38 @@ def pull_quote(name, arguments, options, content, lineno, ...@@ -120,3 +121,38 @@ def pull_quote(name, arguments, options, content, lineno,
return [block_quote] + messages return [block_quote] + messages
pull_quote.content = 1 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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -13,7 +13,8 @@ __docformat__ = 'reStructuredText' ...@@ -13,7 +13,8 @@ __docformat__ = 'reStructuredText'
import sys import sys
from docutils import nodes, utils 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: try:
import Image # PIL import Image # PIL
...@@ -34,8 +35,23 @@ def image(name, arguments, options, content, lineno, ...@@ -34,8 +35,23 @@ def image(name, arguments, options, content, lineno,
nodes.literal_block(block_text, block_text), line=lineno) nodes.literal_block(block_text, block_text), line=lineno)
return [error] return [error]
options['uri'] = reference options['uri'] = reference
image_node = nodes.image(block_text, **options) if options.has_key('target'):
return [image_node] 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.arguments = (1, 0, 1)
image.options = {'alt': directives.unchanged, image.options = {'alt': directives.unchanged,
...@@ -43,6 +59,7 @@ image.options = {'alt': directives.unchanged, ...@@ -43,6 +59,7 @@ image.options = {'alt': directives.unchanged,
'width': directives.nonnegative_int, 'width': directives.nonnegative_int,
'scale': directives.nonnegative_int, 'scale': directives.nonnegative_int,
'align': align, 'align': align,
'target': directives.unchanged_required,
'class': directives.class_option} 'class': directives.class_option}
def figure(name, arguments, options, content, lineno, def figure(name, arguments, options, content, lineno,
......
# Authors: David Goodger, Dethe Elza # Authors: David Goodger, Dethe Elza
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
"""Miscellaneous directives.""" """Miscellaneous directives."""
...@@ -11,11 +11,15 @@ __docformat__ = 'reStructuredText' ...@@ -11,11 +11,15 @@ __docformat__ = 'reStructuredText'
import sys import sys
import os.path import os.path
import re import re
from urllib2 import urlopen, URLError
from docutils import io, nodes, statemachine, utils 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 from docutils.transforms import misc
try:
import urllib2
except ImportError:
urllib2 = None
def include(name, arguments, options, content, lineno, def include(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
...@@ -97,9 +101,16 @@ def raw(name, arguments, options, content, lineno, ...@@ -97,9 +101,16 @@ def raw(name, arguments, options, content, lineno,
raw_file.close() raw_file.close()
attributes['source'] = path attributes['source'] = path
elif options.has_key('url'): 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: try:
raw_file = urlopen(options['url']) raw_file = urllib2.urlopen(options['url'])
except (URLError, IOError, OSError), error: except (urllib2.URLError, IOError, OSError), error:
severe = state_machine.reporter.severe( severe = state_machine.reporter.severe(
'Problems with "%s" directive URL "%s":\n%s.' 'Problems with "%s" directive URL "%s":\n%s.'
% (name, options['url'], error), % (name, options['url'], error),
...@@ -209,7 +220,7 @@ def class_directive(name, arguments, options, content, lineno, ...@@ -209,7 +220,7 @@ def class_directive(name, arguments, options, content, lineno,
return [pending] return [pending]
else: else:
error = state_machine.reporter.error( error = state_machine.reporter.error(
'Invalid class attribute value for "%s" directive: %s' 'Invalid class attribute value for "%s" directive: "%s".'
% (name, arguments[0]), % (name, arguments[0]),
nodes.literal_block(block_text, block_text), line=lineno) nodes.literal_block(block_text, block_text), line=lineno)
return [error] return [error]
...@@ -217,8 +228,67 @@ def class_directive(name, arguments, options, content, lineno, ...@@ -217,8 +228,67 @@ def class_directive(name, arguments, options, content, lineno,
class_directive.arguments = (1, 0, 0) class_directive.arguments = (1, 0, 0)
class_directive.content = 1 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, def directive_test_function(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
"""This directive is useful only for testing purposes."""
if content: if content:
text = '\n'.join(content) text = '\n'.join(content)
info = state_machine.reporter.info( info = state_machine.reporter.info(
......
# Author: David Goodger, Dmitry Jemerov # Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -10,7 +10,7 @@ Directives for document parts. ...@@ -10,7 +10,7 @@ Directives for document parts.
__docformat__ = 'reStructuredText' __docformat__ = 'reStructuredText'
from docutils import nodes from docutils import nodes, languages
from docutils.transforms import parts from docutils.transforms import parts
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
...@@ -27,17 +27,42 @@ def backlinks(arg): ...@@ -27,17 +27,42 @@ def backlinks(arg):
def contents(name, arguments, options, content, lineno, def contents(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
"""Table of contents.""" """Table of contents."""
document = state_machine.document
language = languages.get_language(document.settings.language_code)
if arguments: if arguments:
title_text = arguments[0] title_text = arguments[0]
text_nodes, messages = state.inline_text(title_text, lineno) text_nodes, messages = state.inline_text(title_text, lineno)
title = nodes.title(title_text, '', *text_nodes) title = nodes.title(title_text, '', *text_nodes)
else: else:
messages = [] messages = []
title = None if options.has_key('local'):
pending = nodes.pending(parts.Contents, {'title': title}, block_text) title = None
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) pending.details.update(options)
state_machine.document.note_pending(pending) document.note_pending(pending)
return [pending] + messages topic += pending
return [topic] + messages
contents.arguments = (0, 1, 1) contents.arguments = (0, 1, 1)
contents.options = {'depth': directives.nonnegative_int, contents.options = {'depth': directives.nonnegative_int,
......
# Author: David Goodger, Dmitry Jemerov # Author: David Goodger, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:06 $ # Date: $Date: 2004/05/12 19:57:48 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# Internationalization details are documented in # Internationalization details are documented in
......
# Author: Jannie Hofmeyr # Author: Jannie Hofmeyr
# Contact: jhsh@sun.ac.za # Contact: jhsh@sun.ac.za
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -36,6 +36,7 @@ directives = { ...@@ -36,6 +36,7 @@ directives = {
'epigraaf': 'epigraph', 'epigraaf': 'epigraph',
'hoogtepunte': 'highlights', 'hoogtepunte': 'highlights',
'pull-quote (translation required)': 'pull-quote', 'pull-quote (translation required)': 'pull-quote',
'table (translation required)': 'table',
#'vrae': 'questions', #'vrae': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -48,6 +49,7 @@ directives = { ...@@ -48,6 +49,7 @@ directives = {
'vervang': 'replace', 'vervang': 'replace',
'unicode': 'unicode', # should this be translated? unikode 'unicode': 'unicode', # should this be translated? unikode
'klas': 'class', 'klas': 'class',
'role (translation required)': 'role',
'inhoud': 'contents', 'inhoud': 'contents',
'sectnum': 'sectnum', 'sectnum': 'sectnum',
'section-numbering': '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 -*- # -*- coding: iso-8859-1 -*-
# Author: Engelbert Gruber # Author: Engelbert Gruber
# Contact: grubert@users.sourceforge.net # Contact: grubert@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -37,6 +37,7 @@ directives = { ...@@ -37,6 +37,7 @@ directives = {
'epigraph (translation required)': 'epigraph', 'epigraph (translation required)': 'epigraph',
'highlights (translation required)': 'highlights', 'highlights (translation required)': 'highlights',
'pull-quote (translation required)': 'pull-quote', # kasten too ? 'pull-quote (translation required)': 'pull-quote', # kasten too ?
'table (translation required)': 'table',
#'questions': 'questions', #'questions': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -49,7 +50,8 @@ directives = { ...@@ -49,7 +50,8 @@ directives = {
# einfügung would be the noun. # einfügung would be the noun.
'ersetzung': 'replace', # ersetzen, ersetze 'ersetzung': 'replace', # ersetzen, ersetze
'unicode': 'unicode', 'unicode': 'unicode',
'klasse': 'class', # offer class too ? 'klasse': 'class', # offer "class" too ?
'role (translation required)': 'role',
'inhalt': 'contents', 'inhalt': 'contents',
'sectnum': 'sectnum', 'sectnum': 'sectnum',
'section-numbering': 'sectnum', 'section-numbering': 'sectnum',
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -37,6 +37,7 @@ directives = { ...@@ -37,6 +37,7 @@ directives = {
'epigraph': 'epigraph', 'epigraph': 'epigraph',
'highlights': 'highlights', 'highlights': 'highlights',
'pull-quote': 'pull-quote', 'pull-quote': 'pull-quote',
'table': 'table',
#'questions': 'questions', #'questions': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -49,6 +50,7 @@ directives = { ...@@ -49,6 +50,7 @@ directives = {
'replace': 'replace', 'replace': 'replace',
'unicode': 'unicode', 'unicode': 'unicode',
'class': 'class', 'class': 'class',
'role': 'role',
'contents': 'contents', 'contents': 'contents',
'sectnum': 'sectnum', 'sectnum': 'sectnum',
'section-numbering': 'sectnum', 'section-numbering': 'sectnum',
......
# Author: Marcelo Huerta San Martin # Author: Marcelo Huerta San Martin
# Contact: richieadler@users.sourceforge.net # Contact: richieadler@users.sourceforge.net
# Revision: $Revision: 1.1 $ # Revision: $Revision: 1.1.2.1.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:50 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -40,6 +40,7 @@ directives = { ...@@ -40,6 +40,7 @@ directives = {
u'elstara\u0135oj': 'highlights', u'elstara\u0135oj': 'highlights',
u'ekstera-citajxo': 'pull-quote', u'ekstera-citajxo': 'pull-quote',
u'ekstera-cita\u0135o': 'pull-quote', u'ekstera-cita\u0135o': 'pull-quote',
u'tabelo': 'table',
#'questions': 'questions', #'questions': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -53,6 +54,7 @@ directives = { ...@@ -53,6 +54,7 @@ directives = {
u'anstata\u016di': 'replace', u'anstata\u016di': 'replace',
u'unicode': 'unicode', u'unicode': 'unicode',
u'klaso': 'class', u'klaso': 'class',
u'rolo': 'role',
u'enhavo': 'contents', u'enhavo': 'contents',
u'seknum': 'sectnum', u'seknum': 'sectnum',
u'sekcia-numerado': 'sectnum', u'sekcia-numerado': 'sectnum',
......
# -*- coding: iso-8859-1 -*- # -*- coding: iso-8859-1 -*-
# Author: Marcelo Huerta San Martn # Author: Marcelo Huerta San Martn
# Contact: mghsm@uol.com.ar # Contact: mghsm@uol.com.ar
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -42,6 +42,7 @@ directives = { ...@@ -42,6 +42,7 @@ directives = {
u'epigrafe': 'epigraph', u'epigrafe': 'epigraph',
u'destacado': 'highlights', u'destacado': 'highlights',
u'cita-destacada': 'pull-quote', u'cita-destacada': 'pull-quote',
u'tabla': 'table',
#'questions': 'questions', #'questions': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -54,6 +55,7 @@ directives = { ...@@ -54,6 +55,7 @@ directives = {
u'reemplazar': 'replace', u'reemplazar': 'replace',
u'unicode': 'unicode', u'unicode': 'unicode',
u'clase': 'class', u'clase': 'class',
u'rol': 'role',
u'contenido': 'contents', u'contenido': 'contents',
u'numseccion': 'sectnum', u'numseccion': 'sectnum',
u'numsecci\u00f3n': 'sectnum', u'numsecci\u00f3n': 'sectnum',
...@@ -74,8 +76,10 @@ roles = { ...@@ -74,8 +76,10 @@ roles = {
u'ac': 'acronym', u'ac': 'acronym',
u'indice': 'index', u'indice': 'index',
u'i': 'index', u'i': 'index',
u'subscript (translation required)': 'subscript', u'subindice': 'subscript',
u'superscript (translation required)': 'superscript', u'sub\u00edndice': 'subscript',
u'superindice': 'superscript',
u'super\u00edndice': 'superscript',
u'referencia-titulo': 'title-reference', u'referencia-titulo': 'title-reference',
u'titulo': 'title-reference', u'titulo': 'title-reference',
u't': 'title-reference', u't': 'title-reference',
......
# Authors: David Goodger; William Dode # Authors: David Goodger; William Dode
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -38,6 +38,7 @@ directives = { ...@@ -38,6 +38,7 @@ directives = {
u'\u00E9pigraphe': 'epigraph', u'\u00E9pigraphe': 'epigraph',
u'chapeau': 'highlights', u'chapeau': 'highlights',
u'accroche': 'pull-quote', u'accroche': 'pull-quote',
u'tableau': 'table',
#u'questions': 'questions', #u'questions': 'questions',
#u'qr': 'questions', #u'qr': 'questions',
#u'faq': 'questions', #u'faq': 'questions',
...@@ -51,6 +52,7 @@ directives = { ...@@ -51,6 +52,7 @@ directives = {
u'remplace': 'replace', u'remplace': 'replace',
u'unicode': 'unicode', u'unicode': 'unicode',
u'classe': 'class', u'classe': 'class',
u'role (translation required)': 'role',
u'sommaire': 'contents', u'sommaire': 'contents',
u'table-des-mati\u00E8res': 'contents', u'table-des-mati\u00E8res': 'contents',
u'sectnum': 'sectnum', u'sectnum': 'sectnum',
......
# Author: Nicola Larosa # Author: Nicola Larosa, Lele Gaifax
# Contact: docutils@tekNico.net # Contact: docutils@tekNico.net, lele@seldati.it
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please
# 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 Italian-language mappings for language-dependent features of
reStructuredText. reStructuredText.
...@@ -27,15 +22,16 @@ directives = { ...@@ -27,15 +22,16 @@ directives = {
'nota': 'note', 'nota': 'note',
'consiglio': 'tip', 'consiglio': 'tip',
'avvertenza': 'warning', 'avvertenza': 'warning',
'admonition (translation required)': 'admonition', 'ammonizione': 'admonition',
'sidebar (translation required)': 'sidebar', 'riquadro': 'sidebar',
'argomento': 'topic', 'argomento': 'topic',
'blocco di linee': 'line-block', 'blocco-di-righe': 'line-block',
'parsed-literal': 'parsed-literal', 'blocco-interpretato': 'parsed-literal',
'rubric (translation required)': 'rubric', 'rubrica': 'rubric',
'epigraph (translation required)': 'epigraph', 'epigrafe': 'epigraph',
'highlights (translation required)': 'highlights', 'evidenzia': 'highlights',
'pull-quote (translation required)': 'pull-quote', 'pull-quote (translation required)': 'pull-quote',
'tabella': 'table',
#'questions': 'questions', #'questions': 'questions',
#'qa': 'questions', #'qa': 'questions',
#'faq': 'questions', #'faq': 'questions',
...@@ -47,11 +43,12 @@ directives = { ...@@ -47,11 +43,12 @@ directives = {
'grezzo': 'raw', 'grezzo': 'raw',
'sostituisci': 'replace', 'sostituisci': 'replace',
'unicode': 'unicode', 'unicode': 'unicode',
'class (translation required)': 'class', 'classe': 'class',
'ruolo': 'role',
'indice': 'contents', 'indice': 'contents',
'seznum': 'sectnum', 'seznum': 'sectnum',
'section-numbering': 'sectnum', 'sezioni-autonumerate': 'sectnum',
'target-notes': 'target-notes', 'annota-riferimenti-esterni': 'target-notes',
#'footnotes': 'footnotes', #'footnotes': 'footnotes',
#'citations': 'citations', #'citations': 'citations',
'restructuredtext-test-directive': 'restructuredtext-test-directive'} 'restructuredtext-test-directive': 'restructuredtext-test-directive'}
...@@ -59,23 +56,23 @@ directives = { ...@@ -59,23 +56,23 @@ directives = {
mapping.""" mapping."""
roles = { roles = {
'abbreviation (translation required)': 'abbreviation', 'abbreviazione': 'abbreviation',
'acronym (translation required)': 'acronym', 'acronimo': 'acronym',
'index (translation required)': 'index', 'indice': 'index',
'subscript (translation required)': 'subscript', 'deponente': 'subscript',
'superscript (translation required)': 'superscript', 'esponente': 'superscript',
'title-reference (translation required)': 'title-reference', 'riferimento-titolo': 'title-reference',
'pep-reference (translation required)': 'pep-reference', 'riferimento-pep': 'pep-reference',
'rfc-reference (translation required)': 'rfc-reference', 'riferimento-rfc': 'rfc-reference',
'emphasis (translation required)': 'emphasis', 'enfasi': 'emphasis',
'strong (translation required)': 'strong', 'forte': 'strong',
'literal (translation required)': 'literal', 'letterale': 'literal',
'named-reference (translation required)': 'named-reference', 'riferimento-con-nome': 'named-reference',
'anonymous-reference (translation required)': 'anonymous-reference', 'riferimento-anonimo': 'anonymous-reference',
'footnote-reference (translation required)': 'footnote-reference', 'riferimento-nota': 'footnote-reference',
'citation-reference (translation required)': 'citation-reference', 'riferimento-citazione': 'citation-reference',
'substitution-reference (translation required)': 'substitution-reference', 'riferimento-sostituzione': 'substitution-reference',
'target (translation required)': 'target', 'destinazione': 'target',
'uri-reference (translation required)': 'uri-reference',} 'riferimento-uri': 'uri-reference',}
"""Mapping of Italian role names to canonical role names for interpreted text. """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 # Author: Roman Suzi
# Contact: rnd@onego.ru # Contact: rnd@onego.ru
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.1.2.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -23,6 +23,7 @@ directives = { ...@@ -23,6 +23,7 @@ directives = {
u'parsed-literal', u'parsed-literal',
u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u0430\u044f-\u0446\u0438\u0442\u0430\u0442\u0430': u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u0430\u044f-\u0446\u0438\u0442\u0430\u0442\u0430':
u'pull-quote', u'pull-quote',
u'table (translation required)': 'table',
u'\u0441\u044b\u0440\u043e\u0439': u'raw', u'\u0441\u044b\u0440\u043e\u0439': u'raw',
u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace', u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace',
u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext': u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext':
...@@ -40,6 +41,7 @@ directives = { ...@@ -40,6 +41,7 @@ directives = {
u'\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435': u'\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435':
u'image', u'image',
u'\u043a\u043b\u0430\u0441\u0441': u'class', 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'\u043d\u043e\u043c\u0435\u0440-\u0440\u0430\u0437\u0434\u0435\u043b\u0430':
u'sectnum', u'sectnum',
u'\u043d\u0443\u043c\u0435\u0440\u0430\u0446\u0438\u044f-\u0440\u0430\u0437' u'\u043d\u0443\u043c\u0435\u0440\u0430\u0446\u0438\u044f-\u0440\u0430\u0437'
......
# Author: Miroslav Vasko # Author: Miroslav Vasko
# Contact: zemiak@zoznam.sk # Contact: zemiak@zoznam.sk
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -36,6 +36,7 @@ directives = { ...@@ -36,6 +36,7 @@ directives = {
u'epigraph (translation required)': 'epigraph', u'epigraph (translation required)': 'epigraph',
u'highlights (translation required)': 'highlights', u'highlights (translation required)': 'highlights',
u'pull-quote (translation required)': 'pull-quote', u'pull-quote (translation required)': 'pull-quote',
u'table (translation required)': 'table',
#u'questions': 'questions', #u'questions': 'questions',
#u'qa': 'questions', #u'qa': 'questions',
#u'faq': 'questions', #u'faq': 'questions',
...@@ -48,6 +49,7 @@ directives = { ...@@ -48,6 +49,7 @@ directives = {
u'nahradi\x9d': 'replace', u'nahradi\x9d': 'replace',
u'unicode': 'unicode', u'unicode': 'unicode',
u'class (translation required)': 'class', u'class (translation required)': 'class',
u'role (translation required)': 'role',
u'obsah': 'contents', u'obsah': 'contents',
u'\xe8as\x9d': 'sectnum', u'\xe8as\x9d': 'sectnum',
u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum', u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum',
......
# Author: Adam Chodorowski # Author: Adam Chodorowski
# Contact: chodorowski@users.sourceforge.net # Contact: chodorowski@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:07 $ # Date: $Date: 2004/05/12 19:57:51 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
# New language mappings are welcome. Before doing a new translation, please # New language mappings are welcome. Before doing a new translation, please
...@@ -35,6 +35,7 @@ directives = { ...@@ -35,6 +35,7 @@ directives = {
u'epigraph (translation required)': 'epigraph', u'epigraph (translation required)': 'epigraph',
u'highlights (translation required)': 'highlights', u'highlights (translation required)': 'highlights',
u'pull-quote (translation required)': 'pull-quote', u'pull-quote (translation required)': 'pull-quote',
u'table (translation required)': 'table',
# u'fr\u00e5gor': 'questions', # u'fr\u00e5gor': 'questions',
# NOTE: A bit long, but recommended by http://www.nada.kth.se/dataterm/: # NOTE: A bit long, but recommended by http://www.nada.kth.se/dataterm/:
# u'fr\u00e5gor-och-svar': 'questions', # u'fr\u00e5gor-och-svar': 'questions',
...@@ -48,6 +49,7 @@ directives = { ...@@ -48,6 +49,7 @@ directives = {
u'ers\u00e4tt': 'replace', u'ers\u00e4tt': 'replace',
u'unicode': 'unicode', u'unicode': 'unicode',
u'class (translation required)': 'class', u'class (translation required)': 'class',
u'role (translation required)': 'role',
u'inneh\u00e5ll': 'contents', u'inneh\u00e5ll': 'contents',
u'sektionsnumrering': 'sectnum', u'sektionsnumrering': 'sectnum',
u'target-notes (translation required)': 'target-notes', u'target-notes (translation required)': 'target-notes',
......
# 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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:07:46 $ # Date: $Date: 2004/05/12 19:57:46 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -107,13 +107,14 @@ __docformat__ = 'reStructuredText' ...@@ -107,13 +107,14 @@ __docformat__ = 'reStructuredText'
import sys import sys
import re import re
from docutils import roman import roman
from types import TupleType from types import TupleType
from docutils import nodes, statemachine, utils, urischemes from docutils import nodes, statemachine, utils, urischemes
from docutils import ApplicationError, DataError from docutils import ApplicationError, DataError
from docutils.statemachine import StateMachineWS, StateWS from docutils.statemachine import StateMachineWS, StateWS
from docutils.nodes import fully_normalize_name as normalize_name 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 from docutils.parsers.rst.languages import en as _fallback_language_module
...@@ -274,7 +275,7 @@ class RSTState(StateWS): ...@@ -274,7 +275,7 @@ class RSTState(StateWS):
state_machine.unlink() state_machine.unlink()
new_offset = state_machine.abs_line_offset() new_offset = state_machine.abs_line_offset()
# No `block.parent` implies disconnected -- lines aren't in sync: # 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: # Adjustment for block if modified in nested parse:
self.state_machine.next_line(len(block) - block_length) self.state_machine.next_line(len(block) - block_length)
return new_offset return new_offset
...@@ -475,46 +476,6 @@ class Inliner: ...@@ -475,46 +476,6 @@ class Inliner:
Parse inline markup; call the `parse()` method. 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): def __init__(self, roles=None):
""" """
`roles` is a mapping of canonical role name to role function or bound `roles` is a mapping of canonical role name to role function or bound
...@@ -525,17 +486,6 @@ class Inliner: ...@@ -525,17 +486,6 @@ class Inliner:
"""List of (pattern, bound method) tuples, used by """List of (pattern, bound method) tuples, used by
`self.implicit_inline`.""" `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): def init_customizations(self, settings):
"""Setting-based customizations; run when parsing begins.""" """Setting-based customizations; run when parsing begins."""
if settings.pep_references: if settings.pep_references:
...@@ -546,6 +496,8 @@ class Inliner: ...@@ -546,6 +496,8 @@ class Inliner:
self.rfc_reference)) self.rfc_reference))
def parse(self, text, lineno, memo, parent): 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. Return 2 lists: nodes (text and inline elements), and system_messages.
...@@ -601,11 +553,12 @@ class Inliner: ...@@ -601,11 +553,12 @@ class Inliner:
non_whitespace_after = r'(?![ \n])' non_whitespace_after = r'(?![ \n])'
# Alphanumerics with isolated internal [-._] chars (i.e. not 2 together): # Alphanumerics with isolated internal [-._] chars (i.e. not 2 together):
simplename = r'(?:(?!_)\w)+(?:[-._](?:(?!_)\w)+)*' simplename = r'(?:(?!_)\w)+(?:[-._](?:(?!_)\w)+)*'
# Valid URI characters (see RFC 2396 & RFC 2732): # Valid URI characters (see RFC 2396 & RFC 2732);
uric = r"""[-_.!~*'()[\];/:@&=+$,%a-zA-Z0-9]""" # final \x00 allows backslash escapes in URIs:
uric = r"""[-_.!~*'()[\];/:@&=+$,%a-zA-Z0-9\x00]"""
# Last URI character; same as uric but no punctuation: # Last URI character; same as uric but no punctuation:
urilast = r"""[_~/a-zA-Z0-9]""" urilast = r"""[_~/a-zA-Z0-9]"""
emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9]""" emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9\x00]"""
email_pattern = r""" email_pattern = r"""
%(emailc)s+(?:\.%(emailc)s+)* # name %(emailc)s+(?:\.%(emailc)s+)* # name
@ # at @ # at
...@@ -659,10 +612,10 @@ class Inliner: ...@@ -659,10 +612,10 @@ class Inliner:
embedded_uri=re.compile( embedded_uri=re.compile(
r""" r"""
( (
[ \n]+ # spaces or beginning of line (?:[ \n]+|^) # spaces or beginning of line/string
< # open bracket < # open bracket
%(non_whitespace_after)s %(non_whitespace_after)s
([^<>\0]+) # anything but angle brackets & nulls ([^<>\x00]+) # anything but angle brackets & nulls
%(non_whitespace_before)s %(non_whitespace_before)s
> # close bracket w/o whitespace before > # close bracket w/o whitespace before
) )
...@@ -823,26 +776,11 @@ class Inliner: ...@@ -823,26 +776,11 @@ class Inliner:
return self.phrase_ref(string[:matchstart], string[textend:], return self.phrase_ref(string[:matchstart], string[textend:],
rawsource, escaped, text) rawsource, escaped, text)
else: else:
try: rawsource = unescape(string[rolestart:textend], 1)
return self.interpreted( nodelist, messages = self.interpreted(rawsource, text, role,
string[:rolestart], string[textend:], lineno)
rawsource, text, role, lineno) return (string[:rolestart], nodelist,
except UnknownInterpretedRoleError, detail: string[textend:], messages)
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])
msg = self.reporter.warning( msg = self.reporter.warning(
'Inline interpreted text or phrase reference start-string ' 'Inline interpreted text or phrase reference start-string '
'without end-string.', line=lineno) 'without end-string.', line=lineno)
...@@ -861,10 +799,13 @@ class Inliner: ...@@ -861,10 +799,13 @@ class Inliner:
target = nodes.target(match.group(1), refuri=uri) target = nodes.target(match.group(1), refuri=uri)
else: else:
raise ApplicationError('problem with URI: %r' % uri_text) raise ApplicationError('problem with URI: %r' % uri_text)
if not text:
text = uri
else: else:
target = None target = None
refname = normalize_name(text) refname = normalize_name(text)
reference = nodes.reference(rawsource, text) reference = nodes.reference(rawsource, text,
name=whitespace_normalize_name(text))
node_list = [reference] node_list = [reference]
if rawsource[-2:] == '__': if rawsource[-2:] == '__':
if target: if target:
...@@ -891,50 +832,18 @@ class Inliner: ...@@ -891,50 +832,18 @@ class Inliner:
else: else:
return uri return uri
def interpreted(self, before, after, rawsource, text, role, lineno): def interpreted(self, rawsource, text, role, lineno):
role_function, canonical, messages = self.get_role_function(role, role_fn, messages = roles.role(role, self.language, lineno,
lineno) self.reporter)
if role_function: if role_fn:
nodelist, messages2 = role_function(canonical, rawsource, text, nodes, messages2 = role_fn(role, rawsource, text, lineno, self)
lineno) return nodes, messages + messages2
messages.extend(messages2)
return before, nodelist, after, messages
else:
raise InterpretedRoleNotImplementedError(messages)
def get_role_function(self, role, lineno):
messages = []
msg_text = []
if role:
name = role.lower()
else: else:
name = self.default_interpreted_role msg = self.reporter.error(
canonical = None 'Unknown interpreted text role "%s".' % role,
try: line=lineno)
canonical = self.language.roles[name] return ([self.problematic(rawsource, rawsource, msg)],
except AttributeError, error: messages + [msg])
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)
def literal(self, match, lineno): def literal(self, match, lineno):
before, inlines, remaining, sysmessages, endstring = self.inline_obj( before, inlines, remaining, sysmessages, endstring = self.inline_obj(
...@@ -1014,8 +923,9 @@ class Inliner: ...@@ -1014,8 +923,9 @@ class Inliner:
def reference(self, match, lineno, anonymous=None): def reference(self, match, lineno, anonymous=None):
referencename = match.group('refname') referencename = match.group('refname')
refname = normalize_name(referencename) refname = normalize_name(referencename)
referencenode = nodes.reference(referencename + match.group('refend'), referencenode = nodes.reference(
referencename) referencename + match.group('refend'), referencename,
name=whitespace_normalize_name(referencename))
if anonymous: if anonymous:
referencenode['anonymous'] = 1 referencenode['anonymous'] = 1
self.document.note_anonymous_ref(referencenode) self.document.note_anonymous_ref(referencenode)
...@@ -1104,44 +1014,6 @@ class Inliner: ...@@ -1104,44 +1014,6 @@ class Inliner:
'_': reference, '_': reference,
'__': anonymous_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): class Body(RSTState):
...@@ -1472,15 +1344,15 @@ class Body(RSTState): ...@@ -1472,15 +1344,15 @@ class Body(RSTState):
def field_marker(self, match, context, next_state): def field_marker(self, match, context, next_state):
"""Field list item.""" """Field list item."""
fieldlist = nodes.field_list() field_list = nodes.field_list()
self.parent += fieldlist self.parent += field_list
field, blank_finish = self.field(match) field, blank_finish = self.field(match)
fieldlist += field field_list += field
offset = self.state_machine.line_offset + 1 # next line offset = self.state_machine.line_offset + 1 # next line
newline_offset, blank_finish = self.nested_list_parse( newline_offset, blank_finish = self.nested_list_parse(
self.state_machine.input_lines[offset:], self.state_machine.input_lines[offset:],
input_offset=self.state_machine.abs_line_offset() + 1, input_offset=self.state_machine.abs_line_offset() + 1,
node=fieldlist, initial_state='FieldList', node=field_list, initial_state='FieldList',
blank_finish=blank_finish) blank_finish=blank_finish)
self.goto_line(newline_offset) self.goto_line(newline_offset)
if not blank_finish: if not blank_finish:
...@@ -1492,14 +1364,15 @@ class Body(RSTState): ...@@ -1492,14 +1364,15 @@ class Body(RSTState):
lineno = self.state_machine.abs_line_number() lineno = self.state_machine.abs_line_number()
indented, indent, line_offset, blank_finish = \ indented, indent, line_offset, blank_finish = \
self.state_machine.get_first_known_indented(match.end()) self.state_machine.get_first_known_indented(match.end())
fieldnode = nodes.field() field_node = nodes.field()
fieldnode.line = lineno field_node.line = lineno
fieldnode += nodes.field_name(name, name) name_nodes, name_messages = self.inline_text(name, lineno)
fieldbody = nodes.field_body('\n'.join(indented)) field_node += nodes.field_name(name, '', *name_nodes)
fieldnode += fieldbody field_body = nodes.field_body('\n'.join(indented), *name_messages)
field_node += field_body
if indented: if indented:
self.parse_field_body(indented, line_offset, fieldbody) self.parse_field_body(indented, line_offset, field_body)
return fieldnode, blank_finish return field_node, blank_finish
def parse_field_marker(self, match): def parse_field_marker(self, match):
"""Extract & return field name from a field marker match.""" """Extract & return field name from a field marker match."""
...@@ -1876,39 +1749,59 @@ class Body(RSTState): ...@@ -1876,39 +1749,59 @@ class Body(RSTState):
raise MarkupError('malformed hyperlink target.', lineno) raise MarkupError('malformed hyperlink target.', lineno)
del block[:blockindex] del block[:blockindex]
block[0] = (block[0] + ' ')[targetmatch.end()-len(escaped)-1:].strip() 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 if block and block[-1].strip()[-1:] == '_': # possible indirect target
reference = ' '.join([line.strip() for line in block]) reference = ' '.join([line.strip() for line in block])
refname = self.is_reference(reference) refname = self.is_reference(reference)
if refname: if refname:
target = nodes.target(blocktext, '', refname=refname) return 'refname', refname
target.line = lineno
self.add_target(targetmatch.group('name'), '', target)
self.document.note_indirect_target(target)
return [target], blank_finish
nodelist = []
reference = ''.join([line.strip() for line in block]) 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( warning = self.reporter.warning(
'Hyperlink target contains whitespace. Perhaps a footnote ' 'Hyperlink target contains whitespace. Perhaps a footnote '
'was intended?', 'was intended?',
nodes.literal_block(blocktext, blocktext), line=lineno) nodes.literal_block(block_text, block_text), line=lineno)
nodelist.append(warning) return 'malformed', 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
def is_reference(self, reference): def is_reference(self, reference):
match = self.explicit.patterns.reference.match( match = self.explicit.patterns.reference.match(
normalize_name(reference)) whitespace_normalize_name(reference))
if not match: if not match:
return None return None
return unescape(match.group('simple') or match.group('phrase')) 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: if targetname:
name = normalize_name(unescape(targetname)) name = normalize_name(unescape(targetname))
target['name'] = name target['name'] = name
...@@ -1988,17 +1881,18 @@ class Body(RSTState): ...@@ -1988,17 +1881,18 @@ class Body(RSTState):
return [msg], blank_finish return [msg], blank_finish
def directive(self, match, **option_presets): def directive(self, match, **option_presets):
"""Returns a 2-tuple: list of nodes, and a "blank finish" boolean."""
type_name = match.group(1) type_name = match.group(1)
directive_function, messages = directives.directive( directive_function, messages = directives.directive(
type_name, self.memo.language, self.document) type_name, self.memo.language, self.document)
self.parent += messages self.parent += messages
if directive_function: if directive_function:
return self.parse_directive( return self.run_directive(
directive_function, match, type_name, option_presets) directive_function, match, type_name, option_presets)
else: else:
return self.unknown_directive(type_name) 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. Parse a directive then run its directive function.
...@@ -2020,13 +1914,6 @@ class Body(RSTState): ...@@ -2020,13 +1914,6 @@ class Body(RSTState):
Returns a 2-tuple: list of nodes, and a "blank finish" boolean. 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() lineno = self.state_machine.abs_line_number()
initial_line_offset = self.state_machine.line_offset initial_line_offset = self.state_machine.line_offset
indented, indent, line_offset, blank_finish \ indented, indent, line_offset, blank_finish \
...@@ -2034,6 +1921,30 @@ class Body(RSTState): ...@@ -2034,6 +1921,30 @@ class Body(RSTState):
strip_top=0) strip_top=0)
block_text = '\n'.join(self.state_machine.input_lines[ block_text = '\n'.join(self.state_machine.input_lines[
initial_line_offset : self.state_machine.line_offset + 1]) 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(): if indented and not indented[0].strip():
indented.trim_start() indented.trim_start()
line_offset += 1 line_offset += 1
...@@ -2055,24 +1966,15 @@ class Body(RSTState): ...@@ -2055,24 +1966,15 @@ class Body(RSTState):
while content and not content[0].strip(): while content and not content[0].strip():
content.trim_start() content.trim_start()
content_offset += 1 content_offset += 1
try: if option_spec:
if option_spec: options, arg_block = self.parse_directive_options(
options, arg_block = self.parse_directive_options( option_presets, option_spec, arg_block)
option_presets, option_spec, arg_block) if argument_spec:
if argument_spec: arguments = self.parse_directive_arguments(
arguments = self.parse_directive_arguments(argument_spec, argument_spec, arg_block)
arg_block) if content and not content_spec:
if content and not content_spec: raise MarkupError('no content permitted')
raise MarkupError('no content permitted') return (arguments, options, content, content_offset)
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_options(self, option_presets, option_spec, arg_block): def parse_directive_options(self, option_presets, option_spec, arg_block):
options = option_presets.copy() options = option_presets.copy()
...@@ -2251,38 +2153,14 @@ class Body(RSTState): ...@@ -2251,38 +2153,14 @@ class Body(RSTState):
return [], next_state, [] return [], next_state, []
def anonymous_target(self, match): def anonymous_target(self, match):
lineno = self.state_machine.abs_line_number()
block, indent, offset, blank_finish \ block, indent, offset, blank_finish \
= self.state_machine.get_first_known_indented(match.end(), = self.state_machine.get_first_known_indented(match.end(),
until_blank=1) until_blank=1)
blocktext = match.string[:match.end()] + '\n'.join(block) blocktext = match.string[:match.end()] + '\n'.join(block)
if block and block[-1].strip()[-1:] == '_': # possible indirect target block = [escape2null(line) for line in block]
reference = escape2null(' '.join([line.strip() target = self.make_target(block, blocktext, lineno, '')
for line in block])) return [target], blank_finish
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)
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): def line(self, match, context, next_state):
"""Section title overline or transition marker.""" """Section title overline or transition marker."""
...@@ -2660,20 +2538,29 @@ class Text(RSTState): ...@@ -2660,20 +2538,29 @@ class Text(RSTState):
"""Return a list of nodes.""" """Return a list of nodes."""
indented, indent, offset, blank_finish = \ indented, indent, offset, blank_finish = \
self.state_machine.get_indented() self.state_machine.get_indented()
nodelist = []
while indented and not indented[-1].strip(): while indented and not indented[-1].strip():
indented.trim_end() indented.trim_end()
if indented: if not indented:
data = '\n'.join(indented) return self.quoted_literal_block()
nodelist.append(nodes.literal_block(data, data)) nodelist = []
if not blank_finish: data = '\n'.join(indented)
nodelist.append(self.unindent_warning('Literal block')) nodelist.append(nodes.literal_block(data, data))
else: if not blank_finish:
nodelist.append(self.reporter.warning( nodelist.append(self.unindent_warning('Literal block'))
'Literal block expected; none found.',
line=self.state_machine.abs_line_number()))
return nodelist 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): def definition_list_item(self, termline):
indented, indent, line_offset, blank_finish = \ indented, indent, line_offset, blank_finish = \
self.state_machine.get_indented() self.state_machine.get_indented()
...@@ -2687,8 +2574,8 @@ class Text(RSTState): ...@@ -2687,8 +2574,8 @@ class Text(RSTState):
definitionlistitem += definition definitionlistitem += definition
if termline[0][-2:] == '::': if termline[0][-2:] == '::':
definition += self.reporter.info( definition += self.reporter.info(
'Blank line missing before literal block? Interpreted as a ' 'Blank line missing before literal block (after the "::")? '
'definition list item.', line=line_offset + 1) 'Interpreted as a definition list item.', line=line_offset+1)
self.nested_parse(indented, input_offset=line_offset, node=definition) self.nested_parse(indented, input_offset=line_offset, node=definition)
return definitionlistitem, blank_finish return definitionlistitem, blank_finish
...@@ -2890,6 +2777,77 @@ class Line(SpecializedText): ...@@ -2890,6 +2777,77 @@ class Line(SpecializedText):
raise statemachine.StateCorrection('Body', 'text') 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, state_classes = (Body, BulletList, DefinitionList, EnumeratedList, FieldList,
OptionList, ExtensionOptions, Explicit, Text, Definition, OptionList, ExtensionOptions, Explicit, Text, Definition,
Line, SubstitutionDef, RFC2822Body, RFC2822List) Line, SubstitutionDef, RFC2822Body, RFC2822List)
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:07:46 $ # Date: $Date: 2004/05/12 19:57:47 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Authors: David Goodger; Ueli Schlaepfer # Authors: David Goodger; Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:08 $ # Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:08 $ # Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.3.2.1.8.1 $
# Date: $Date: 2003/11/30 15:06:08 $ # Date: $Date: 2004/05/12 19:57:53 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -13,9 +13,117 @@ __docformat__ = 'reStructuredText' ...@@ -13,9 +13,117 @@ __docformat__ = 'reStructuredText'
import sys import sys
import docutils.readers 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): class Reader(docutils.readers.Reader):
config_section = 'python reader' config_section = 'python reader'
config_section_dependencies = ('readers',) 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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.3 $ # Revision: $Revision: 1.3.2.1.8.1 $
# Date: $Date: 2003/11/30 15:06:08 $ # Date: $Date: 2004/05/12 19:57:53 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
Parser for Python modules. Parser for Python modules.
The `parse_module()` function takes a module's text and file name, runs it The `parse_module()` function takes a module's text and file name,
through the module parser (using compiler.py and tokenize.py) and produces a runs it through the module parser (using compiler.py and tokenize.py)
"module documentation tree": a high-level AST full of nodes that are and produces a parse tree of the source code, using the nodes as found
interesting from an auto-documentation standpoint. For example, given this in pynodes.py. For example, given this module (x.py)::
module (x.py)::
# comment # comment
...@@ -50,69 +49,95 @@ module (x.py):: ...@@ -50,69 +49,95 @@ module (x.py)::
The module parser will produce this module documentation tree:: The module parser will produce this module documentation tree::
<Module filename="test data"> <module_section filename="test data">
<Comment lineno=1> <docstring>
comment
<Docstring>
Docstring Docstring
<Docstring lineno="5"> <docstring lineno="5">
Additional docstring Additional docstring
<Attribute lineno="7" name="__docformat__"> <attribute lineno="7">
<Expression lineno="7"> <object_name>
__docformat__
<expression_value lineno="7">
'reStructuredText' 'reStructuredText'
<Attribute lineno="9" name="a"> <attribute lineno="9">
<Expression lineno="9"> <object_name>
a
<expression_value lineno="9">
1 1
<Docstring lineno="10"> <docstring lineno="10">
Attribute docstring Attribute docstring
<Class bases="Super" lineno="12" name="C"> <class_section lineno="12">
<Docstring lineno="12"> <object_name>
C
<class_base>
Super
<docstring lineno="12">
C's docstring C's docstring
<Attribute lineno="16" name="class_attribute"> <attribute lineno="16">
<Expression lineno="16"> <object_name>
class_attribute
<expression_value lineno="16">
1 1
<Docstring lineno="17"> <docstring lineno="17">
class_attribute's docstring class_attribute's docstring
<Method lineno="19" name="__init__"> <method_section lineno="19">
<Docstring lineno="19"> <object_name>
__init__
<docstring lineno="19">
__init__'s docstring __init__'s docstring
<ParameterList lineno="19"> <parameter_list lineno="19">
<Parameter lineno="19" name="self"> <parameter lineno="19">
<Parameter lineno="19" name="text"> <object_name>
<Default lineno="19"> self
<parameter lineno="19">
<object_name>
text
<parameter_default lineno="19">
None None
<Attribute lineno="22" name="self.instance_attribute"> <attribute lineno="22">
<Expression lineno="22"> <object_name>
self.instance_attribute
<expression_value lineno="22">
(text * 7 + ' whaddyaknow') (text * 7 + ' whaddyaknow')
<Docstring lineno="24"> <docstring lineno="24">
instance_attribute's docstring instance_attribute's docstring
<Function lineno="27" name="f"> <function_section lineno="27">
<Docstring lineno="27"> <object_name>
f
<docstring lineno="27">
f's docstring f's docstring
<ParameterList lineno="27"> <parameter_list lineno="27">
<Parameter lineno="27" name="x"> <parameter lineno="27">
<Comment> <object_name>
x
<comment>
# parameter x # parameter x
<Parameter lineno="27" name="y"> <parameter lineno="27">
<Default lineno="27"> <object_name>
y
<parameter_default lineno="27">
a * 5 a * 5
<Comment> <comment>
# parameter y # parameter y
<ExcessPositionalArguments lineno="27" name="args"> <parameter excess_positional="1" lineno="27">
<Comment> <object_name>
args
<comment>
# parameter args # parameter args
<Attribute lineno="33" name="f.function_attribute"> <attribute lineno="33">
<Expression lineno="33"> <object_name>
f.function_attribute
<expression_value lineno="33">
1 1
<Docstring lineno="34"> <docstring lineno="34">
f.function_attribute's docstring f.function_attribute's docstring
(Comments are not implemented yet.) (Comments are not implemented yet.)
compiler.parse() provides most of what's needed for this doctree, and 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 "tokenize" can be used to get the rest. We can determine the line
the compiler.parse() AST, and the TokenParser.rhs(lineno) method provides the number from the compiler.parse() AST, and the TokenParser.rhs(lineno)
rest. method provides the rest.
The Docutils Python reader component will transform this module doctree into a The Docutils Python reader component will transform this module doctree into a
Python-specific Docutils doctree, and then a `stylist transform`_ will Python-specific Docutils doctree, and then a `stylist transform`_ will
...@@ -190,6 +215,8 @@ import token ...@@ -190,6 +215,8 @@ import token
from compiler.consts import OP_ASSIGN from compiler.consts import OP_ASSIGN
from compiler.visitor import ASTVisitor from compiler.visitor import ASTVisitor
from types import StringType, UnicodeType, TupleType from types import StringType, UnicodeType, TupleType
from docutils.readers.python import pynodes
from docutils.nodes import Text
def parse_module(module_text, filename): def parse_module(module_text, filename):
...@@ -200,168 +227,6 @@ def parse_module(module_text, filename): ...@@ -200,168 +227,6 @@ def parse_module(module_text, filename):
compiler.walk(ast, visitor, walker=visitor) compiler.walk(ast, visitor, walker=visitor)
return visitor.module 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): class BaseVisitor(ASTVisitor):
def __init__(self, token_parser): def __init__(self, token_parser):
...@@ -389,7 +254,7 @@ class DocstringVisitor(BaseVisitor): ...@@ -389,7 +254,7 @@ class DocstringVisitor(BaseVisitor):
def visitConst(self, node): def visitConst(self, node):
if self.documentable: if self.documentable:
if type(node.value) in (StringType, UnicodeType): if type(node.value) in (StringType, UnicodeType):
self.documentable.append(Docstring(node, node.value)) self.documentable.append(make_docstring(node.value, node.lineno))
else: else:
self.documentable = None self.documentable = None
...@@ -418,25 +283,28 @@ class ModuleVisitor(AssignmentVisitor): ...@@ -418,25 +283,28 @@ class ModuleVisitor(AssignmentVisitor):
self.module = None self.module = None
def visitModule(self, node): def visitModule(self, node):
self.module = module = Module(node, self.filename) self.module = module = pynodes.module_section()
if node.doc is not None: module['filename'] = self.filename
module.append(Docstring(node, node.doc)) append_docstring(module, node.doc, node.lineno)
self.context.append(module) self.context.append(module)
self.documentable = module self.documentable = module
self.visit(node.node) self.visit(node.node)
self.context.pop() self.context.pop()
def visitImport(self, node): 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 self.documentable = None
def visitFrom(self, node): def visitFrom(self, node):
self.context[-1].append( 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 self.documentable = None
def visitFunction(self, node): 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) compiler.walk(node, visitor, walker=visitor)
self.context[-1].append(visitor.function) self.context[-1].append(visitor.function)
...@@ -450,29 +318,32 @@ class AttributeVisitor(BaseVisitor): ...@@ -450,29 +318,32 @@ class AttributeVisitor(BaseVisitor):
def __init__(self, token_parser): def __init__(self, token_parser):
BaseVisitor.__init__(self, token_parser) BaseVisitor.__init__(self, token_parser)
self.attributes = [] self.attributes = pynodes.class_attribute_section()
def visitAssign(self, node): def visitAssign(self, node):
# Don't visit the expression itself, just the attribute nodes: # Don't visit the expression itself, just the attribute nodes:
for child in node.nodes: for child in node.nodes:
self.dispatch(child) self.dispatch(child)
expression_text = self.token_parser.rhs(node.lineno) 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: for attribute in self.attributes:
attribute.append(expression) attribute.append(expression)
def visitAssName(self, node): 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): def visitAssTuple(self, node):
attributes = self.attributes attributes = self.attributes
self.attributes = [] self.attributes = []
self.default_visit(node) self.default_visit(node)
names = [attribute.name for attribute in self.attributes] n = pynodes.attribute_tuple()
att_tuple = AttributeTuple(node, names) n.extend(self.attributes)
att_tuple.lineno = self.attributes[0].lineno n['lineno'] = self.attributes[0]['lineno']
attributes.append(n)
self.attributes = attributes self.attributes = attributes
self.attributes.append(att_tuple) #self.attributes.append(att_tuple)
def visitAssAttr(self, node): def visitAssAttr(self, node):
self.default_visit(node, node.attrname) self.default_visit(node, node.attrname)
...@@ -481,13 +352,17 @@ class AttributeVisitor(BaseVisitor): ...@@ -481,13 +352,17 @@ class AttributeVisitor(BaseVisitor):
self.default_visit(node, node.attrname + '.' + suffix) self.default_visit(node, node.attrname + '.' + suffix)
def visitName(self, node, 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): class FunctionVisitor(DocstringVisitor):
in_function = 0 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): def visitFunction(self, node):
if self.in_function: if self.in_function:
...@@ -495,9 +370,11 @@ class FunctionVisitor(DocstringVisitor): ...@@ -495,9 +370,11 @@ class FunctionVisitor(DocstringVisitor):
# Don't bother with nested function definitions. # Don't bother with nested function definitions.
return return
self.in_function = 1 self.in_function = 1
self.function = function = self.function_class(node, node.name) self.function = function = make_function_like_section(
if node.doc is not None: name=node.name,
function.append(Docstring(node, node.doc)) lineno=node.lineno,
doc=node.doc,
function_class=self.function_class)
self.context.append(function) self.context.append(function)
self.documentable = function self.documentable = function
self.parse_parameter_list(node) self.parse_parameter_list(node)
...@@ -509,10 +386,11 @@ class FunctionVisitor(DocstringVisitor): ...@@ -509,10 +386,11 @@ class FunctionVisitor(DocstringVisitor):
special = [] special = []
argnames = list(node.argnames) argnames = list(node.argnames)
if node.kwargs: if node.kwargs:
special.append(ExcessKeywordArguments(node, argnames[-1])) special.append(make_parameter(argnames[-1], excess_keyword=1))
argnames.pop() argnames.pop()
if node.varargs: if node.varargs:
special.append(ExcessPositionalArguments(node, argnames[-1])) special.append(make_parameter(argnames[-1],
excess_positional=1))
argnames.pop() argnames.pop()
defaults = list(node.defaults) defaults = list(node.defaults)
defaults = [None] * (len(argnames) - len(defaults)) + defaults defaults = [None] * (len(argnames) - len(defaults)) + defaults
...@@ -521,17 +399,21 @@ class FunctionVisitor(DocstringVisitor): ...@@ -521,17 +399,21 @@ class FunctionVisitor(DocstringVisitor):
#print >>sys.stderr, function_parameters #print >>sys.stderr, function_parameters
for argname, default in zip(argnames, defaults): for argname, default in zip(argnames, defaults):
if type(argname) is TupleType: 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) argname = normalize_parameter_name(argname)
else: else:
parameter = Parameter(node, argname) parameter = make_parameter(argname)
if default: 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) parameters.append(parameter)
if parameters or special: if parameters or special:
special.reverse() special.reverse()
parameters.extend(special) parameters.extend(special)
parameter_list = ParameterList(node) parameter_list = pynodes.parameter_list()
parameter_list.extend(parameters) parameter_list.extend(parameters)
self.function.append(parameter_list) self.function.append(parameter_list)
...@@ -554,9 +436,9 @@ class ClassVisitor(AssignmentVisitor): ...@@ -554,9 +436,9 @@ class ClassVisitor(AssignmentVisitor):
#pdb.set_trace() #pdb.set_trace()
for base in node.bases: for base in node.bases:
self.visit(base) self.visit(base)
self.klass = klass = Class(node, node.name, self.bases) self.klass = klass = make_class_section(node.name, self.bases,
if node.doc is not None: doc=node.doc,
klass.append(Docstring(node, node.doc)) lineno=node.lineno)
self.context.append(klass) self.context.append(klass)
self.documentable = klass self.documentable = klass
self.visit(node.code) self.visit(node.code)
...@@ -578,19 +460,17 @@ class ClassVisitor(AssignmentVisitor): ...@@ -578,19 +460,17 @@ class ClassVisitor(AssignmentVisitor):
def visitFunction(self, node): def visitFunction(self, node):
if node.name == '__init__': 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: else:
visitor = MethodVisitor(self.token_parser) visitor = FunctionVisitor(self.token_parser,
compiler.walk(node, visitor, walker=visitor) function_class=pynodes.method_section)
compiler.walk(node, visitor, walker=visitor)
self.context[-1].append(visitor.function) self.context[-1].append(visitor.function)
class MethodVisitor(FunctionVisitor): class InitMethodVisitor(FunctionVisitor, AssignmentVisitor): pass
function_class = Method
class InitMethodVisitor(MethodVisitor, AssignmentVisitor): pass
class TokenParser: class TokenParser:
...@@ -744,6 +624,85 @@ class TokenParser: ...@@ -744,6 +624,85 @@ class TokenParser:
return parameters 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): def trim_docstring(text):
""" """
Trim indentation and blank lines from docstring text & return it. Trim indentation and blank lines from docstring text & return it.
...@@ -782,3 +741,18 @@ def normalize_parameter_name(name): ...@@ -782,3 +741,18 @@ def normalize_parameter_name(name):
return '(%s)' % ', '.join([normalize_parameter_name(n) for n in name]) return '(%s)' % ', '.join([normalize_parameter_name(n) for n in name])
else: else:
return name 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 # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:08 $ # Date: $Date: 2004/05/12 19:57:52 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -14,7 +14,6 @@ __docformat__ = 'reStructuredText' ...@@ -14,7 +14,6 @@ __docformat__ = 'reStructuredText'
import sys import sys
from docutils import frontend, readers from docutils import frontend, readers
from docutils.transforms import frontmatter, references from docutils.transforms import frontmatter, references
from docutils.parsers.rst import Parser
class Reader(readers.Reader): class Reader(readers.Reader):
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:39 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Authors: David Goodger, Ueli Schlaepfer # Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -59,6 +59,7 @@ class Transform: ...@@ -59,6 +59,7 @@ class Transform:
self.language = languages.get_language( self.language = languages.get_language(
document.settings.language_code) document.settings.language_code)
"""Language module local to this document.""" """Language module local to this document."""
def apply(self): def apply(self):
"""Override to apply the transform to the document tree.""" """Override to apply the transform to the document tree."""
...@@ -76,7 +77,8 @@ class Transformer(TransformSpec): ...@@ -76,7 +77,8 @@ class Transformer(TransformSpec):
default_transforms = (universal.Decorations, default_transforms = (universal.Decorations,
universal.FinalChecks, universal.FinalChecks,
universal.Messages) universal.Messages,
universal.FilterMessages)
"""These transforms are applied to all document trees.""" """These transforms are applied to all document trees."""
def __init__(self, document): def __init__(self, document):
...@@ -84,6 +86,9 @@ class Transformer(TransformSpec): ...@@ -84,6 +86,9 @@ class Transformer(TransformSpec):
"""List of transforms to apply. Each item is a 3-tuple: """List of transforms to apply. Each item is a 3-tuple:
``(priority string, transform class, pending node or None)``.""" ``(priority string, transform class, pending node or None)``."""
self.unknown_reference_resolvers = []
"""List of hook functions which assist in resolving references"""
self.document = document self.document = document
"""The `nodes.document` object this Transformer is attached to.""" """The `nodes.document` object this Transformer is attached to."""
...@@ -149,6 +154,16 @@ class Transformer(TransformSpec): ...@@ -149,6 +154,16 @@ class Transformer(TransformSpec):
self.add_transforms(component.default_transforms) self.add_transforms(component.default_transforms)
self.components[component.component_type] = component self.components[component.component_type] = component
self.sorted = 0 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): def apply_transforms(self):
"""Apply all of the stored transforms, in priority order.""" """Apply all of the stored transforms, in priority order."""
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Authors: David Goodger, Ueli Schlaepfer # Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -334,10 +334,10 @@ class DocInfo(Transform): ...@@ -334,10 +334,10 @@ class DocInfo(Transform):
return 1 return 1
rcs_keyword_substitutions = [ 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.IGNORECASE), r'\1-\2-\3'),
(re.compile(r'\$' r'RCSfile: (.+),v \$$', re.IGNORECASE), r'\1'), (re.compile(r'\$' r'RCSfile: (.+),v \$', re.IGNORECASE), r'\1'),
(re.compile(r'\$[a-zA-Z]+: (.+) \$$'), r'\1'),] (re.compile(r'\$[a-zA-Z]+: (.+) \$'), r'\1'),]
def extract_authors(self, field, name, docinfo): def extract_authors(self, field, name, docinfo):
try: try:
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov # Authors: David Goodger, Ueli Schlaepfer, Dmitry Jemerov
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -34,7 +34,8 @@ class SectNum(Transform): ...@@ -34,7 +34,8 @@ class SectNum(Transform):
def apply(self): def apply(self):
self.maxdepth = self.startnode.details.get('depth', sys.maxint) self.maxdepth = self.startnode.details.get('depth', sys.maxint)
self.startnode.parent.remove(self.startnode) self.startnode.parent.remove(self.startnode)
self.update_section_numbers(self.document) if self.document.settings.sectnum_xform:
self.update_section_numbers(self.document)
def update_section_numbers(self, node, prefix=(), depth=0): def update_section_numbers(self, node, prefix=(), depth=0):
depth += 1 depth += 1
...@@ -58,11 +59,11 @@ class Contents(Transform): ...@@ -58,11 +59,11 @@ class Contents(Transform):
""" """
This transform generates a table of contents from the entire document tree This transform generates a table of contents from the entire document tree
or from a single branch. It locates "section" elements and builds them 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 into a nested bullet list, which is placed within a "topic" created by the
either explicitly specified, taken from the appropriate language module, contents directive. A title is either explicitly specified, taken from
or omitted (local table of contents). The depth may be specified. the appropriate language module, or omitted (local table of contents).
Two-way references between the table of contents and section titles are The depth may be specified. Two-way references between the table of
generated (requires Writer support). contents and section titles are generated (requires Writer support).
This transform requires a startnode, which which contains generation This transform requires a startnode, which which contains generation
options and provides the location for the generated table of contents (the options and provides the location for the generated table of contents (the
...@@ -72,41 +73,26 @@ class Contents(Transform): ...@@ -72,41 +73,26 @@ class Contents(Transform):
default_priority = 720 default_priority = 720
def apply(self): def apply(self):
topic = nodes.topic(CLASS='contents')
details = self.startnode.details details = self.startnode.details
if details.has_key('class'):
topic.set_class(details['class'])
title = details['title']
if details.has_key('local'): if details.has_key('local'):
startnode = self.startnode.parent startnode = self.startnode.parent.parent
# @@@ generate an error if the startnode (directive) not at # @@@ generate an error if the startnode (directive) not at
# section/document top-level? Drag it up until it is? # section/document top-level? Drag it up until it is?
while not isinstance(startnode, nodes.Structural): while not isinstance(startnode, nodes.Structural):
startnode = startnode.parent startnode = startnode.parent
else: else:
startnode = self.document startnode = self.document
if not title:
title = nodes.title('', self.language.labels['contents']) self.toc_id = self.startnode.parent['id']
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']
if details.has_key('backlinks'): if details.has_key('backlinks'):
self.backlinks = details['backlinks'] self.backlinks = details['backlinks']
else: else:
self.backlinks = self.document.settings.toc_backlinks self.backlinks = self.document.settings.toc_backlinks
contents = self.build_contents(startnode) contents = self.build_contents(startnode)
if len(contents): if len(contents):
topic += contents self.startnode.parent.replace(self.startnode, contents)
self.startnode.parent.replace(self.startnode, topic)
else: else:
self.startnode.parent.remove(self.startnode) self.startnode.parent.parent.remove(self.startnode.parent)
def build_contents(self, node, level=0): def build_contents(self, node, level=0):
level += 1 level += 1
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -19,7 +19,7 @@ import sys ...@@ -19,7 +19,7 @@ import sys
import os import os
import re import re
import time import time
from docutils import nodes, utils from docutils import nodes, utils, languages
from docutils import ApplicationError, DataError from docutils import ApplicationError, DataError
from docutils.transforms import Transform, TransformError from docutils.transforms import Transform, TransformError
from docutils.transforms import parts, references, misc from docutils.transforms import parts, references, misc
...@@ -137,15 +137,24 @@ class Headers(Transform): ...@@ -137,15 +137,24 @@ class Headers(Transform):
class Contents(Transform): class Contents(Transform):
""" """
Insert a table of contents transform placeholder into the document after Insert an empty table of contents topic and a transform placeholder into
the RFC 2822 header. the document after the RFC 2822 header.
""" """
default_priority = 380 default_priority = 380
def apply(self): def apply(self):
pending = nodes.pending(parts.Contents, {'title': None}) language = languages.get_language(self.document.settings.language_code)
self.document.insert(1, pending) 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) self.document.note_pending(pending)
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -216,7 +216,13 @@ class IndirectHyperlinks(Transform): ...@@ -216,7 +216,13 @@ class IndirectHyperlinks(Transform):
refname = target['refname'] refname = target['refname']
reftarget_id = self.document.nameids.get(refname) reftarget_id = self.document.nameids.get(refname)
if not reftarget_id: if not reftarget_id:
self.nonexistent_indirect_target(target) # 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 return
reftarget = self.document.ids[reftarget_id] reftarget = self.document.ids[reftarget_id]
if isinstance(reftarget, nodes.target) \ if isinstance(reftarget, nodes.target) \
...@@ -248,7 +254,11 @@ class IndirectHyperlinks(Transform): ...@@ -248,7 +254,11 @@ class IndirectHyperlinks(Transform):
reftarget.referenced = 1 reftarget.referenced = 1
def nonexistent_indirect_target(self, target): def nonexistent_indirect_target(self, target):
self.indirect_target_error(target, 'which does not exist') 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): def circular_indirect_reference(self, target):
self.indirect_target_error(target, 'forming a circular reference') self.indirect_target_error(target, 'forming a circular reference')
...@@ -353,7 +363,8 @@ class ExternalTargets(Transform): ...@@ -353,7 +363,8 @@ class ExternalTargets(Transform):
try: try:
reflist = self.document.refnames[name] reflist = self.document.refnames[name]
except KeyError, instance: except KeyError, instance:
if target.referenced: # @@@ First clause correct???
if not isinstance(target, nodes.target) or target.referenced:
continue continue
msg = self.document.reporter.info( msg = self.document.reporter.info(
'External hyperlink target "%s" is not referenced.' 'External hyperlink target "%s" is not referenced.'
......
# Authors: David Goodger, Ueli Schlaepfer # Authors: David Goodger, Ueli Schlaepfer
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:55 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -111,6 +111,29 @@ class Messages(Transform): ...@@ -111,6 +111,29 @@ class Messages(Transform):
self.document += section 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): class TestMessages(Transform):
""" """
...@@ -136,7 +159,9 @@ class FinalChecks(Transform): ...@@ -136,7 +159,9 @@ class FinalChecks(Transform):
default_priority = 840 default_priority = 840
def apply(self): def apply(self):
visitor = FinalCheckVisitor(self.document) visitor = FinalCheckVisitor(
self.document,
self.document.transformer.unknown_reference_resolvers)
self.document.walk(visitor) self.document.walk(visitor)
if self.document.settings.expose_internals: if self.document.settings.expose_internals:
visitor = InternalAttributeExposer(self.document) visitor = InternalAttributeExposer(self.document)
...@@ -144,6 +169,11 @@ class FinalChecks(Transform): ...@@ -144,6 +169,11 @@ class FinalChecks(Transform):
class FinalCheckVisitor(nodes.SparseNodeVisitor): 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): def unknown_visit(self, node):
pass pass
...@@ -154,15 +184,24 @@ class FinalCheckVisitor(nodes.SparseNodeVisitor): ...@@ -154,15 +184,24 @@ class FinalCheckVisitor(nodes.SparseNodeVisitor):
refname = node['refname'] refname = node['refname']
id = self.document.nameids.get(refname) id = self.document.nameids.get(refname)
if id is None: if id is None:
msg = self.document.reporter.error( for resolver_function in self.unknown_reference_resolvers:
'Unknown target name: "%s".' % (node['refname']), if resolver_function(node):
base_node=node) break
msgid = self.document.set_id(msg) else:
prb = nodes.problematic( if self.document.nameids.has_key(refname):
node.rawsource, node.rawsource, refid=msgid) msg = self.document.reporter.error(
prbid = self.document.set_id(prb) 'Duplicate target name, cannot be used as a unique '
msg.add_backref(prbid) 'reference: "%s".' % (node['refname']), base_node=node)
node.parent.replace(node, prb) else:
msg = self.document.reporter.error(
'Unknown target name: "%s".' % (node['refname']),
base_node=node)
msgid = self.document.set_id(msg)
prb = nodes.problematic(
node.rawsource, node.rawsource, refid=msgid)
prbid = self.document.set_id(prb)
msg.add_backref(prbid)
node.parent.replace(node, prb)
else: else:
del node['refname'] del node['refname']
node['refid'] = id node['refid'] = id
......
""" """
`schemes` is a dictionary with lowercase URI addressing schemes as `schemes` is a dictionary with lowercase URI addressing schemes as
keys and descriptions as values. It was compiled from the index at 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. # Many values are blank and should be filled in with useful descriptions.
...@@ -26,10 +27,12 @@ schemes = { ...@@ -26,10 +27,12 @@ schemes = {
'specialized to justify their own schemes'), 'specialized to justify their own schemes'),
'fax': ('a connection to a terminal that can handle telefaxes ' 'fax': ('a connection to a terminal that can handle telefaxes '
'(facsimiles); RFC 2806'), '(facsimiles); RFC 2806'),
'feed' : 'NetNewsWire feed',
'file': 'Host-specific file names', 'file': 'Host-specific file names',
'finger': '', 'finger': '',
'freenet': '', 'freenet': '',
'ftp': 'File Transfer Protocol', 'ftp': 'File Transfer Protocol',
'go': 'go; RFC3368',
'gopher': 'The Gopher Protocol', 'gopher': 'The Gopher Protocol',
'gsm-sms': ('Global System for Mobile Communications Short Message ' 'gsm-sms': ('Global System for Mobile Communications Short Message '
'Service'), 'Service'),
...@@ -40,16 +43,19 @@ schemes = { ...@@ -40,16 +43,19 @@ schemes = {
'hnews': 'an HTTP-tunneling variant of the NNTP news protocol', 'hnews': 'an HTTP-tunneling variant of the NNTP news protocol',
'http': 'Hypertext Transfer Protocol', 'http': 'Hypertext Transfer Protocol',
'https': 'HTTP over SSL', 'https': 'HTTP over SSL',
'hydra': 'SubEthaEdit URI. See http://www.codingmonkeys.de/subethaedit.',
'iioploc': 'Internet Inter-ORB Protocol Location?', 'iioploc': 'Internet Inter-ORB Protocol Location?',
'ilu': 'Inter-Language Unification', 'ilu': 'Inter-Language Unification',
'im': 'Instant Messaging',
'imap': 'Internet Message Access Protocol', 'imap': 'Internet Message Access Protocol',
'ior': 'CORBA interoperable object reference', 'ior': 'CORBA interoperable object reference',
'ipp': 'Internet Printing Protocol', 'ipp': 'Internet Printing Protocol',
'irc': 'Internet Relay Chat', 'irc': 'Internet Relay Chat',
'iseek' : 'See www.ambrosiasw.com; a little util for OS X.',
'jar': 'Java archive', 'jar': 'Java archive',
'javascript': ('JavaScript code; evaluates the expression after the ' 'javascript': ('JavaScript code; evaluates the expression after the '
'colon'), 'colon'),
'jdbc': '', 'jdbc': 'JDBC connection URI.',
'ldap': 'Lightweight Directory Access Protocol', 'ldap': 'Lightweight Directory Access Protocol',
'lifn': '', 'lifn': '',
'livescript': '', 'livescript': '',
...@@ -62,6 +68,7 @@ schemes = { ...@@ -62,6 +68,7 @@ schemes = {
'mocha': '', 'mocha': '',
'modem': ('a connection to a terminal that can handle incoming data ' 'modem': ('a connection to a terminal that can handle incoming data '
'calls; RFC 2806'), 'calls; RFC 2806'),
'mupdate': 'Mailbox Update (MUPDATE) Protocol',
'news': 'USENET news', 'news': 'USENET news',
'nfs': 'Network File System protocol', 'nfs': 'Network File System protocol',
'nntp': 'USENET news using NNTP access', 'nntp': 'USENET news using NNTP access',
...@@ -69,8 +76,10 @@ schemes = { ...@@ -69,8 +76,10 @@ schemes = {
'phone': '', 'phone': '',
'pop': 'Post Office Protocol', 'pop': 'Post Office Protocol',
'pop3': 'Post Office Protocol v3', 'pop3': 'Post Office Protocol v3',
'pres': 'Presence',
'printer': '', 'printer': '',
'prospero': 'Prospero Directory Service', 'prospero': 'Prospero Directory Service',
'rdar' : 'URLs found in Darwin source (http://www.opensource.apple.com/darwinsource/).',
'res': '', 'res': '',
'rtsp': 'real time streaming protocol', 'rtsp': 'real time streaming protocol',
'rvp': '', 'rvp': '',
...@@ -80,8 +89,12 @@ schemes = { ...@@ -80,8 +89,12 @@ schemes = {
'service': 'service location', 'service': 'service location',
'shttp': 'secure hypertext transfer protocol', 'shttp': 'secure hypertext transfer protocol',
'sip': 'Session Initiation Protocol', 'sip': 'Session Initiation Protocol',
'smb': '', 'sips': 'secure session intitiaion protocol',
'smb': 'SAMBA filesystems.',
'snews': 'For NNTP postings via SSL', 'snews': 'For NNTP postings via SSL',
'soap.beep': '',
'soap.beeps': '',
'ssh': 'Reference to interactive sessions via ssh.',
't120': 'real time data conferencing (audiographics)', 't120': 'real time data conferencing (audiographics)',
'tcp': '', 'tcp': '',
'tel': ('a connection to a terminal that handles normal voice ' 'tel': ('a connection to a terminal that handles normal voice '
...@@ -90,6 +103,7 @@ schemes = { ...@@ -90,6 +103,7 @@ schemes = {
'RFC 2806.'), 'RFC 2806.'),
'telephone': 'telephone', 'telephone': 'telephone',
'telnet': 'Reference to interactive sessions', 'telnet': 'Reference to interactive sessions',
'tftp': 'Trivial File Transfer Protocol',
'tip': 'Transaction Internet Protocol', 'tip': 'Transaction Internet Protocol',
'tn3270': 'Interactive 3270 emulation sessions', 'tn3270': 'Interactive 3270 emulation sessions',
'tv': '', 'tv': '',
...@@ -101,5 +115,8 @@ schemes = { ...@@ -101,5 +115,8 @@ schemes = {
'wais': 'Wide Area Information Servers', 'wais': 'Wide Area Information Servers',
'whodp': '', 'whodp': '',
'whois++': 'Distributed directory service.', '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.50r': 'Z39.50 Retrieval',
'z39.50s': 'Z39.50 Session',} 'z39.50s': 'Z39.50 Session',}
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:04 $ # Date: $Date: 2004/05/12 19:57:39 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -324,7 +324,9 @@ def assemble_option_dict(option_list, options_spec): ...@@ -324,7 +324,9 @@ def assemble_option_dict(option_list, options_spec):
""" """
options = {} options = {}
for name, value in option_list: for name, value in option_list:
convertor = options_spec[name] # raises KeyError if unknown convertor = options_spec[name] # raises KeyError if unknown
if convertor is None:
raise KeyError(name) # or if explicitly disabled
if options.has_key(name): if options.has_key(name):
raise DuplicateOptionError('duplicate option "%s"' % name) raise DuplicateOptionError('duplicate option "%s"' % name)
try: try:
...@@ -406,7 +408,7 @@ def clean_rcs_keywords(paragraph, keyword_substitutions): ...@@ -406,7 +408,7 @@ def clean_rcs_keywords(paragraph, keyword_substitutions):
if len(paragraph) == 1 and isinstance(paragraph[0], nodes.Text): if len(paragraph) == 1 and isinstance(paragraph[0], nodes.Text):
textnode = paragraph[0] textnode = paragraph[0]
for pattern, substitution in keyword_substitutions: for pattern, substitution in keyword_substitutions:
match = pattern.match(textnode.data) match = pattern.search(textnode.data)
if match: if match:
textnode.data = pattern.sub(substitution, textnode.data) textnode.data = pattern.sub(substitution, textnode.data)
return return
......
# Authors: David Goodger # Authors: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -26,25 +26,43 @@ class Writer(Component): ...@@ -26,25 +26,43 @@ class Writer(Component):
Each writer must support all standard node types listed in Each writer must support all standard node types listed in
`docutils.nodes.node_class_names`. `docutils.nodes.node_class_names`.
Call `write()` to process a document. The `write()` method is the main entry point.
""" """
component_type = 'writer' component_type = 'writer'
config_section = 'writers' config_section = 'writers'
document = None 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 = None
"""Language module for the document.""" """Language module for the document; set by `write`."""
destination = None 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): 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): 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.document = document
self.language = languages.get_language( self.language = languages.get_language(
document.settings.language_code) document.settings.language_code)
...@@ -55,9 +73,10 @@ class Writer(Component): ...@@ -55,9 +73,10 @@ class Writer(Component):
def translate(self): 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 combination with a call to `docutils.nodes.Node.walk()` or
`docutils.nodes.Node.walkabout()`. The ``NodeVisitor`` subclass must `docutils.nodes.Node.walkabout()`. The ``NodeVisitor`` subclass must
support all standard elements (listed in support all standard elements (listed in
...@@ -66,6 +85,10 @@ class Writer(Component): ...@@ -66,6 +85,10 @@ class Writer(Component):
""" """
raise NotImplementedError('subclass must override this method') 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 = { _writer_aliases = {
'html': 'html4css1', 'html': 'html4css1',
......
# Authors: David Goodger # Authors: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -22,6 +22,10 @@ import os.path ...@@ -22,6 +22,10 @@ import os.path
import time import time
import re import re
from types import ListType from types import ListType
try:
import Image # check for the Python Imaging Library
except ImportError:
Image = None
import docutils import docutils
from docutils import frontend, nodes, utils, writers, languages from docutils import frontend, nodes, utils, writers, languages
...@@ -55,6 +59,11 @@ class Writer(writers.Writer): ...@@ -55,6 +59,11 @@ class Writer(writers.Writer):
'stylesheet, do not embed it.', 'stylesheet, do not embed it.',
['--embed-stylesheet'], ['--embed-stylesheet'],
{'action': 'store_true', 'validator': frontend.validate_boolean}), {'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 ' ('Format for footnote references: one of "superscript" or '
'"brackets". Default is "superscript".', '"brackets". Default is "superscript".',
['--footnote-references'], ['--footnote-references'],
...@@ -85,9 +94,6 @@ class Writer(writers.Writer): ...@@ -85,9 +94,6 @@ class Writer(writers.Writer):
config_section = 'html4css1 writer' config_section = 'html4css1 writer'
config_section_dependencies = ('writers',) config_section_dependencies = ('writers',)
output = None
"""Final translated form of `document`."""
def __init__(self): def __init__(self):
writers.Writer.__init__(self) writers.Writer.__init__(self)
self.translator_class = HTMLTranslator self.translator_class = HTMLTranslator
...@@ -96,14 +102,17 @@ class Writer(writers.Writer): ...@@ -96,14 +102,17 @@ class Writer(writers.Writer):
visitor = self.translator_class(self.document) visitor = self.translator_class(self.document)
self.document.walkabout(visitor) self.document.walkabout(visitor)
self.output = visitor.astext() self.output = visitor.astext()
self.head_prefix = visitor.head_prefix self.visitor = visitor
self.stylesheet = visitor.stylesheet for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
self.head = visitor.head 'body_pre_docinfo', 'docinfo', 'body', 'fragment',
self.body_prefix = visitor.body_prefix 'body_suffix'):
self.body_pre_docinfo = visitor.body_pre_docinfo setattr(self, attr, getattr(visitor, attr))
self.docinfo = visitor.docinfo
self.body = visitor.body def assemble_parts(self):
self.body_suffix = visitor.body_suffix 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): class HTMLTranslator(nodes.NodeVisitor):
...@@ -143,14 +152,14 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -143,14 +152,14 @@ class HTMLTranslator(nodes.NodeVisitor):
- Regardless of the above, in definitions, table cells, field bodies, - Regardless of the above, in definitions, table cells, field bodies,
option descriptions, and list items, mark the first child with option descriptions, and list items, mark the first child with
'class="first"' and the last child with 'class="last"'. The stylesheet '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 The ``no_compact_lists`` setting (``--no-compact-lists`` command-line
option) disables list whitespace optimization. option) disables list whitespace optimization.
""" """
xml_declaration = '<?xml version="1.0" encoding="%s" ?>\n' xml_declaration = '<?xml version="1.0" encoding="%s" ?>\n'
doctype = ('<!DOCTYPE html' doctype = ('<!DOCTYPE html'
' PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' ' PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
' "http://www.w3.org/TR/xhtml1/DTD/' ' "http://www.w3.org/TR/xhtml1/DTD/'
'xhtml1-transitional.dtd">\n') 'xhtml1-transitional.dtd">\n')
...@@ -171,11 +180,12 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -171,11 +180,12 @@ class HTMLTranslator(nodes.NodeVisitor):
self.settings = settings = document.settings self.settings = settings = document.settings
lcode = settings.language_code lcode = settings.language_code
self.language = languages.get_language(lcode) self.language = languages.get_language(lcode)
self.meta = [self.content_type % settings.output_encoding,
self.generator % docutils.__version__]
self.head_prefix = [ self.head_prefix = [
self.doctype, self.doctype,
self.html_head % (lcode, lcode), self.html_head % (lcode, lcode)]
self.content_type % settings.output_encoding, self.head_prefix.extend(self.meta)
self.generator % docutils.__version__]
if settings.xml_declaration: if settings.xml_declaration:
self.head_prefix.insert(0, self.xml_declaration self.head_prefix.insert(0, self.xml_declaration
% settings.output_encoding) % settings.output_encoding)
...@@ -192,11 +202,17 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -192,11 +202,17 @@ class HTMLTranslator(nodes.NodeVisitor):
else: else:
self.stylesheet = [] self.stylesheet = []
self.body_prefix = ['</head>\n<body>\n'] self.body_prefix = ['</head>\n<body>\n']
# document title, subtitle display
self.body_pre_docinfo = [] self.body_pre_docinfo = []
# author, date, etc.
self.docinfo = [] self.docinfo = []
self.body = [] self.body = []
self.fragment = []
self.body_suffix = ['</body>\n</html>\n'] self.body_suffix = ['</body>\n</html>\n']
self.section_level = 0 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.context = []
self.topic_class = '' self.topic_class = ''
self.colspecs = [] self.colspecs = []
...@@ -204,6 +220,11 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -204,6 +220,11 @@ class HTMLTranslator(nodes.NodeVisitor):
self.compact_simple = None self.compact_simple = None
self.in_docinfo = None self.in_docinfo = None
self.in_sidebar = 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): def get_stylesheet_reference(self, relative_to=None):
settings = self.settings settings = self.settings
...@@ -267,8 +288,11 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -267,8 +288,11 @@ class HTMLTranslator(nodes.NodeVisitor):
parts.append('%s="%s"' % (name.lower(), parts.append('%s="%s"' % (name.lower(),
self.attval(' '.join(values)))) self.attval(' '.join(values))))
else: else:
parts.append('%s="%s"' % (name.lower(), try:
self.attval(unicode(value)))) 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) return '<%s%s>%s' % (' '.join(parts), infix, suffix)
def emptytag(self, node, tagname, suffix='\n', **attributes): def emptytag(self, node, tagname, suffix='\n', **attributes):
...@@ -307,7 +331,7 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -307,7 +331,7 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'div', self.body.append(self.starttag(node, 'div',
CLASS=(name or 'admonition'))) CLASS=(name or 'admonition')))
if name: if name:
self.body.append('<p class="admonition-title">' self.body.append('<p class="admonition-title first">'
+ self.language.labels[name] + '</p>\n') + self.language.labels[name] + '</p>\n')
def depart_admonition(self, node=None): def depart_admonition(self, node=None):
...@@ -519,14 +543,14 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -519,14 +543,14 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('</tbody>\n</table>\n') self.body.append('</tbody>\n</table>\n')
self.in_docinfo = None self.in_docinfo = None
start = self.context.pop() start = self.context.pop()
self.body_pre_docinfo = self.body[:start]
self.docinfo = self.body[start:] self.docinfo = self.body[start:]
self.body = [] self.body = []
def visit_docinfo_item(self, node, name, meta=1): def visit_docinfo_item(self, node, name, meta=1):
if meta: if meta:
self.head.append('<meta name="%s" content="%s" />\n' meta_tag = '<meta name="%s" content="%s" />\n' \
% (name, self.attval(node.astext()))) % (name, self.attval(node.astext()))
self.add_meta(meta_tag)
self.body.append(self.starttag(node, 'tr', '')) self.body.append(self.starttag(node, 'tr', ''))
self.body.append('<th class="docinfo-name">%s:</th>\n<td>' self.body.append('<th class="docinfo-name">%s:</th>\n<td>'
% self.language.labels[name]) % self.language.labels[name])
...@@ -546,9 +570,14 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -546,9 +570,14 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('\n</pre>\n') self.body.append('\n</pre>\n')
def visit_document(self, node): 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): 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') self.body.append('</div>\n')
def visit_emphasis(self, node): def visit_emphasis(self, node):
...@@ -671,6 +700,7 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -671,6 +700,7 @@ class HTMLTranslator(nodes.NodeVisitor):
footer = (['<hr class="footer" />\n', footer = (['<hr class="footer" />\n',
self.starttag(node, 'div', CLASS='footer')] self.starttag(node, 'div', CLASS='footer')]
+ self.body[start:] + ['</div>\n']) + self.body[start:] + ['</div>\n'])
self.footer.extend(footer)
self.body_suffix[:0] = footer self.body_suffix[:0] = footer
del self.body[start:] del self.body[start:]
...@@ -739,9 +769,11 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -739,9 +769,11 @@ class HTMLTranslator(nodes.NodeVisitor):
def depart_header(self, node): def depart_header(self, node):
start = self.context.pop() start = self.context.pop()
self.body_prefix.append(self.starttag(node, 'div', CLASS='header')) header = [self.starttag(node, 'div', CLASS='header')]
self.body_prefix.extend(self.body[start:]) header.extend(self.body[start:])
self.body_prefix.append('<hr />\n</div>\n') header.append('<hr />\n</div>\n')
self.body_prefix.extend(header)
self.header = header
del self.body[start:] del self.body[start:]
def visit_hint(self, node): def visit_hint(self, node):
...@@ -756,6 +788,27 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -756,6 +788,27 @@ class HTMLTranslator(nodes.NodeVisitor):
del atts['class'] # prevent duplication with node attrs del atts['class'] # prevent duplication with node attrs
atts['src'] = atts['uri'] atts['src'] = atts['uri']
del atts['uri'] del atts['uri']
if atts.has_key('scale'):
if 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'): if not atts.has_key('alt'):
atts['alt'] = atts['src'] atts['alt'] = atts['src']
if isinstance(node.parent, nodes.TextElement): if isinstance(node.parent, nodes.TextElement):
...@@ -837,11 +890,16 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -837,11 +890,16 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append('\n</pre>\n') self.body.append('\n</pre>\n')
def visit_meta(self, node): def visit_meta(self, node):
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): def depart_meta(self, node):
pass pass
def add_meta(self, tag):
self.meta.append(tag)
self.head.append(tag)
def visit_note(self, node): def visit_note(self, node):
self.visit_admonition(node, 'note') self.visit_admonition(node, 'note')
...@@ -941,6 +999,11 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -941,6 +999,11 @@ class HTMLTranslator(nodes.NodeVisitor):
raise nodes.SkipNode raise nodes.SkipNode
def visit_reference(self, node): 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'): if node.has_key('refuri'):
href = node['refuri'] href = node['refuri']
elif node.has_key('refid'): elif node.has_key('refid'):
...@@ -952,6 +1015,7 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -952,6 +1015,7 @@ class HTMLTranslator(nodes.NodeVisitor):
def depart_reference(self, node): def depart_reference(self, node):
self.body.append('</a>') self.body.append('</a>')
self.body.append(self.context.pop())
def visit_revision(self, node): def visit_revision(self, node):
self.visit_docinfo_item(node, 'revision', meta=None) self.visit_docinfo_item(node, 'revision', meta=None)
...@@ -1017,12 +1081,18 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -1017,12 +1081,18 @@ class HTMLTranslator(nodes.NodeVisitor):
self.body.append(self.starttag(node, 'p', '', self.body.append(self.starttag(node, 'p', '',
CLASS='sidebar-subtitle')) CLASS='sidebar-subtitle'))
self.context.append('</p>\n') self.context.append('</p>\n')
else: elif isinstance(node.parent, nodes.document):
self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle')) self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
self.context.append('</h2>\n') self.context.append('</h2>\n')
self.in_document_title = len(self.body)
def depart_subtitle(self, node): def depart_subtitle(self, node):
self.body.append(self.context.pop()) 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): def visit_superscript(self, node):
self.body.append(self.starttag(node, 'sup', '')) self.body.append(self.starttag(node, 'sup', ''))
...@@ -1135,27 +1205,35 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -1135,27 +1205,35 @@ class HTMLTranslator(nodes.NodeVisitor):
def visit_title(self, node): def visit_title(self, node):
"""Only 6 section levels are supported by HTML.""" """Only 6 section levels are supported by HTML."""
check_id = 0 check_id = 0
close_tag = '</p>\n'
if isinstance(node.parent, nodes.topic): if isinstance(node.parent, nodes.topic):
self.body.append( self.body.append(
self.starttag(node, 'p', '', CLASS='topic-title')) self.starttag(node, 'p', '', CLASS='topic-title first'))
check_id = 1 check_id = 1
elif isinstance(node.parent, nodes.sidebar): elif isinstance(node.parent, nodes.sidebar):
self.body.append( self.body.append(
self.starttag(node, 'p', '', CLASS='sidebar-title')) self.starttag(node, 'p', '', CLASS='sidebar-title first'))
check_id = 1 check_id = 1
elif isinstance(node.parent, nodes.admonition): elif isinstance(node.parent, nodes.admonition):
self.body.append( 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 check_id = 1
close_tag = '</caption>\n'
elif self.section_level == 0: elif self.section_level == 0:
# document title # document title
self.head.append('<title>%s</title>\n' self.head.append('<title>%s</title>\n'
% self.encode(node.astext())) % self.encode(node.astext()))
self.body.append(self.starttag(node, 'h1', '', CLASS='title')) self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
self.context.append('</h1>\n') self.context.append('</h1>\n')
self.in_document_title = len(self.body)
else: else:
h_level = self.section_level + self.initial_header_level - 1
self.body.append( self.body.append(
self.starttag(node, 'h%s' % self.section_level, '')) self.starttag(node, 'h%s' % h_level, ''))
atts = {} atts = {}
if node.parent.hasattr('id'): if node.parent.hasattr('id'):
atts['name'] = node.parent['id'] atts['name'] = node.parent['id']
...@@ -1163,17 +1241,22 @@ class HTMLTranslator(nodes.NodeVisitor): ...@@ -1163,17 +1241,22 @@ class HTMLTranslator(nodes.NodeVisitor):
atts['class'] = 'toc-backref' atts['class'] = 'toc-backref'
atts['href'] = '#' + node['refid'] atts['href'] = '#' + node['refid']
self.body.append(self.starttag({}, 'a', '', **atts)) self.body.append(self.starttag({}, 'a', '', **atts))
self.context.append('</a></h%s>\n' % (self.section_level)) self.context.append('</a></h%s>\n' % (h_level))
if check_id: if check_id:
if node.parent.hasattr('id'): if node.parent.hasattr('id'):
self.body.append( self.body.append(
self.starttag({}, 'a', '', name=node.parent['id'])) self.starttag({}, 'a', '', name=node.parent['id']))
self.context.append('</a></p>\n') self.context.append('</a>' + close_tag)
else: else:
self.context.append('</p>\n') self.context.append(close_tag)
def depart_title(self, node): def depart_title(self, node):
self.body.append(self.context.pop()) 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): def visit_title_reference(self, node):
self.body.append(self.starttag(node, 'cite', '')) self.body.append(self.starttag(node, 'cite', ''))
......
""" """
:Author: Engelbert Gruber :Author: Engelbert Gruber
:Contact: grubert@users.sourceforge.net :Contact: grubert@users.sourceforge.net
:Revision: $Revision: 1.3 $ :Revision: $Revision: 1.1.2.3.8.1 $
:Date: $Date: 2003/11/30 15:06:09 $ :Date: $Date: 2004/05/12 19:57:57 $
:Copyright: This module has been placed in the public domain. :Copyright: This module has been placed in the public domain.
LaTeX2e document tree Writer. LaTeX2e document tree Writer.
...@@ -11,7 +11,7 @@ LaTeX2e document tree Writer. ...@@ -11,7 +11,7 @@ LaTeX2e document tree Writer.
__docformat__ = 'reStructuredText' __docformat__ = 'reStructuredText'
# code contributions from several people included, thanks too all. # 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. ##. # convention deactivate code by two # e.g. ##.
...@@ -76,13 +76,43 @@ class Writer(writers.Writer): ...@@ -76,13 +76,43 @@ class Writer(writers.Writer):
['--use-latex-toc'], ['--use-latex-toc'],
{'default': 0, 'action': 'store_true', {'default': 0, 'action': 'store_true',
'validator': frontend.validate_boolean}), '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'], ['--use-latex-docinfo'],
{'default': 0, 'action': 'store_true', {'default': 0, 'action': 'store_true',
'validator': frontend.validate_boolean}), 'validator': frontend.validate_boolean}),
('Color of any hyperlinks embedded in text ' ('Color of any hyperlinks embedded in text '
'(default: "blue", "0" to disable).', '(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'} settings_defaults = {'output_encoding': 'latin-1'}
...@@ -245,10 +275,38 @@ latex_headings = { ...@@ -245,10 +275,38 @@ latex_headings = {
'% some commands, that could be overwritten in the style file.\n' '% some commands, that could be overwritten in the style file.\n'
'\\newcommand{\\rubric}[1]' '\\newcommand{\\rubric}[1]'
'{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n' '{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
'\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
'% end of "some commands"\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): class LaTeXTranslator(nodes.NodeVisitor):
# When options are given to the documentclass, latex will pass them # When options are given to the documentclass, latex will pass them
...@@ -277,6 +335,16 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -277,6 +335,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
# list environment for docinfo. else tabularx # list environment for docinfo. else tabularx
use_optionlist_for_docinfo = 0 # NOT YET IN USE 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 # default link color
hyperlink_color = "blue" hyperlink_color = "blue"
...@@ -287,6 +355,11 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -287,6 +355,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.use_latex_docinfo = settings.use_latex_docinfo self.use_latex_docinfo = settings.use_latex_docinfo
self.use_latex_footnotes = settings.use_latex_footnotes self.use_latex_footnotes = settings.use_latex_footnotes
self.hyperlink_color = settings.hyperlink_color 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': if self.hyperlink_color == '0':
self.hyperlink_color = 'black' self.hyperlink_color = 'black'
self.colorlinks = 'false' self.colorlinks = 'false'
...@@ -302,6 +375,9 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -302,6 +375,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
if self.babel.get_language(): if self.babel.get_language():
self.d_options += ',%s' % \ self.d_options += ',%s' % \
self.babel.get_language() self.babel.get_language()
self.d_class = DocumentClass(settings.documentclass)
self.head_prefix = [ self.head_prefix = [
self.latex_head % (self.d_options,self.settings.documentclass), self.latex_head % (self.d_options,self.settings.documentclass),
'\\usepackage{babel}\n', # language is in documents settings. '\\usepackage{babel}\n', # language is in documents settings.
...@@ -333,7 +409,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -333,7 +409,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'\\newlength{\\docinfowidth}\n', '\\newlength{\\docinfowidth}\n',
'\\setlength{\\docinfowidth}{0.9\\textwidth}\n' '\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
# linewidth of current environment, so tables are not wider # 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. # of linewidth, this is fixing it.
'\\newlength{\\locallinewidth}\n', '\\newlength{\\locallinewidth}\n',
# will be set later. # will be set later.
...@@ -368,6 +444,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -368,6 +444,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.topic_class = '' self.topic_class = ''
# column specification for tables # column specification for tables
self.colspecs = [] self.colspecs = []
self.table_caption = None
# do we have one or more authors # do we have one or more authors
self.author_stack = None self.author_stack = None
# Flags to encode # Flags to encode
...@@ -383,11 +460,24 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -383,11 +460,24 @@ class LaTeXTranslator(nodes.NodeVisitor):
# enumeration is done by list environment. # enumeration is done by list environment.
self._enum_cnt = 0 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. # docinfo.
self.docinfo = None self.docinfo = None
# inside literal block: no quote mangling. # inside literal block: no quote mangling.
self.literal_block = 0 self.literal_block = 0
self.literal_block_stack = []
self.literal = 0 self.literal = 0
# true when encoding in math mode
self.mathmode = 0
def get_stylesheet_reference(self): def get_stylesheet_reference(self):
if self.settings.stylesheet_path: if self.settings.stylesheet_path:
...@@ -465,7 +555,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -465,7 +555,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# then dollar # then dollar
text = text.replace("$", '{\\$}') 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 # the vertical bar: in mathmode |,\vert or \mid
# in textmode \textbar # in textmode \textbar
text = text.replace("|", '{\\textbar}') text = text.replace("|", '{\\textbar}')
...@@ -492,7 +582,13 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -492,7 +582,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
# ! LaTeX Error: There's no line here to end. # ! LaTeX Error: There's no line here to end.
text = text.replace("\n", '~\\\\\n') text = text.replace("\n", '~\\\\\n')
elif self.mbox_newline: 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: if self.insert_none_breaking_blanks:
text = text.replace(' ', '~') text = text.replace(' ', '~')
# unicode !!! # unicode !!!
...@@ -528,14 +624,15 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -528,14 +624,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_address(self, node): def depart_address(self, node):
self.depart_docinfo_item(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('\\begin{center}\\begin{sffamily}\n')
self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n') self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n'); if name:
self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n');
self.body.append('\\vspace{2mm}\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('}}\n') # end parbox fbox
self.body.append('\\end{sffamily}\n\\end{center}\n'); self.body.append('\\end{sffamily}\n\\end{center}\n');
...@@ -582,6 +679,24 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -582,6 +679,24 @@ class LaTeXTranslator(nodes.NodeVisitor):
else: else:
self.body.append( '\\end{itemize}\n' ) 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): def visit_caption(self, node):
self.body.append( '\\caption{' ) self.body.append( '\\caption{' )
...@@ -601,11 +716,10 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -601,11 +716,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.depart_footnote(node) self.depart_footnote(node)
def visit_title_reference(self, node): def visit_title_reference(self, node):
# BUG title-references are what? self.body.append( '\\titlereference{' )
pass
def depart_title_reference(self, node): def depart_title_reference(self, node):
pass self.body.append( '}' )
def visit_citation_reference(self, node): def visit_citation_reference(self, node):
href = '' href = ''
...@@ -767,9 +881,11 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -767,9 +881,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_emphasis(self, node): def visit_emphasis(self, node):
self.body.append('\\emph{') self.body.append('\\emph{')
self.literal_block_stack.append('\\emph{')
def depart_emphasis(self, node): def depart_emphasis(self, node):
self.body.append('}') self.body.append('}')
self.literal_block_stack.pop()
def visit_entry(self, node): def visit_entry(self, node):
# cell separation # cell separation
...@@ -781,13 +897,15 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -781,13 +897,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
# multi{row,column} # multi{row,column}
if node.has_key('morerows') and node.has_key('morecols'): 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.') 'span multiple rows *and* columns, sorry.')
atts = {} atts = {}
if node.has_key('morerows'): if node.has_key('morerows'):
raise NotImplementedError('multiple rows are not working (yet), sorry.')
count = node['morerows'] + 1 count = node['morerows'] + 1
self.body.append('\\multirow{%d}*{' % count) self.body.append('\\multirow{%d}*{' % count)
self.context.append('}') self.context.append('}')
# BUG following rows must have empty cells.
elif node.has_key('morecols'): elif node.has_key('morecols'):
# the vertical bar before column is missing if it is the first column. # the vertical bar before column is missing if it is the first column.
# the one after always. # the one after always.
...@@ -830,13 +948,22 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -830,13 +948,22 @@ class LaTeXTranslator(nodes.NodeVisitor):
enum_prefix = "" enum_prefix = ""
if node.has_key('prefix'): if node.has_key('prefix'):
enum_prefix = node['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" enum_type = "arabic"
if node.has_key('enumtype'): if node.has_key('enumtype'):
enum_type = node['enumtype'] enum_type = node['enumtype']
if enum_style.has_key(enum_type): if enum_style.has_key(enum_type):
enum_type = enum_style[enum_type] enum_type = enum_style[enum_type]
counter_name = "listcnt%d" % self._enum_cnt; 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('\\newcounter{%s}\n' % counter_name)
self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \ self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
(enum_prefix,enum_type,counter_name,enum_suffix)) (enum_prefix,enum_type,counter_name,enum_suffix))
...@@ -852,6 +979,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -852,6 +979,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_enumerated_list(self, node): def depart_enumerated_list(self, node):
self.body.append('\\end{list}\n') self.body.append('\\end{list}\n')
self._enumeration_counters.pop()
def visit_error(self, node): def visit_error(self, node):
self.visit_admonition(node, 'error') self.visit_admonition(node, 'error')
...@@ -964,6 +1092,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -964,6 +1092,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
return return
self.body.append('}%s' % self.context.pop()) self.body.append('}%s' % self.context.pop())
# elements generated by the framework e.g. section numbers.
def visit_generated(self, node): def visit_generated(self, node):
pass pass
...@@ -987,17 +1116,15 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -987,17 +1116,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.depart_admonition() self.depart_admonition()
def visit_image(self, node): def visit_image(self, node):
attrs = node.attributes.copy() attrs = node.attributes
pre = [] # in reverse order pre = [] # in reverse order
post = ['\\includegraphics{%s}' % attrs['uri']] 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) inline = isinstance(node.parent, nodes.TextElement)
if 'scale' in attrs: if 'scale' in attrs:
# Could also be done with ``scale`` option to # Could also be done with ``scale`` option to
# ``\includegraphics``; doing it this way for consistency. # ``\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: if 'align' in attrs:
align_prepost = { align_prepost = {
# By default latex aligns the top of an image. # By default latex aligns the top of an image.
...@@ -1011,11 +1138,13 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1011,11 +1138,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
(0, 'left'): ('{', '\\hfill}'), (0, 'left'): ('{', '\\hfill}'),
(0, 'right'): ('{\\hfill', '}'),} (0, 'right'): ('{\\hfill', '}'),}
try: 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: except KeyError:
pass # complain here? pass # XXX complain here?
if not inline: if not inline:
prepost('\n', '\n') pre.append('\n')
post.append('\n')
pre.reverse() pre.reverse()
self.body.extend(pre + post) self.body.extend(pre + post)
...@@ -1054,6 +1183,8 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1054,6 +1183,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
* whitespace (including linebreaks) is significant * whitespace (including linebreaks) is significant
* inline markup is supported. * inline markup is supported.
* serif typeface * serif typeface
mbox would stop LaTeX from wrapping long lines.
""" """
self.body.append('\\begin{flushleft}\n') self.body.append('\\begin{flushleft}\n')
self.insert_none_breaking_blanks = 1 self.insert_none_breaking_blanks = 1
...@@ -1074,7 +1205,9 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1074,7 +1205,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\n\\end{flushleft}\n') self.body.append('\n\\end{flushleft}\n')
def visit_list_item(self, node): 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): def depart_list_item(self, node):
self.body.append('\n') self.body.append('\n')
...@@ -1089,31 +1222,51 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1089,31 +1222,51 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_literal_block(self, node): 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. # In both cases, we want to use a typewriter/monospaced typeface.
# care must be taken with the text, because inline markup is recognized. # For "real" literal-blocks, we can use \verbatim, while for all
# the others we must use \mbox.
# #
# possibilities: # We can distinguish between the two kinds by the number of
# * verbatim: is no possibility, as inline markup does not work. # siblings the compose this node: if it is composed by a
# * obey..: is from julien and never worked for me (grubert). # single element, it's surely is either a real one, otherwise
self.use_for_literal_block = "mbox" # it's a parsed-literal that does not contain any markup.
self.literal_block = 1 #
if (self.use_for_literal_block == "mbox"): if (self.settings.use_verbatim_when_possible and (len(node) == 1)
self.mbox_newline = 1 # in case of a parsed-literal containing just a "**bold**" word:
self.insert_none_breaking_blanks = 1 and isinstance(node[0], nodes.Text)):
self.body.append('\\begin{ttfamily}\\begin{flushleft}\n\\mbox{') self.verbatim = 1
self.body.append('\\begin{verbatim}\n')
else: else:
self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n') self.literal_block = 1
self.insert_none_breaking_blanks = 1
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): def depart_literal_block(self, node):
if (self.use_for_literal_block == "mbox"): if self.verbatim:
self.body.append('}\n\\end{flushleft}\\end{ttfamily}\n') 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.insert_none_breaking_blanks = 0
self.mbox_newline = 0 self.mbox_newline = 0
else: # obey end: self.body.append('}\n')
self.body.append('}\n') self.literal_block = 0
self.literal_block = 0
def visit_meta(self, node): def visit_meta(self, node):
self.body.append('[visit_meta]\n') self.body.append('[visit_meta]\n')
...@@ -1226,13 +1379,14 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1226,13 +1379,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# BUG: hash_char "#" is trouble some in LaTeX. # BUG: hash_char "#" is trouble some in LaTeX.
# mbox and other environment do not like the '#'. # mbox and other environment do not like the '#'.
hash_char = '\\#' hash_char = '\\#'
if node.has_key('refuri'): if node.has_key('refuri'):
href = node['refuri'] href = node['refuri'].replace('#',hash_char)
elif node.has_key('refid'): elif node.has_key('refid'):
href = hash_char + node['refid'] href = hash_char + node['refid']
elif node.has_key('refname'): elif node.has_key('refname'):
href = hash_char + self.document.nameids[node['refname']] href = hash_char + self.document.nameids[node['refname']]
else:
raise AssertionError('Unknown reference.')
self.body.append('\\href{%s}{' % href) self.body.append('\\href{%s}{' % href)
def depart_reference(self, node): def depart_reference(self, node):
...@@ -1250,11 +1404,18 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1250,11 +1404,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_row(self, node): def depart_row(self, node):
self.context.pop() # remove cell counter self.context.pop() # remove cell counter
self.body.append(' \\\\ \\hline\n') self.body.append(' \\\\ \\hline\n')
# BUG if multirow cells \cline{x-y}
def visit_section(self, node): def visit_section(self, node):
self.section_level += 1 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): def depart_section(self, node):
# Remove counter for potential subsections:
self._section_number.pop()
self.section_level -= 1 self.section_level -= 1
def visit_sidebar(self, node): def visit_sidebar(self, node):
...@@ -1292,9 +1453,11 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1292,9 +1453,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_strong(self, node): def visit_strong(self, node):
self.body.append('\\textbf{') self.body.append('\\textbf{')
self.literal_block_stack.append('\\textbf{')
def depart_strong(self, node): def depart_strong(self, node):
self.body.append('}') self.body.append('}')
self.literal_block_stack.pop()
def visit_substitution_definition(self, node): def visit_substitution_definition(self, node):
raise nodes.SkipNode raise nodes.SkipNode
...@@ -1328,13 +1491,20 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1328,13 +1491,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
Return column specification for longtable. Return column specification for longtable.
Assumes reST line length being 80 characters. Assumes reST line length being 80 characters.
Table width is hairy.
=== ===
ABC DEF
=== ===
usually gets to narrow, therefore we add 1 (fiddlefactor).
""" """
width = 80 width = 80
total_width = 0.0 total_width = 0.0
# first see if we get too wide. # first see if we get too wide.
for node in self.colspecs: for node in self.colspecs:
colwidth = float(node['colwidth']) / width colwidth = float(node['colwidth']+1) / width
total_width += colwidth total_width += colwidth
# donot make it full linewidth # donot make it full linewidth
factor = 0.93 factor = 0.93
...@@ -1343,8 +1513,8 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1343,8 +1513,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
latex_table_spec = "" latex_table_spec = ""
for node in self.colspecs: for node in self.colspecs:
colwidth = factor * float(node['colwidth']) / width colwidth = factor * float(node['colwidth']+1) / width
latex_table_spec += "|p{%.2f\\locallinewidth}" % colwidth latex_table_spec += "|p{%.2f\\locallinewidth}" % (colwidth+0.005)
self.colspecs = [] self.colspecs = []
return latex_table_spec+"|" return latex_table_spec+"|"
...@@ -1369,6 +1539,9 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1369,6 +1539,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
def table_preamble(self): def table_preamble(self):
if self.use_longtable: if self.use_longtable:
self.body.append('{%s}\n' % self.get_colspecs()) 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: else:
if self.context[-1] != 'table_sentinel': if self.context[-1] != 'table_sentinel':
self.body.append('{%s}' % ('|X' * self.context.pop() + '|')) self.body.append('{%s}' % ('|X' * self.context.pop() + '|'))
...@@ -1476,6 +1649,10 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1476,6 +1649,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
elif isinstance(node.parent, nodes.sidebar): elif isinstance(node.parent, nodes.sidebar):
self.body.append('\\textbf{\\large ') self.body.append('\\textbf{\\large ')
self.context.append('}\n\\smallskip\n') 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: elif self.section_level == 0:
# document title # document title
self.title = self.encode(node.astext()) self.title = self.encode(node.astext())
...@@ -1487,21 +1664,15 @@ class LaTeXTranslator(nodes.NodeVisitor): ...@@ -1487,21 +1664,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('%' + '_' * 75) self.body.append('%' + '_' * 75)
self.body.append('\n\n') self.body.append('\n\n')
self.bookmark(node) 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: if self.use_latex_toc:
section_star = "" section_star = ""
else: else:
section_star = "*" section_star = "*"
if (self.section_level<=3): # 1,2,3
self.body.append('\\%ssection%s{' % ('sub'*(self.section_level-1),section_star)) section_name = self.d_class.section(self.section_level)
elif (self.section_level==4): self.body.append('\\%s%s{' % (section_name, section_star))
#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)
self.context.append('}\n') self.context.append('}\n')
def depart_title(self, node): def depart_title(self, node):
......
# Author: David Goodger # Author: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
...@@ -88,15 +88,6 @@ class Writer(html4css1.Writer): ...@@ -88,15 +88,6 @@ class Writer(html4css1.Writer):
class HTMLTranslator(html4css1.HTMLTranslator): 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): def depart_field_list(self, node):
html4css1.HTMLTranslator.depart_field_list(self, node) html4css1.HTMLTranslator.depart_field_list(self, node)
if node.get('class') == 'rfc2822': if node.get('class') == 'rfc2822':
......
# Authors: David Goodger # Authors: David Goodger
# Contact: goodger@users.sourceforge.net # Contact: goodger@users.sourceforge.net
# Revision: $Revision: 1.5 $ # Revision: $Revision: 1.2.10.3.8.1 $
# Date: $Date: 2003/11/30 15:06:09 $ # Date: $Date: 2004/05/12 19:57:57 $
# Copyright: This module has been placed in the public domain. # Copyright: This module has been placed in the public domain.
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment