@@ -6,6 +6,7 @@ v0.3 (unreleased): | |||||
- Wikicode's filter methods are now passed 'recursive=True' by default instead | - 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 | of False. This is a breaking change if you rely on any filter() methods being | ||||
non-recursive by default. | non-recursive by default. | ||||
- Added a matches() method to Wikicode for page/template name comparisons. | |||||
- Various fixes and cleanup. | - Various fixes and cleanup. | ||||
v0.2 (released June 20, 2013): | v0.2 (released June 20, 2013): | ||||
@@ -82,13 +82,15 @@ templates manually. This is possible because nodes can contain additional | |||||
template | template | ||||
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()``, | |||||
``replace()``, and more:: | |||||
objects can be treated like lists, with ``append()``, ``insert()``, | |||||
``remove()``, ``replace()``, and more. They also have a ``matches()`` method | |||||
for comparing page or template names, which takes care of capitalization and | |||||
whitespace:: | |||||
>>> text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | >>> text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | ||||
>>> code = mwparserfromhell.parse(text) | >>> code = mwparserfromhell.parse(text) | ||||
>>> for template in code.filter_templates(): | >>> for template in code.filter_templates(): | ||||
... if template.name == "cleanup" and not template.has_param("date"): | |||||
... if template.name.matches("Cleanup") and not template.has_param("date"): | |||||
... template.add("date", "July 2012") | ... template.add("date", "July 2012") | ||||
... | ... | ||||
>>> print code | >>> print code | ||||
@@ -14,6 +14,8 @@ Unreleased | |||||
- :py:class:`Wikicode's <.Wikicode>` :py:meth:`.filter` methods are now passed | - :py:class:`Wikicode's <.Wikicode>` :py:meth:`.filter` methods are now passed | ||||
*recursive=True* by default instead of *False*. **This is a breaking change | *recursive=True* by default instead of *False*. **This is a breaking change | ||||
if you rely on any filter() methods being non-recursive by default.** | if you rely on any filter() methods being non-recursive by default.** | ||||
- Added a :py:meth:`.matches` method to :py:class:`~.Wikicode` for | |||||
page/template name comparisons. | |||||
- Various fixes and cleanup. | - Various fixes and cleanup. | ||||
v0.2 | v0.2 | ||||
@@ -49,14 +49,16 @@ additional :py:class:`~.Wikicode` objects:: | |||||
template | template | ||||
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` objects can be treated like lists, with | |||||
:py:meth:`~.Wikicode.append`, :py:meth:`~.Wikicode.insert`, | :py:meth:`~.Wikicode.append`, :py:meth:`~.Wikicode.insert`, | ||||
:py:meth:`~.Wikicode.remove`, :py:meth:`~.Wikicode.replace`, and more:: | |||||
:py:meth:`~.Wikicode.remove`, :py:meth:`~.Wikicode.replace`, and more. They | |||||
also have a :py:meth:`~.Wikicode.matches` method for comparing page or template | |||||
names, which takes care of capitalization and whitespace:: | |||||
>>> text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | >>> text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | ||||
>>> code = mwparserfromhell.parse(text) | >>> code = mwparserfromhell.parse(text) | ||||
>>> for template in code.filter_templates(): | >>> for template in code.filter_templates(): | ||||
... if template.name == "cleanup" and not template.has_param("date"): | |||||
... if template.name.matches("Cleanup") and not template.has_param("date"): | |||||
... template.add("date", "July 2012") | ... template.add("date", "July 2012") | ||||
... | ... | ||||
>>> print code | >>> print code | ||||
@@ -309,6 +309,21 @@ 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 matches(self, other): | |||||
"""Do a loose equivalency test suitable for comparing page names. | |||||
*other* can be any string-like object, including | |||||
:py:class:`~.Wikicode`. This operation is symmetric; both sides are | |||||
adjusted. Specifically, whitespace and markup is stripped and the first | |||||
letter's case is normalized. Typical usage is | |||||
``if template.name.matches("stub"): ...``. | |||||
""" | |||||
this = self.strip_code().strip() | |||||
that = parse_anything(other).strip_code().strip() | |||||
if not this or not that: | |||||
return this == that | |||||
return this[0].upper() + this[1:] == that[0].upper() + that[1:] | |||||
def ifilter(self, recursive=True, 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. | ||||
@@ -90,7 +90,7 @@ class TestDocs(unittest.TestCase): | |||||
text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | text = "{{cleanup}} '''Foo''' is a [[bar]]. {{uncategorized}}" | ||||
code = mwparserfromhell.parse(text) | code = mwparserfromhell.parse(text) | ||||
for template in code.filter_templates(): | for template in code.filter_templates(): | ||||
if template.name == "cleanup" and not template.has_param("date"): | |||||
if template.name.matches("Cleanup") and not template.has_param("date"): | |||||
template.add("date", "July 2012") | template.add("date", "July 2012") | ||||
res = "{{cleanup|date=July 2012}} '''Foo''' is a [[bar]]. {{uncategorized}}" | res = "{{cleanup|date=July 2012}} '''Foo''' is a [[bar]]. {{uncategorized}}" | ||||
self.assertPrint(code, res) | self.assertPrint(code, res) | ||||
@@ -210,6 +210,19 @@ class TestWikicode(TreeEqualityTestCase): | |||||
self.assertEqual("{{a||{{c|d={{h}}}}}}", code2) | self.assertEqual("{{a||{{c|d={{h}}}}}}", code2) | ||||
self.assertRaises(ValueError, code2.remove, "{{h}}", recursive=False) | self.assertRaises(ValueError, code2.remove, "{{h}}", recursive=False) | ||||
def test_matches(self): | |||||
"""test Wikicode.matches()""" | |||||
code1 = parse("Cleanup") | |||||
code2 = parse("\nstub<!-- TODO: make more specific -->") | |||||
self.assertTrue(code1.matches("Cleanup")) | |||||
self.assertTrue(code1.matches("cleanup")) | |||||
self.assertTrue(code1.matches(" cleanup\n")) | |||||
self.assertFalse(code1.matches("CLEANup")) | |||||
self.assertFalse(code1.matches("Blah")) | |||||
self.assertTrue(code2.matches("stub")) | |||||
self.assertTrue(code2.matches("Stub<!-- no, it's fine! -->")) | |||||
self.assertFalse(code2.matches("StuB")) | |||||
def test_filter_family(self): | def test_filter_family(self): | ||||
"""test the Wikicode.i?filter() family of functions""" | """test the Wikicode.i?filter() family of functions""" | ||||
def genlist(gen): | def genlist(gen): | ||||