Author: felixwiemann Date: 2005-10-11 21:34:52 +0200 (Tue, 11 Oct 2005) New Revision: 3935 Modified: trunk/docutils/BUGS.txt trunk/docutils/HISTORY.txt trunk/docutils/docutils/nodes.py trunk/docutils/docutils/transforms/references.py trunk/docutils/test/test_nodes.py trunk/docutils/test/test_transforms/test_substitutions.py Log: added Node.deepcopy(); fixed bug with doubly-indirect substitutions Modified: trunk/docutils/BUGS.txt =================================================================== --- trunk/docutils/BUGS.txt 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/BUGS.txt 2005-10-11 19:34:52 UTC (rev 3935) @@ -151,14 +151,6 @@ "Active" content inside of substitution definitions does not work. This bug becomes manifest in the following cases: - * There's a bug with _`doubly-indirect substitutions` but only when - there's multiple references and in certain cases. See the - commented-out test case in test_transforms/test_substitutions.py - (line 104 on, in revision 3808) which fails. - - This is tricky. Substitution definitions have to propagate back - completely, through multiple levels of references. - * .. _substitutions and references: Another bug from David Abrahams (run with ``rst2html.py --traceback``):: Modified: trunk/docutils/HISTORY.txt =================================================================== --- trunk/docutils/HISTORY.txt 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/HISTORY.txt 2005-10-11 19:34:52 UTC (rev 3935) @@ -44,6 +44,7 @@ elements of a list, for serialization. Modified Docutils-XML writing (``Element._dom_node``) and pseudo-XML writing (``Element.starttag``) to use ``serial_escape``. + - Added ``Node.deepcopy()`` method. * docutils/parsers/null.py: Added to project; a do-nothing parser. @@ -93,6 +94,7 @@ - Added references.DanglingReferences transform, extracted from universal.FinalChecks. + - Fixed bug with doubly-indirect substitutions. * docutils/transforms/universal.py: Modified: trunk/docutils/docutils/nodes.py =================================================================== --- trunk/docutils/docutils/nodes.py 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/docutils/nodes.py 2005-10-11 19:34:52 UTC (rev 3935) @@ -82,6 +82,10 @@ """Return a copy of self.""" raise NotImplementedError + def deepcopy(self): + """Return a deep copy of self (also copying children).""" + raise NotImplementedError + def setup_child(self, child): child.parent = self if self.document: @@ -288,6 +292,9 @@ def copy(self): return self.__class__(self.data) + def deepcopy(self): + return self.copy() + def pformat(self, indent=' ', level=0): result = [] indent = indent * level @@ -639,6 +646,11 @@ def copy(self): return self.__class__(**self.attributes) + def deepcopy(self): + copy = self.copy() + copy.extend([child.deepcopy() for child in self.children]) + return copy + def set_class(self, name): """Add a new class to the "classes" attribute.""" warnings.warn('docutils.nodes.Element.set_class deprecated; ' @@ -816,10 +828,6 @@ self.ids = {} """Mapping of ids to nodes.""" - self.substitution_refs = {} - """Mapping of substitution names to lists of substitution_reference - nodes.""" - self.footnote_refs = {} """Mapping of footnote labels to lists of footnote_reference nodes.""" @@ -1063,8 +1071,7 @@ self.substitution_names[fully_normalize_name(name)] = name def note_substitution_ref(self, subref, refname): - name = subref['refname'] = whitespace_normalize_name(refname) - self.substitution_refs.setdefault(name, []).append(subref) + subref['refname'] = whitespace_normalize_name(refname) def note_pending(self, pending, priority=None): self.transformer.add_pending(pending, priority) Modified: trunk/docutils/docutils/transforms/references.py =================================================================== --- trunk/docutils/docutils/transforms/references.py 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/docutils/transforms/references.py 2005-10-11 19:34:52 UTC (rev 3935) @@ -654,45 +654,46 @@ def apply(self): defs = self.document.substitution_defs normed = self.document.substitution_names - subreflist = self.document.substitution_refs.items() - subreflist.sort() - for refname, refs in subreflist: - for ref in refs: - key = None - if defs.has_key(refname): - key = refname - else: - normed_name = refname.lower() - if normed.has_key(normed_name): - key = normed[normed_name] - if key is None: - msg = self.document.reporter.error( - 'Undefined substitution referenced: "%s".' - % refname, base_node=ref) - msgid = self.document.set_id(msg) - prb = nodes.problematic( - ref.rawsource, ref.rawsource, refid=msgid) - prbid = self.document.set_id(prb) - msg.add_backref(prbid) - ref.replace_self(prb) - else: - subdef = defs[key] - parent = ref.parent - index = parent.index(ref) - if (subdef.attributes.has_key('ltrim') - or subdef.attributes.has_key('trim')): - if index > 0 and isinstance(parent[index - 1], - nodes.Text): - parent.replace(parent[index - 1], - parent[index - 1].rstrip()) - if (subdef.attributes.has_key('rtrim') - or subdef.attributes.has_key('trim')): - if (len(parent) > index + 1 - and isinstance(parent[index + 1], nodes.Text)): - parent.replace(parent[index + 1], - parent[index + 1].lstrip()) - ref.replace_self(subdef.children) - self.document.substitution_refs = None # release replaced references + subreflist = self.document.traverse(nodes.substitution_reference) + for ref in subreflist: + refname = ref['refname'] + key = None + if defs.has_key(refname): + key = refname + else: + normed_name = refname.lower() + if normed.has_key(normed_name): + key = normed[normed_name] + if key is None: + msg = self.document.reporter.error( + 'Undefined substitution referenced: "%s".' + % refname, base_node=ref) + msgid = self.document.set_id(msg) + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + else: + subdef = defs[key] + parent = ref.parent + index = parent.index(ref) + if (subdef.attributes.has_key('ltrim') + or subdef.attributes.has_key('trim')): + if index > 0 and isinstance(parent[index - 1], + nodes.Text): + parent.replace(parent[index - 1], + parent[index - 1].rstrip()) + if (subdef.attributes.has_key('rtrim') + or subdef.attributes.has_key('trim')): + if (len(parent) > index + 1 + and isinstance(parent[index + 1], nodes.Text)): + parent.replace(parent[index + 1], + parent[index + 1].lstrip()) + subdef_copy = subdef.deepcopy() + # Take care of nested substitution references. + subreflist.extend(subdef_copy.traverse(nodes.substitution_reference)) + ref.replace_self(subdef_copy.children) class TargetNotes(Transform): Modified: trunk/docutils/test/test_nodes.py =================================================================== --- trunk/docutils/test/test_nodes.py 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/test/test_nodes.py 2005-10-11 19:34:52 UTC (rev 3935) @@ -242,7 +242,29 @@ def not_in_testlist(self, x): return x not in self.testlist + def test_copy(self): + grandchild = nodes.Text('rawsource') + child = nodes.emphasis('rawsource', grandchild, att='child') + e = nodes.Element('rawsource', child, att='e') + # Shallow copy: + e_copy = e.copy() + self.assert_(e is not e_copy) + # Internal attributes (like `rawsource`) are not copied. + self.assertEquals(e.rawsource, 'rawsource') + self.assertEquals(e_copy.rawsource, '') + self.assertEquals(e_copy['att'], 'e') + # Children are not copied. + self.assertEquals(len(e_copy), 0) + # Deep copy: + e_deepcopy = e.deepcopy() + self.assertEquals(e_deepcopy.rawsource, '') + self.assertEquals(e_deepcopy['att'], 'e') + # Children are copied recursively. + self.assertEquals(e_deepcopy[0][0], grandchild) + self.assert_(e_deepcopy[0][0] is not grandchild) + self.assertEquals(e_deepcopy[0]['att'], 'child') + class TreeCopyVisitorTests(unittest.TestCase): def setUp(self): Modified: trunk/docutils/test/test_transforms/test_substitutions.py =================================================================== --- trunk/docutils/test/test_transforms/test_substitutions.py 2005-10-11 19:26:35 UTC (rev 3934) +++ trunk/docutils/test/test_transforms/test_substitutions.py 2005-10-11 19:34:52 UTC (rev 3935) @@ -101,51 +101,49 @@ <substitution_definition names="replace"> swap """], -#["""\ -#.. |l| unicode:: U+00AB .. left chevron -#.. |r| unicode:: U+00BB .. right chevron -#.. |.| replace:: |l|\ ``.``\ |r| -# -#.. Delete either of the following lines, and there is no error. -# -#Regular expression |.| will match any character -# -#.. Note:: Note that |.| matches *exactly* one character -#""", -#u"""\ -#<document source="test data"> -# <substitution_definition names="l"> -# \xab -# <substitution_definition names="r"> -# \xbb -# <substitution_definition names="."> -# <substitution_reference refname="l"> -# l -# <literal> -# . -# <substitution_reference refname="r"> -# r -# <comment xml:space="preserve"> -# Delete either of the following lines, and there is no error. -# <paragraph> -# Regular expression \n\ -# \xab -# <literal> -# . -# \xbb -# will match any character -# <note> -# <paragraph> -# Note that \n\ -# \xab -# <literal> -# . -# \xbb -# matches \n\ -# <emphasis> -# exactly -# one character -#"""], +["""\ +.. |l| unicode:: U+00AB .. left chevron +.. |r| unicode:: U+00BB .. right chevron +.. |.| replace:: |l|\ ``.``\ |r| + +.. Delete either of the following lines, and there is no error. + +Regular expression |.| will match any character + +.. Note:: Note that |.| matches *exactly* one character +""", +u"""\ +<document source="test data"> + <substitution_definition names="l"> + \xab + <substitution_definition names="r"> + \xbb + <substitution_definition names="."> + \xab + <literal> + . + \xbb + <comment xml:space="preserve"> + Delete either of the following lines, and there is no error. + <paragraph> + Regular expression \n\ + \xab + <literal> + . + \xbb + will match any character + <note> + <paragraph> + Note that \n\ + \xab + <literal> + . + \xbb + matches \n\ + <emphasis> + exactly + one character +"""], ]) totest['unicode'] = ((Substitutions,), [ |