From 02e52185f4afafa701fe5bc8daa3d9af64605a1d Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Wed, 14 Aug 2013 15:39:23 -0400 Subject: [PATCH] Wikicode's filter() methods now passed recursive=True instead of False. This is a breaking change for those who rely on default behavior. --- CHANGELOG | 5 ++++- README.rst | 22 +++++++++++----------- docs/changelog.rst | 5 ++++- docs/usage.rst | 22 +++++++++++----------- mwparserfromhell/wikicode.py | 4 ++-- tests/test_docs.py | 26 +++++++++++++------------- tests/test_wikicode.py | 18 +++++++++--------- 7 files changed, 54 insertions(+), 48 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index aa8d0b5..32a14e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,10 @@ v0.3 (unreleased): - Added complete support for HTML Tags, along with appropriate unit tests. This includes forms like foo, , and wiki-markup tags - like bold ('''), italics (''), and lists (''*'', ''#'', '';'' and '':''). + like bold ('''), italics (''), and lists (*, #, ; and :). +- Wikicode's filter methods are now passed 'recursive=True' by default instead + of False. This is a breaking change if you rely on any filter() methods being + non-recursive by default. - Various fixes and cleanup. v0.2 (released June 20, 2013): diff --git a/README.rst b/README.rst index 26b63bb..d481700 100644 --- a/README.rst +++ b/README.rst @@ -60,13 +60,20 @@ For example:: >>> print template.get("eggs").value spam -Since every node you reach is also a ``Wikicode`` object, it's trivial to get -nested templates:: +Since nodes can contain other nodes, getting nested templates is trivial:: + + >>> text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" + >>> mwparserfromhell.parse(text).filter_templates() + ['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}'] + +You can also pass ``recursive=False`` to ``filter_templates()`` and explore +templates manually. This is possible because nodes can contain additional +``Wikicode`` objects:: >>> code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") - >>> print code.filter_templates() + >>> print code.filter_templates(recursive=False) ['{{foo|this {{includes a|template}}}}'] - >>> foo = code.filter_templates()[0] + >>> foo = code.filter_templates(recursive=False)[0] >>> print foo.get(1).value this {{includes a|template}} >>> print foo.get(1).value.filter_templates()[0] @@ -74,13 +81,6 @@ nested templates:: >>> print foo.get(1).value.filter_templates()[0].get(1).value template -Additionally, you can include nested templates in ``filter_templates()`` by -passing ``recursive=True``:: - - >>> text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" - >>> mwparserfromhell.parse(text).filter_templates(recursive=True) - ['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}'] - Templates can be easily modified to add, remove, or alter params. ``Wikicode`` can also be treated like a list with ``append()``, ``insert()``, ``remove()``, ``replace()``, and more:: diff --git a/docs/changelog.rst b/docs/changelog.rst index cf8708d..18687f0 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,7 +10,10 @@ Unreleased - Added complete support for HTML :py:class:`Tags <.Tag>`, along with appropriate unit tests. This includes forms like ``foo``, ````, and wiki-markup tags like bold (``'''``), italics - (``''``), and lists (``''*''``, ``''#''``, ``'';''`` and ``'':''``). + (``''``), and lists (``*``, ``#``, ``;`` and ``:``). +- :py:class:`Wikicode's <.Wikicode>` :py:meth:`.filter` methods are now passed + *recursive=True* by default instead of *False*. **This is a breaking change + if you rely on any filter() methods being non-recursive by default.** - Various fixes and cleanup. v0.2 diff --git a/docs/usage.rst b/docs/usage.rst index 2fd19af..fd24a15 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -27,13 +27,20 @@ some extra methods. For example:: >>> print template.get("eggs").value spam -Since every node you reach is also a :py:class:`~.Wikicode` object, it's -trivial to get nested templates:: +Since nodes can contain other nodes, getting nested templates is trivial:: + + >>> text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" + >>> mwparserfromhell.parse(text).filter_templates() + ['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}'] + +You can also pass *recursive=False* to :py:meth:`~.filter_templates` and +explore templates manually. This is possible because nodes can contain +additional :py:class:`~.Wikicode` objects:: >>> code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") - >>> print code.filter_templates() + >>> print code.filter_templates(recursive=False) ['{{foo|this {{includes a|template}}}}'] - >>> foo = code.filter_templates()[0] + >>> foo = code.filter_templates(recursive=False)[0] >>> print foo.get(1).value this {{includes a|template}} >>> print foo.get(1).value.filter_templates()[0] @@ -41,13 +48,6 @@ trivial to get nested templates:: >>> print foo.get(1).value.filter_templates()[0].get(1).value template -Additionally, you can include nested templates in :py:meth:`~.filter_templates` -by passing *recursive=True*:: - - >>> text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" - >>> mwparserfromhell.parse(text).filter_templates(recursive=True) - ['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}'] - Templates can be easily modified to add, remove, or alter params. :py:class:`~.Wikicode` can also be treated like a list with :py:meth:`~.Wikicode.append`, :py:meth:`~.Wikicode.insert`, diff --git a/mwparserfromhell/wikicode.py b/mwparserfromhell/wikicode.py index 4ec889e..90b5d18 100644 --- a/mwparserfromhell/wikicode.py +++ b/mwparserfromhell/wikicode.py @@ -309,7 +309,7 @@ class Wikicode(StringMixIn): callback = lambda self, i: self.nodes.pop(i) self._do_search(obj, recursive, callback, self) - def ifilter(self, recursive=False, matches=None, flags=FLAGS, + def ifilter(self, recursive=True, matches=None, flags=FLAGS, forcetype=None): """Iterate over nodes in our list matching certain conditions. @@ -327,7 +327,7 @@ class Wikicode(StringMixIn): if not matches or re.search(matches, str(node), flags): yield node - def filter(self, recursive=False, matches=None, flags=FLAGS, + def filter(self, recursive=True, matches=None, flags=FLAGS, forcetype=None): """Return a list of nodes within our list matching certain conditions. diff --git a/tests/test_docs.py b/tests/test_docs.py index 8d95c47..5fdb520 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -61,30 +61,30 @@ class TestDocs(unittest.TestCase): def test_readme_2(self): """test a block of example code in the README""" + text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" + temps = mwparserfromhell.parse(text).filter_templates() + if py3k: + res = "['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}']" + else: + res = "[u'{{foo|{{bar}}={{baz|{{spam}}}}}}', u'{{bar}}', u'{{baz|{{spam}}}}', u'{{spam}}']" + self.assertPrint(temps, res) + + def test_readme_3(self): + """test a block of example code in the README""" code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") if py3k: - self.assertPrint(code.filter_templates(), + self.assertPrint(code.filter_templates(recursive=False), "['{{foo|this {{includes a|template}}}}']") else: - self.assertPrint(code.filter_templates(), + self.assertPrint(code.filter_templates(recursive=False), "[u'{{foo|this {{includes a|template}}}}']") - foo = code.filter_templates()[0] + foo = code.filter_templates(recursive=False)[0] self.assertPrint(foo.get(1).value, "this {{includes a|template}}") self.assertPrint(foo.get(1).value.filter_templates()[0], "{{includes a|template}}") self.assertPrint(foo.get(1).value.filter_templates()[0].get(1).value, "template") - def test_readme_3(self): - """test a block of example code in the README""" - text = "{{foo|{{bar}}={{baz|{{spam}}}}}}" - temps = mwparserfromhell.parse(text).filter_templates(recursive=True) - if py3k: - res = "['{{foo|{{bar}}={{baz|{{spam}}}}}}', '{{bar}}', '{{baz|{{spam}}}}', '{{spam}}']" - else: - res = "[u'{{foo|{{bar}}={{baz|{{spam}}}}}}', u'{{bar}}', u'{{baz|{{spam}}}}', u'{{spam}}']" - self.assertPrint(temps, res) - def test_readme_4(self): """test a block of example code in the README""" text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" diff --git a/tests/test_wikicode.py b/tests/test_wikicode.py index 8dfa655..2684414 100644 --- a/tests/test_wikicode.py +++ b/tests/test_wikicode.py @@ -219,11 +219,11 @@ class TestWikicode(TreeEqualityTestCase): code = parse("a{{b}}c[[d]]{{{e}}}{{f}}[[g]]") for func in (code.filter, ifilter(code)): - self.assertEqual(["a", "{{b}}", "c", "[[d]]", "{{{e}}}", "{{f}}", - "[[g]]"], func()) + self.assertEqual(["a", "{{b}}", "b", "c", "[[d]]", "d", "{{{e}}}", + "e", "{{f}}", "f", "[[g]]", "g"], func()) self.assertEqual(["{{{e}}}"], func(forcetype=Argument)) self.assertIs(code.get(4), func(forcetype=Argument)[0]) - self.assertEqual(["a", "c"], func(forcetype=Text)) + self.assertEqual(list("abcdefg"), func(forcetype=Text)) self.assertEqual([], func(forcetype=Heading)) self.assertRaises(TypeError, func, forcetype=True) @@ -239,7 +239,7 @@ class TestWikicode(TreeEqualityTestCase): self.assertEqual([], get_filter("html_entities")) self.assertEqual([], get_filter("tags")) self.assertEqual(["{{b}}", "{{f}}"], get_filter("templates")) - self.assertEqual(["a", "c"], get_filter("text")) + self.assertEqual(list("abcdefg"), get_filter("text")) self.assertEqual(["[[d]]", "[[g]]"], get_filter("wikilinks")) code2 = parse("{{a|{{b}}|{{c|d={{f}}{{h}}}}}}") @@ -252,13 +252,13 @@ class TestWikicode(TreeEqualityTestCase): code3 = parse("{{foobar}}{{FOO}}{{baz}}{{bz}}") for func in (code3.filter, ifilter(code3)): - self.assertEqual(["{{foobar}}", "{{FOO}}"], func(matches=r"foo")) + self.assertEqual(["{{foobar}}", "{{FOO}}"], func(recursive=False, matches=r"foo")) self.assertEqual(["{{foobar}}", "{{FOO}}"], - func(matches=r"^{{foo.*?}}")) + func(recursive=False, matches=r"^{{foo.*?}}")) self.assertEqual(["{{foobar}}"], - func(matches=r"^{{foo.*?}}", flags=re.UNICODE)) - self.assertEqual(["{{baz}}", "{{bz}}"], func(matches=r"^{{b.*?z")) - self.assertEqual(["{{baz}}"], func(matches=r"^{{b.+?z}}")) + func(recursive=False, matches=r"^{{foo.*?}}", flags=re.UNICODE)) + self.assertEqual(["{{baz}}", "{{bz}}"], func(recursive=False, matches=r"^{{b.*?z")) + self.assertEqual(["{{baz}}"], func(recursive=False, matches=r"^{{b.+?z}}")) self.assertEqual(["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"], code2.filter_templates(recursive=False))