diff options
author | goodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2002-10-24 00:51:10 +0000 |
---|---|---|
committer | goodger <goodger@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2002-10-24 00:51:10 +0000 |
commit | 1ecb8a1bf993d41cb606a26ca5168809bd75412b (patch) | |
tree | b4681cf3d3775ea0a0aa9cbc9cf4e3f5570bd001 | |
parent | cb678b87cdec6c2fc503cccf524b48bc80950edd (diff) | |
download | docutils-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__.py | 107 | ||||
-rw-r--r-- | docutils/transforms/components.py | 26 | ||||
-rw-r--r-- | docutils/transforms/frontmatter.py | 4 | ||||
-rw-r--r-- | docutils/transforms/parts.py | 20 | ||||
-rw-r--r-- | docutils/transforms/peps.py | 17 | ||||
-rw-r--r-- | test/DocutilsTestSupport.py | 11 |
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) |