summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgoodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2002-10-24 00:51:10 +0000
committergoodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2002-10-24 00:51:10 +0000
commit1ecb8a1bf993d41cb606a26ca5168809bd75412b (patch)
treeb4681cf3d3775ea0a0aa9cbc9cf4e3f5570bd001
parentcb678b87cdec6c2fc503cccf524b48bc80950edd (diff)
downloaddocutils-1ecb8a1bf993d41cb606a26ca5168809bd75412b.tar.gz
Completed transform reform; updated.
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk/docutils@853 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
-rw-r--r--docutils/transforms/__init__.py107
-rw-r--r--docutils/transforms/components.py26
-rw-r--r--docutils/transforms/frontmatter.py4
-rw-r--r--docutils/transforms/parts.py20
-rw-r--r--docutils/transforms/peps.py17
-rw-r--r--test/DocutilsTestSupport.py11
6 files changed, 119 insertions, 66 deletions
diff --git a/docutils/transforms/__init__.py b/docutils/transforms/__init__.py
index 34aa7911d..24cc4771f 100644
--- a/docutils/transforms/__init__.py
+++ b/docutils/transforms/__init__.py
@@ -40,7 +40,10 @@ class Transform:
Docutils transform component abstract base class.
"""
- def __init__(self, document, component, startnode=None):
+ default_priority = None
+ """Numerical priority of this transform, 0 through 999 (override)."""
+
+ def __init__(self, document, startnode=None):
"""
Initial setup for in-place document transforms.
"""
@@ -48,11 +51,6 @@ class Transform:
self.document = document
"""The document tree to transform."""
- self.component = component
- """The Docutils component running this transform. Transforms can
- query their controlling components with calls to
- `docutils.Component.supports()`."""
-
self.startnode = startnode
"""Node from which to begin the transform. For many transforms which
apply to the document as a whole, `startnode` is not set (i.e. its
@@ -70,7 +68,8 @@ class Transform:
class Transformer(TransformSpec):
"""
- The Transformer class records and applies transforms to documents.
+ Stores transforms (`Transform` classes) and applies them to document
+ trees. Also keeps track of components by component type name.
"""
from docutils.transforms import universal
@@ -78,54 +77,90 @@ class Transformer(TransformSpec):
default_transforms = (universal.Decorations,
universal.FinalChecks,
universal.Messages)
+ """These transforms are applied to all document trees."""
def __init__(self, document):
self.transforms = []
- """Queue of transforms to apply."""
-
- self.applying = None
- """Boolean: am I now applying tranforms?"""
+ """List of transforms to apply. Each item is a 3-tuple:
+ ``(priority string, transform class, pending node or None)``."""
self.document = document
"""The `nodes.document` object this Transformer is attached to."""
- def add_transform(self, transform_class, priority=None, component=None):
+ self.applied = []
+ """Transforms already applied, in order."""
+
+ self.sorted = 0
+ """Boolean: is `self.tranforms` sorted?"""
+
+ self.components = {}
+ """Mapping of component type name to component object. Set by
+ `self.populate_from_components()`."""
+
+ self.serialno = 0
+ """Internal serial number to keep track of the add order of
+ transforms."""
+
+ def add_transform(self, transform_class, priority=None):
+ """
+ Store a single transform. Use `priority` to override the default.
+ """
if priority is None:
- priority = transform_class.priority
- self.transforms.append((priority, transform_class, None, component))
- if self.applying:
- self.transforms.sort()
+ priority = transform_class.default_priority
+ priority_string = self.get_priority_string(priority)
+ self.transforms.append((priority_string, transform_class, None))
+ self.sorted = 0
- def add_transforms(self, transform_list, component=None):
+ def add_transforms(self, transform_list):
+ """Store multiple transforms, with default priorities."""
for transform_class in transform_list:
- self.transforms.append(
- (transform_class.priority, transform_class, None, component))
- if self.applying:
- self.transforms.sort()
+ priority_string = self.get_priority_string(
+ transform_class.default_priority)
+ self.transforms.append((priority_string, transform_class, None))
+ self.sorted = 0
- def add_pending(self, pending, priority=None, component=None):
+ def add_pending(self, pending, priority=None):
+ """Store a transform with an associated `pending` node."""
transform_class = pending.transform
if priority is None:
- priority = transform_class.priority
- self.transforms.append(
- (priority, transform_class, pending, component))
- if self.applying:
- self.transforms.sort()
+ priority = transform_class.default_priority
+ priority_string = self.get_priority_string(priority)
+ self.transforms.append((priority_string, transform_class, pending))
+ self.sorted = 0
+
+ def get_priority_string(self, priority):
+ """
+ Return a string, `priority` combined with `self.serialno`.
+
+ This ensures FIFO order on transforms with identical priority.
+ """
+ self.serialno += 1
+ return '%03d-%03d' % (priority, self.serialno)
def populate_from_components(self, components):
+ """
+ Store each component's default transforms, with default priorities.
+ Also, store components by type name in a mapping for later lookup.
+ """
+ self.add_transforms(self.default_transforms)
for component in components:
if component is None:
continue
- self.add_transforms(component.default_transforms,
- component=component)
+ self.add_transforms(component.default_transforms)
+ self.components[component.component_type] = component
+ self.sorted = 0
def apply_transforms(self):
- self.applying = 1
- self.transforms.sort()
+ """Apply all of the stored transforms, in priority order."""
+ self.document.reporter.attach_observer(
+ self.document.note_transform_message)
while self.transforms:
- priority, transform_class, pending, component \
- = self.transforms.pop(0)
- transform = transform_class(self.document, component,
- startnode=pending)
+ if not self.sorted:
+ # Unsorted initially, and whenever a transform is added.
+ self.transforms.sort()
+ self.transforms.reverse()
+ self.sorted = 1
+ priority, transform_class, pending = self.transforms.pop()
+ transform = transform_class(self.document, startnode=pending)
transform.apply()
- self.applying = None
+ self.applied.append((priority, transform_class, pending))
diff --git a/docutils/transforms/components.py b/docutils/transforms/components.py
index 7bb4e959d..8f4a267e1 100644
--- a/docutils/transforms/components.py
+++ b/docutils/transforms/components.py
@@ -25,26 +25,30 @@ class Filter(Transform):
Include or exclude elements which depend on a specific Docutils component.
For use with `nodes.pending` elements. A "pending" element's dictionary
- attribute ``details`` must contain a key matching the dependency component
- type (e.g. ``details['writer']`` for a "pending" element whose ``stage``
- attribute is 'last writer'). The value is the name of a specific format
- or context of that component (e.g. ``details['writer'] = 'html'``). If
- the Docutils component which called this transform supports that format or
- context, the "pending" element is replaced by the contents of
+ attribute ``details`` must contain the keys "component" and "format". The
+ value of ``details['component']`` must match the type name of the
+ component the elements depend on (e.g. "writer"). The value of
+ ``details['format']`` is the name of a specific format or context of that
+ component (e.g. "html"). If the matching Docutils component supports that
+ format or context, the "pending" element is replaced by the contents of
``details['nodes']`` (a list of nodes); otherwise, the "pending" element
is removed.
For example, the reStructuredText "meta" directive creates a "pending"
element containing a "meta" element (in ``pending.details['nodes']``).
- Only writers supporting the "html" format will include the "meta" element;
- it will be deleted from the output of all other writers.
+ Only writers (``pending.details['component'] == 'writer'``) supporting the
+ "html" format (``pending.details['format'] == 'html'``) will include the
+ "meta" element; it will be deleted from the output of all other writers.
"""
+ default_priority = 780
+
def apply(self):
pending = self.startnode
- component_type = pending.stage.split()[-1] # 'reader' or 'writer'
- component_name = pending.details[component_type]
- if self.component.supports(component_name):
+ component_type = pending.details['component'] # 'reader' or 'writer'
+ format = pending.details['format']
+ component = self.document.transformer.components[component_type]
+ if component.supports(format):
pending.parent.replace(pending, pending.details['nodes'])
else:
pending.parent.remove(pending)
diff --git a/docutils/transforms/frontmatter.py b/docutils/transforms/frontmatter.py
index f8d4869c5..86cae9854 100644
--- a/docutils/transforms/frontmatter.py
+++ b/docutils/transforms/frontmatter.py
@@ -102,6 +102,8 @@ class DocTitle(Transform):
after the title(s).
"""
+ default_priority = 320
+
def apply(self):
if self.promote_document_title():
self.promote_document_subtitle()
@@ -225,6 +227,8 @@ class DocInfo(Transform):
expansion text changes only if the file name changes.)
"""
+ default_priority = 340
+
def apply(self):
document = self.document
index = document.first_child_not_matching_class(
diff --git a/docutils/transforms/parts.py b/docutils/transforms/parts.py
index 8bf0d2946..05747183e 100644
--- a/docutils/transforms/parts.py
+++ b/docutils/transforms/parts.py
@@ -6,9 +6,6 @@
"""
Transforms related to document parts.
-
-- `Contents`: Used to build a table of contents.
-- `SectNum`: Used to automatically number the section titles.
"""
__docformat__ = 'reStructuredText'
@@ -23,14 +20,17 @@ from docutils.transforms import TransformError, Transform
class SectNum(Transform):
"""
- This transform automatically assigns numbers to the titles of
- document sections. It is possible to limit the maximum section
- level for which the numbers are added. For those sections that
- are auto-numbered, the "autonum" attribute is set, informing the
- contents table generator that a different form of the TOC should
- be used.
+ Automatically assigns numbers to the titles of document sections.
+
+ It is possible to limit the maximum section level for which the numbers
+ are added. For those sections that are auto-numbered, the "autonum"
+ attribute is set, informing the contents table generator that a different
+ form of the TOC should be used.
"""
+ default_priority = 710
+ """Should be applied before `Contents`."""
+
def apply(self):
self.maxdepth = self.startnode.details.get('depth', sys.maxint)
self.startnode.parent.remove(self.startnode)
@@ -69,6 +69,8 @@ class Contents(Transform):
startnode is replaced by the table of contents "topic").
"""
+ default_priority = 720
+
def apply(self):
topic = nodes.topic(CLASS='contents')
title = self.startnode.details['title']
diff --git a/docutils/transforms/peps.py b/docutils/transforms/peps.py
index 91d59bf72..db1cdf47f 100644
--- a/docutils/transforms/peps.py
+++ b/docutils/transforms/peps.py
@@ -31,6 +31,8 @@ class Headers(Transform):
Process fields in a PEP's initial RFC-2822 header.
"""
+ default_priority = 360
+
pep_url = 'pep-%04d.html'
pep_cvs_url = ('http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/python/'
'python/nondist/peps/pep-%04d.txt')
@@ -74,7 +76,7 @@ class Headers(Transform):
'header.')
if pep == 0:
# Special processing for PEP 0.
- pending = nodes.pending(PEPZero, 'last reader', {})
+ pending = nodes.pending(PEPZero)
self.document.insert(1, pending)
self.document.note_pending(pending)
for field in header:
@@ -136,9 +138,10 @@ class Contents(Transform):
the RFC 2822 header.
"""
+ default_priority = 380
+
def apply(self):
- pending = nodes.pending(parts.Contents, 'first writer',
- {'title': None})
+ pending = nodes.pending(parts.Contents, {'title': None})
self.document.insert(1, pending)
self.document.note_pending(pending)
@@ -150,6 +153,8 @@ class TargetNotes(Transform):
target footnote insertion transform at the end, and run the transform.
"""
+ default_priority = 520
+
def apply(self):
doc = self.document
i = len(doc) - 1
@@ -168,9 +173,9 @@ class TargetNotes(Transform):
doc.insert(copyright, refsect)
else:
doc.append(refsect)
- pending = nodes.pending(references.TargetNotes, 'immediate', {})
+ pending = nodes.pending(references.TargetNotes)
refsect.append(pending)
- pending.transform(doc, self, pending).apply()
+ self.document.note_pending(pending, 0)
class PEPZero(Transform):
@@ -179,6 +184,8 @@ class PEPZero(Transform):
Special processing for PEP 0.
"""
+ default_priority =760
+
def apply(self):
visitor = PEPZeroSpecial(self.document)
self.document.walk(visitor)
diff --git a/test/DocutilsTestSupport.py b/test/DocutilsTestSupport.py
index 7d7252184..822a45946 100644
--- a/test/DocutilsTestSupport.py
+++ b/test/DocutilsTestSupport.py
@@ -273,12 +273,13 @@ class TransformTestCase(CustomTestCase):
if self.run_in_debugger:
pdb.set_trace()
document = utils.new_document('test data', self.settings)
- document.reporter.attach_observer(document.note_parse_message)
self.parser.parse(self.input, document)
- document.reporter.detach_observer(document.note_parse_message)
- document.reporter.attach_observer(document.note_transform_message)
- for transformClass in (self.transforms + universal.test_transforms):
- transformClass(document, self).apply()
+ # Don't do a ``populate_from_components()`` because that would
+ # enable the Transformer's default transforms.
+ document.transformer.add_transforms(self.transforms)
+ document.transformer.add_transform(universal.TestMessages)
+ document.transformer.components['writer'] = self
+ document.transformer.apply_transforms()
output = document.pformat()
self.compare_output(self.input, output, self.expected)