This is a breaking change for those who rely on default behavior.tags/v0.3
| @@ -2,7 +2,10 @@ v0.3 (unreleased): | |||||
| - Added complete support for HTML Tags, along with appropriate unit tests. This | - Added complete support for HTML Tags, along with appropriate unit tests. This | ||||
| includes forms like <ref>foo</ref>, <ref name="bar"/>, and wiki-markup tags | includes forms like <ref>foo</ref>, <ref name="bar"/>, 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. | - Various fixes and cleanup. | ||||
| v0.2 (released June 20, 2013): | v0.2 (released June 20, 2013): | ||||
| @@ -60,13 +60,20 @@ For example:: | |||||
| >>> print template.get("eggs").value | >>> print template.get("eggs").value | ||||
| spam | 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}}}}") | >>> code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") | ||||
| >>> print code.filter_templates() | |||||
| >>> print code.filter_templates(recursive=False) | |||||
| ['{{foo|this {{includes a|template}}}}'] | ['{{foo|this {{includes a|template}}}}'] | ||||
| >>> foo = code.filter_templates()[0] | |||||
| >>> foo = code.filter_templates(recursive=False)[0] | |||||
| >>> print foo.get(1).value | >>> print foo.get(1).value | ||||
| this {{includes a|template}} | this {{includes a|template}} | ||||
| >>> print foo.get(1).value.filter_templates()[0] | >>> 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 | >>> print foo.get(1).value.filter_templates()[0].get(1).value | ||||
| template | 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`` | Templates can be easily modified to add, remove, or alter params. ``Wikicode`` | ||||
| can also be treated like a list with ``append()``, ``insert()``, ``remove()``, | can also be treated like a list with ``append()``, ``insert()``, ``remove()``, | ||||
| ``replace()``, and more:: | ``replace()``, and more:: | ||||
| @@ -10,7 +10,10 @@ Unreleased | |||||
| - Added complete support for HTML :py:class:`Tags <.Tag>`, along with | - Added complete support for HTML :py:class:`Tags <.Tag>`, along with | ||||
| appropriate unit tests. This includes forms like ``<ref>foo</ref>``, | appropriate unit tests. This includes forms like ``<ref>foo</ref>``, | ||||
| ``<ref name="bar"/>``, and wiki-markup tags like bold (``'''``), italics | ``<ref name="bar"/>``, 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. | - Various fixes and cleanup. | ||||
| v0.2 | v0.2 | ||||
| @@ -27,13 +27,20 @@ some extra methods. For example:: | |||||
| >>> print template.get("eggs").value | >>> print template.get("eggs").value | ||||
| spam | 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}}}}") | >>> code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") | ||||
| >>> print code.filter_templates() | |||||
| >>> print code.filter_templates(recursive=False) | |||||
| ['{{foo|this {{includes a|template}}}}'] | ['{{foo|this {{includes a|template}}}}'] | ||||
| >>> foo = code.filter_templates()[0] | |||||
| >>> foo = code.filter_templates(recursive=False)[0] | |||||
| >>> print foo.get(1).value | >>> print foo.get(1).value | ||||
| this {{includes a|template}} | this {{includes a|template}} | ||||
| >>> print foo.get(1).value.filter_templates()[0] | >>> 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 | >>> print foo.get(1).value.filter_templates()[0].get(1).value | ||||
| template | 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. | Templates can be easily modified to add, remove, or alter params. | ||||
| :py:class:`~.Wikicode` can also be treated like a list with | :py:class:`~.Wikicode` can also be treated like a list with | ||||
| :py:meth:`~.Wikicode.append`, :py:meth:`~.Wikicode.insert`, | :py:meth:`~.Wikicode.append`, :py:meth:`~.Wikicode.insert`, | ||||
| @@ -309,7 +309,7 @@ class Wikicode(StringMixIn): | |||||
| callback = lambda self, i: self.nodes.pop(i) | callback = lambda self, i: self.nodes.pop(i) | ||||
| self._do_search(obj, recursive, callback, self) | 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): | forcetype=None): | ||||
| """Iterate over nodes in our list matching certain conditions. | """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): | if not matches or re.search(matches, str(node), flags): | ||||
| yield node | yield node | ||||
| def filter(self, recursive=False, matches=None, flags=FLAGS, | |||||
| def filter(self, recursive=True, matches=None, flags=FLAGS, | |||||
| forcetype=None): | forcetype=None): | ||||
| """Return a list of nodes within our list matching certain conditions. | """Return a list of nodes within our list matching certain conditions. | ||||
| @@ -61,30 +61,30 @@ class TestDocs(unittest.TestCase): | |||||
| def test_readme_2(self): | def test_readme_2(self): | ||||
| """test a block of example code in the README""" | """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}}}}") | code = mwparserfromhell.parse("{{foo|this {{includes a|template}}}}") | ||||
| if py3k: | if py3k: | ||||
| self.assertPrint(code.filter_templates(), | |||||
| self.assertPrint(code.filter_templates(recursive=False), | |||||
| "['{{foo|this {{includes a|template}}}}']") | "['{{foo|this {{includes a|template}}}}']") | ||||
| else: | else: | ||||
| self.assertPrint(code.filter_templates(), | |||||
| self.assertPrint(code.filter_templates(recursive=False), | |||||
| "[u'{{foo|this {{includes a|template}}}}']") | "[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, "this {{includes a|template}}") | ||||
| self.assertPrint(foo.get(1).value.filter_templates()[0], | self.assertPrint(foo.get(1).value.filter_templates()[0], | ||||
| "{{includes a|template}}") | "{{includes a|template}}") | ||||
| self.assertPrint(foo.get(1).value.filter_templates()[0].get(1).value, | self.assertPrint(foo.get(1).value.filter_templates()[0].get(1).value, | ||||
| "template") | "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): | def test_readme_4(self): | ||||
| """test a block of example code in the README""" | """test a block of example code in the README""" | ||||
| text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | ||||
| @@ -219,11 +219,11 @@ class TestWikicode(TreeEqualityTestCase): | |||||
| code = parse("a{{b}}c[[d]]{{{e}}}{{f}}[[g]]") | code = parse("a{{b}}c[[d]]{{{e}}}{{f}}[[g]]") | ||||
| for func in (code.filter, ifilter(code)): | 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.assertEqual(["{{{e}}}"], func(forcetype=Argument)) | ||||
| self.assertIs(code.get(4), func(forcetype=Argument)[0]) | 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.assertEqual([], func(forcetype=Heading)) | ||||
| self.assertRaises(TypeError, func, forcetype=True) | self.assertRaises(TypeError, func, forcetype=True) | ||||
| @@ -239,7 +239,7 @@ class TestWikicode(TreeEqualityTestCase): | |||||
| self.assertEqual([], get_filter("html_entities")) | self.assertEqual([], get_filter("html_entities")) | ||||
| self.assertEqual([], get_filter("tags")) | self.assertEqual([], get_filter("tags")) | ||||
| self.assertEqual(["{{b}}", "{{f}}"], get_filter("templates")) | 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")) | self.assertEqual(["[[d]]", "[[g]]"], get_filter("wikilinks")) | ||||
| code2 = parse("{{a|{{b}}|{{c|d={{f}}{{h}}}}}}") | code2 = parse("{{a|{{b}}|{{c|d={{f}}{{h}}}}}}") | ||||
| @@ -252,13 +252,13 @@ class TestWikicode(TreeEqualityTestCase): | |||||
| code3 = parse("{{foobar}}{{FOO}}{{baz}}{{bz}}") | code3 = parse("{{foobar}}{{FOO}}{{baz}}{{bz}}") | ||||
| for func in (code3.filter, ifilter(code3)): | 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}}"], | self.assertEqual(["{{foobar}}", "{{FOO}}"], | ||||
| func(matches=r"^{{foo.*?}}")) | |||||
| func(recursive=False, matches=r"^{{foo.*?}}")) | |||||
| self.assertEqual(["{{foobar}}"], | 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}}}}}}"], | self.assertEqual(["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"], | ||||
| code2.filter_templates(recursive=False)) | code2.filter_templates(recursive=False)) | ||||