Browse Source

Fix Wikicode transformation methods on empty sections (fixes #212)

tags/v0.5.3
Ben Kurtovic 5 years ago
parent
commit
840a88bcd6
4 changed files with 63 additions and 18 deletions
  1. +2
    -1
      CHANGELOG
  2. +3
    -1
      docs/changelog.rst
  3. +25
    -6
      mwparserfromhell/wikicode.py
  4. +33
    -10
      tests/test_wikicode.py

+ 2
- 1
CHANGELOG View File

@@ -1,6 +1,7 @@
v0.6 (unreleased):

- ...
- Fixed Wikicode transformation methods (replace(), remove(), etc.) when passed
an empty section as an argument. (#212)

v0.5.2 (released November 1, 2018):



+ 3
- 1
docs/changelog.rst View File

@@ -7,7 +7,9 @@ v0.6
Unreleased
(`changes <https://github.com/earwig/mwparserfromhell/compare/v0.5.2...develop>`__):

- ...
- Fixed :class:`.Wikicode` transformation methods (:meth:`.Wikicode.replace`,
:meth:`.Wikicode.remove`, etc.) when passed an empty section as an argument.
(`#212 <https://github.com/earwig/mwparserfromhell/issues/212>`_)

v0.5.2
------


+ 25
- 6
mwparserfromhell/wikicode.py View File

@@ -27,6 +27,7 @@ import re
from .compat import bytes, py3k, range, str
from .nodes import (Argument, Comment, ExternalLink, Heading, HTMLEntity,
Node, Tag, Template, Text, Wikilink)
from .smart_list import _ListProxy
from .string_mixin import StringMixIn
from .utils import parse_anything

@@ -108,6 +109,23 @@ class Wikicode(StringMixIn):
if (not forcetype or isinstance(node, forcetype)) and match(node):
yield (i, node)

def _is_child_wikicode(self, obj, recursive=True):
"""Return whether the given :class:`.Wikicode` is a descendant."""
nodes = obj.nodes
if isinstance(nodes, _ListProxy):
nodes = nodes._parent # pylint: disable=protected-access
if nodes is self.nodes:
return True
if recursive:
todo = [self]
while todo:
code = todo.pop()
if nodes is code.nodes:
return True
for node in code.nodes:
todo += list(node.__children__())
return False

def _do_strong_search(self, obj, recursive=True):
"""Search for the specific element *obj* within the node list.

@@ -120,11 +138,16 @@ class Wikicode(StringMixIn):
:class:`.Wikicode` contained by a node within ``self``. If *obj* is not
found, :exc:`ValueError` is raised.
"""
if isinstance(obj, Wikicode):
if not self._is_child_wikicode(obj, recursive):
raise ValueError(obj)
return obj, slice(0, len(obj.nodes))

if isinstance(obj, Node):
mkslice = lambda i: slice(i, i + 1)
if not recursive:
return self, mkslice(self.index(obj))
for i, node in enumerate(self.nodes):
for node in self.nodes:
for context, child in self._get_children(node, contexts=True):
if obj is child:
if not context:
@@ -132,11 +155,7 @@ class Wikicode(StringMixIn):
return context, mkslice(context.index(child))
raise ValueError(obj)

context, ind = self._do_strong_search(obj.get(0), recursive)
for i in range(1, len(obj.nodes)):
if obj.get(i) is not context.get(ind.start + i):
raise ValueError(obj)
return context, slice(ind.start, ind.start + len(obj.nodes))
raise TypeError(obj)

def _do_weak_search(self, obj, recursive):
"""Search for an element that looks like *obj* within the node list.


+ 33
- 10
tests/test_wikicode.py View File

@@ -188,8 +188,8 @@ class TestWikicode(TreeEqualityTestCase):
self.assertRaises(ValueError, func, fake, "q", recursive=True)
func("{{b}}{{c}}", "w", recursive=False)
func("{{d}}{{e}}", "x", recursive=True)
func(wrap(code4.nodes[-2:]), "y", recursive=False)
func(wrap(code4.nodes[-2:]), "z", recursive=True)
func(Wikicode(code4.nodes[-2:]), "y", recursive=False)
func(Wikicode(code4.nodes[-2:]), "z", recursive=True)
self.assertEqual(expected[3], code4)
self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=False)
self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=True)
@@ -218,6 +218,13 @@ class TestWikicode(TreeEqualityTestCase):
func("{{foo}}{{baz}}", "{{lol}}")
self.assertEqual(expected[6], code7)

code8 = parse("== header ==")
func = partial(meth, code8)
sec1, sec2 = code8.get_sections(include_headings=False)
func(sec1, "lead\n")
func(sec2, "\nbody")
self.assertEqual(expected[7], code8)

def test_insert_before(self):
"""test Wikicode.insert_before()"""
meth = lambda code, *args, **kw: code.insert_before(*args, **kw)
@@ -228,7 +235,9 @@ class TestWikicode(TreeEqualityTestCase):
"{{a}}w{{b}}{{c}}x{{d}}{{e}}{{f}}{{g}}{{h}}yz{{i}}{{j}}",
"{{a|x{{b}}{{c}}|{{f|{{g}}=y{{h}}{{i}}}}}}",
"here cdis {{some abtext and a {{template}}}}",
"{{foo}}{{bar}}{{baz}}{{lol}}{{foo}}{{baz}}"]
"{{foo}}{{bar}}{{baz}}{{lol}}{{foo}}{{baz}}",
"lead\n== header ==\nbody",
]
self._test_search(meth, expected)

def test_insert_after(self):
@@ -241,16 +250,24 @@ class TestWikicode(TreeEqualityTestCase):
"{{a}}{{b}}{{c}}w{{d}}{{e}}x{{f}}{{g}}{{h}}{{i}}{{j}}yz",
"{{a|{{b}}{{c}}x|{{f|{{g}}={{h}}{{i}}y}}}}",
"here is {{somecd text andab a {{template}}}}",
"{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}{{lol}}"]
"{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}{{lol}}",
"lead\n== header ==\nbody",
]
self._test_search(meth, expected)

def test_replace(self):
"""test Wikicode.replace()"""
meth = lambda code, *args, **kw: code.replace(*args, **kw)
expected = [
"{{a}}xz[[y]]{{e}}", "dcdffe", "{{a|x|{{c|d=y}}}}",
"{{a}}wx{{f}}{{g}}z", "{{a|x|{{f|{{g}}=y}}}}",
"here cd ab a {{template}}}}", "{{foo}}{{bar}}{{baz}}{{lol}}"]
"{{a}}xz[[y]]{{e}}",
"dcdffe",
"{{a|x|{{c|d=y}}}}",
"{{a}}wx{{f}}{{g}}z",
"{{a|x|{{f|{{g}}=y}}}}",
"here cd ab a {{template}}}}",
"{{foo}}{{bar}}{{baz}}{{lol}}",
"lead\n== header ==\nbody",
]
self._test_search(meth, expected)

def test_append(self):
@@ -269,9 +286,15 @@ class TestWikicode(TreeEqualityTestCase):
"""test Wikicode.remove()"""
meth = lambda code, obj, value, **kw: code.remove(obj, **kw)
expected = [
"{{a}}{{c}}", "", "{{a||{{c|d=}}}}", "{{a}}{{f}}",
"{{a||{{f|{{g}}=}}}}", "here a {{template}}}}",
"{{foo}}{{bar}}{{baz}}"]
"{{a}}{{c}}",
"",
"{{a||{{c|d=}}}}",
"{{a}}{{f}}",
"{{a||{{f|{{g}}=}}}}",
"here a {{template}}}}",
"{{foo}}{{bar}}{{baz}}",
"== header ==",
]
self._test_search(meth, expected)

def test_matches(self):


Loading…
Cancel
Save