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)) | ||||