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): 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): v0.5.2 (released November 1, 2018):




+ 3
- 1
docs/changelog.rst View File

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


+ 25
- 6
mwparserfromhell/wikicode.py View File

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


@@ -108,6 +109,23 @@ class Wikicode(StringMixIn):
if (not forcetype or isinstance(node, forcetype)) and match(node): if (not forcetype or isinstance(node, forcetype)) and match(node):
yield (i, 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): def _do_strong_search(self, obj, recursive=True):
"""Search for the specific element *obj* within the node list. """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 :class:`.Wikicode` contained by a node within ``self``. If *obj* is not
found, :exc:`ValueError` is raised. 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): if isinstance(obj, Node):
mkslice = lambda i: slice(i, i + 1) mkslice = lambda i: slice(i, i + 1)
if not recursive: if not recursive:
return self, mkslice(self.index(obj)) 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): for context, child in self._get_children(node, contexts=True):
if obj is child: if obj is child:
if not context: if not context:
@@ -132,11 +155,7 @@ class Wikicode(StringMixIn):
return context, mkslice(context.index(child)) return context, mkslice(context.index(child))
raise ValueError(obj) 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): def _do_weak_search(self, obj, recursive):
"""Search for an element that looks like *obj* within the node list. """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) self.assertRaises(ValueError, func, fake, "q", recursive=True)
func("{{b}}{{c}}", "w", recursive=False) func("{{b}}{{c}}", "w", recursive=False)
func("{{d}}{{e}}", "x", recursive=True) 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.assertEqual(expected[3], code4)
self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=False) self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=False)
self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=True) self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=True)
@@ -218,6 +218,13 @@ class TestWikicode(TreeEqualityTestCase):
func("{{foo}}{{baz}}", "{{lol}}") func("{{foo}}{{baz}}", "{{lol}}")
self.assertEqual(expected[6], code7) 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): def test_insert_before(self):
"""test Wikicode.insert_before()""" """test Wikicode.insert_before()"""
meth = lambda code, *args, **kw: code.insert_before(*args, **kw) 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}}w{{b}}{{c}}x{{d}}{{e}}{{f}}{{g}}{{h}}yz{{i}}{{j}}",
"{{a|x{{b}}{{c}}|{{f|{{g}}=y{{h}}{{i}}}}}}", "{{a|x{{b}}{{c}}|{{f|{{g}}=y{{h}}{{i}}}}}}",
"here cdis {{some abtext and a {{template}}}}", "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) self._test_search(meth, expected)


def test_insert_after(self): 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}}w{{d}}{{e}}x{{f}}{{g}}{{h}}{{i}}{{j}}yz",
"{{a|{{b}}{{c}}x|{{f|{{g}}={{h}}{{i}}y}}}}", "{{a|{{b}}{{c}}x|{{f|{{g}}={{h}}{{i}}y}}}}",
"here is {{somecd text andab a {{template}}}}", "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) self._test_search(meth, expected)


def test_replace(self): def test_replace(self):
"""test Wikicode.replace()""" """test Wikicode.replace()"""
meth = lambda code, *args, **kw: code.replace(*args, **kw) meth = lambda code, *args, **kw: code.replace(*args, **kw)
expected = [ 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) self._test_search(meth, expected)


def test_append(self): def test_append(self):
@@ -269,9 +286,15 @@ class TestWikicode(TreeEqualityTestCase):
"""test Wikicode.remove()""" """test Wikicode.remove()"""
meth = lambda code, obj, value, **kw: code.remove(obj, **kw) meth = lambda code, obj, value, **kw: code.remove(obj, **kw)
expected = [ 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) self._test_search(meth, expected)


def test_matches(self): def test_matches(self):


Loading…
Cancel
Save