| @@ -2,6 +2,8 @@ v0.5 (unreleased): | |||||
| - Made Template.remove(keep_field=True) behave more reasonably when the | - Made Template.remove(keep_field=True) behave more reasonably when the | ||||
| parameter is already empty. | parameter is already empty. | ||||
| - Added the keep_template_params argument to Wikicode.strip_code(). If True, | |||||
| then template parameters will be preserved in the output. | |||||
| - Wikicode objects can now be pickled properly (fixed infinite recursion error | - Wikicode objects can now be pickled properly (fixed infinite recursion error | ||||
| on incompletely-constructed StringMixIn subclasses). | on incompletely-constructed StringMixIn subclasses). | ||||
| - Fixed Wikicode.matches()'s behavior on iterables besides lists and tuples. | - Fixed Wikicode.matches()'s behavior on iterables besides lists and tuples. | ||||
| @@ -9,6 +9,8 @@ Unreleased | |||||
| - Made :meth:`Template.remove(keep_field=True) <.Template.remove>` behave more | - Made :meth:`Template.remove(keep_field=True) <.Template.remove>` behave more | ||||
| reasonably when the parameter is already empty. | reasonably when the parameter is already empty. | ||||
| - Added the *keep_template_params* argument to :meth:`.Wikicode.strip_code`. | |||||
| If *True*, then template parameters will be preserved in the output. | |||||
| - :class:`.Wikicode` objects can now be pickled properly (fixed infinite | - :class:`.Wikicode` objects can now be pickled properly (fixed infinite | ||||
| recursion error on incompletely-constructed :class:`.StringMixIn` | recursion error on incompletely-constructed :class:`.StringMixIn` | ||||
| subclasses). | subclasses). | ||||
| @@ -58,7 +58,7 @@ class Node(StringMixIn): | |||||
| return | return | ||||
| yield # pragma: no cover (this is a generator that yields nothing) | yield # pragma: no cover (this is a generator that yields nothing) | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| return None | return None | ||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| @@ -47,9 +47,9 @@ class Argument(Node): | |||||
| if self.default is not None: | if self.default is not None: | ||||
| yield self.default | yield self.default | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| if self.default is not None: | if self.default is not None: | ||||
| return self.default.strip_code(normalize, collapse) | |||||
| return self.default.strip_code(**kwargs) | |||||
| return None | return None | ||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| @@ -49,12 +49,12 @@ class ExternalLink(Node): | |||||
| if self.title is not None: | if self.title is not None: | ||||
| yield self.title | yield self.title | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| if self.brackets: | if self.brackets: | ||||
| if self.title: | if self.title: | ||||
| return self.title.strip_code(normalize, collapse) | |||||
| return self.title.strip_code(**kwargs) | |||||
| return None | return None | ||||
| return self.url.strip_code(normalize, collapse) | |||||
| return self.url.strip_code(**kwargs) | |||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| if self.brackets: | if self.brackets: | ||||
| @@ -42,8 +42,8 @@ class Heading(Node): | |||||
| def __children__(self): | def __children__(self): | ||||
| yield self.title | yield self.title | ||||
| def __strip__(self, normalize, collapse): | |||||
| return self.title.strip_code(normalize, collapse) | |||||
| def __strip__(self, **kwargs): | |||||
| return self.title.strip_code(**kwargs) | |||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| write("=" * self.level) | write("=" * self.level) | ||||
| @@ -58,8 +58,8 @@ class HTMLEntity(Node): | |||||
| return "&#{0}{1};".format(self.hex_char, self.value) | return "&#{0}{1};".format(self.hex_char, self.value) | ||||
| return "&#{0};".format(self.value) | return "&#{0};".format(self.value) | ||||
| def __strip__(self, normalize, collapse): | |||||
| if normalize: | |||||
| def __strip__(self, **kwargs): | |||||
| if kwargs.get("normalize"): | |||||
| return self.normalize() | return self.normalize() | ||||
| return self | return self | ||||
| @@ -98,9 +98,9 @@ class Tag(Node): | |||||
| if not self.self_closing and not self.wiki_markup and self.closing_tag: | if not self.self_closing and not self.wiki_markup and self.closing_tag: | ||||
| yield self.closing_tag | yield self.closing_tag | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| if self.contents and is_visible(self.tag): | if self.contents and is_visible(self.tag): | ||||
| return self.contents.strip_code(normalize, collapse) | |||||
| return self.contents.strip_code(**kwargs) | |||||
| return None | return None | ||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| @@ -58,6 +58,12 @@ class Template(Node): | |||||
| yield param.name | yield param.name | ||||
| yield param.value | yield param.value | ||||
| def __strip__(self, **kwargs): | |||||
| if kwargs.get("keep_template_params"): | |||||
| parts = [param.value.strip_code(**kwargs) for param in self.params] | |||||
| return " ".join(part for part in parts if part) | |||||
| return None | |||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| write("{{") | write("{{") | ||||
| get(self.name) | get(self.name) | ||||
| @@ -37,7 +37,7 @@ class Text(Node): | |||||
| def __unicode__(self): | def __unicode__(self): | ||||
| return self.value | return self.value | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| return self | return self | ||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| @@ -46,10 +46,10 @@ class Wikilink(Node): | |||||
| if self.text is not None: | if self.text is not None: | ||||
| yield self.text | yield self.text | ||||
| def __strip__(self, normalize, collapse): | |||||
| def __strip__(self, **kwargs): | |||||
| if self.text is not None: | if self.text is not None: | ||||
| return self.text.strip_code(normalize, collapse) | |||||
| return self.title.strip_code(normalize, collapse) | |||||
| return self.text.strip_code(**kwargs) | |||||
| return self.title.strip_code(**kwargs) | |||||
| def __showtree__(self, write, get, mark): | def __showtree__(self, write, get, mark): | ||||
| write("[[") | write("[[") | ||||
| @@ -531,23 +531,33 @@ class Wikicode(StringMixIn): | |||||
| # Ensure that earlier sections are earlier in the returned list: | # Ensure that earlier sections are earlier in the returned list: | ||||
| return [section for i, section in sorted(sections)] | return [section for i, section in sorted(sections)] | ||||
| def strip_code(self, normalize=True, collapse=True): | |||||
| def strip_code(self, normalize=True, collapse=True, | |||||
| keep_template_params=False): | |||||
| """Return a rendered string without unprintable code such as templates. | """Return a rendered string without unprintable code such as templates. | ||||
| The way a node is stripped is handled by the | The way a node is stripped is handled by the | ||||
| :meth:`~.Node.__strip__` method of :class:`.Node` objects, which | :meth:`~.Node.__strip__` method of :class:`.Node` objects, which | ||||
| generally return a subset of their nodes or ``None``. For example, | generally return a subset of their nodes or ``None``. For example, | ||||
| templates and tags are removed completely, links are stripped to just | templates and tags are removed completely, links are stripped to just | ||||
| their display part, headings are stripped to just their title. If | |||||
| *normalize* is ``True``, various things may be done to strip code | |||||
| their display part, headings are stripped to just their title. | |||||
| If *normalize* is ``True``, various things may be done to strip code | |||||
| further, such as converting HTML entities like ``Σ``, ``Σ``, | further, such as converting HTML entities like ``Σ``, ``Σ``, | ||||
| and ``Σ`` to ``Σ``. If *collapse* is ``True``, we will try to | and ``Σ`` to ``Σ``. If *collapse* is ``True``, we will try to | ||||
| remove excess whitespace as well (three or more newlines are converted | remove excess whitespace as well (three or more newlines are converted | ||||
| to two, for example). | |||||
| to two, for example). If *keep_template_params* is ``True``, then | |||||
| template parameters will be preserved in the output (normally, they are | |||||
| removed completely). | |||||
| """ | """ | ||||
| kwargs = { | |||||
| "normalize": normalize, | |||||
| "collapse": collapse, | |||||
| "keep_template_params": keep_template_params | |||||
| } | |||||
| nodes = [] | nodes = [] | ||||
| for node in self.nodes: | for node in self.nodes: | ||||
| stripped = node.__strip__(normalize, collapse) | |||||
| stripped = node.__strip__(**kwargs) | |||||
| if stripped: | if stripped: | ||||
| nodes.append(str(stripped)) | nodes.append(str(stripped)) | ||||
| @@ -56,12 +56,10 @@ class TestArgument(TreeEqualityTestCase): | |||||
| def test_strip(self): | def test_strip(self): | ||||
| """test Argument.__strip__()""" | """test Argument.__strip__()""" | ||||
| node = Argument(wraptext("foobar")) | |||||
| node1 = Argument(wraptext("foobar")) | |||||
| node2 = Argument(wraptext("foo"), wraptext("bar")) | node2 = Argument(wraptext("foo"), wraptext("bar")) | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertIs(None, node.__strip__(a, b)) | |||||
| self.assertEqual("bar", node2.__strip__(a, b)) | |||||
| self.assertIs(None, node1.__strip__()) | |||||
| self.assertEqual("bar", node2.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Argument.__showtree__()""" | """test Argument.__showtree__()""" | ||||
| @@ -49,9 +49,7 @@ class TestComment(TreeEqualityTestCase): | |||||
| def test_strip(self): | def test_strip(self): | ||||
| """test Comment.__strip__()""" | """test Comment.__strip__()""" | ||||
| node = Comment("foobar") | node = Comment("foobar") | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertIs(None, node.__strip__(a, b)) | |||||
| self.assertIs(None, node.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Comment.__showtree__()""" | """test Comment.__showtree__()""" | ||||
| @@ -66,12 +66,11 @@ class TestExternalLink(TreeEqualityTestCase): | |||||
| node2 = ExternalLink(wraptext("http://example.com")) | node2 = ExternalLink(wraptext("http://example.com")) | ||||
| node3 = ExternalLink(wraptext("http://example.com"), wrap([])) | node3 = ExternalLink(wraptext("http://example.com"), wrap([])) | ||||
| node4 = ExternalLink(wraptext("http://example.com"), wraptext("Link")) | node4 = ExternalLink(wraptext("http://example.com"), wraptext("Link")) | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertEqual("http://example.com", node1.__strip__(a, b)) | |||||
| self.assertEqual(None, node2.__strip__(a, b)) | |||||
| self.assertEqual(None, node3.__strip__(a, b)) | |||||
| self.assertEqual("Link", node4.__strip__(a, b)) | |||||
| self.assertEqual("http://example.com", node1.__strip__()) | |||||
| self.assertEqual(None, node2.__strip__()) | |||||
| self.assertEqual(None, node3.__strip__()) | |||||
| self.assertEqual("Link", node4.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test ExternalLink.__showtree__()""" | """test ExternalLink.__showtree__()""" | ||||
| @@ -52,9 +52,7 @@ class TestHeading(TreeEqualityTestCase): | |||||
| def test_strip(self): | def test_strip(self): | ||||
| """test Heading.__strip__()""" | """test Heading.__strip__()""" | ||||
| node = Heading(wraptext("foobar"), 3) | node = Heading(wraptext("foobar"), 3) | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertEqual("foobar", node.__strip__(a, b)) | |||||
| self.assertEqual("foobar", node.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Heading.__showtree__()""" | """test Heading.__showtree__()""" | ||||
| @@ -57,13 +57,13 @@ class TestHTMLEntity(TreeEqualityTestCase): | |||||
| node1 = HTMLEntity("nbsp", named=True, hexadecimal=False) | node1 = HTMLEntity("nbsp", named=True, hexadecimal=False) | ||||
| node2 = HTMLEntity("107", named=False, hexadecimal=False) | node2 = HTMLEntity("107", named=False, hexadecimal=False) | ||||
| node3 = HTMLEntity("e9", named=False, hexadecimal=True) | node3 = HTMLEntity("e9", named=False, hexadecimal=True) | ||||
| for a in (True, False): | |||||
| self.assertEqual("\xa0", node1.__strip__(True, a)) | |||||
| self.assertEqual(" ", node1.__strip__(False, a)) | |||||
| self.assertEqual("k", node2.__strip__(True, a)) | |||||
| self.assertEqual("k", node2.__strip__(False, a)) | |||||
| self.assertEqual("é", node3.__strip__(True, a)) | |||||
| self.assertEqual("é", node3.__strip__(False, a)) | |||||
| self.assertEqual("\xa0", node1.__strip__(normalize=True)) | |||||
| self.assertEqual(" ", node1.__strip__(normalize=False)) | |||||
| self.assertEqual("k", node2.__strip__(normalize=True)) | |||||
| self.assertEqual("k", node2.__strip__(normalize=False)) | |||||
| self.assertEqual("é", node3.__strip__(normalize=True)) | |||||
| self.assertEqual("é", node3.__strip__(normalize=False)) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test HTMLEntity.__showtree__()""" | """test HTMLEntity.__showtree__()""" | ||||
| @@ -103,11 +103,10 @@ class TestTag(TreeEqualityTestCase): | |||||
| node1 = Tag(wraptext("i"), wraptext("foobar")) | node1 = Tag(wraptext("i"), wraptext("foobar")) | ||||
| node2 = Tag(wraptext("math"), wraptext("foobar")) | node2 = Tag(wraptext("math"), wraptext("foobar")) | ||||
| node3 = Tag(wraptext("br"), self_closing=True) | node3 = Tag(wraptext("br"), self_closing=True) | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertEqual("foobar", node1.__strip__(a, b)) | |||||
| self.assertEqual(None, node2.__strip__(a, b)) | |||||
| self.assertEqual(None, node3.__strip__(a, b)) | |||||
| self.assertEqual("foobar", node1.__strip__()) | |||||
| self.assertEqual(None, node2.__strip__()) | |||||
| self.assertEqual(None, node3.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Tag.__showtree__()""" | """test Tag.__showtree__()""" | ||||
| @@ -67,12 +67,19 @@ class TestTemplate(TreeEqualityTestCase): | |||||
| def test_strip(self): | def test_strip(self): | ||||
| """test Template.__strip__()""" | """test Template.__strip__()""" | ||||
| node1 = Template(wraptext("foobar")) | node1 = Template(wraptext("foobar")) | ||||
| node2 = Template(wraptext("foo"), | |||||
| [pgenh("1", "bar"), pgens("abc", "def")]) | |||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertEqual(None, node1.__strip__(a, b)) | |||||
| self.assertEqual(None, node2.__strip__(a, b)) | |||||
| node2 = Template(wraptext("foo"), [ | |||||
| pgenh("1", "bar"), pgens("foo", ""), pgens("abc", "def")]) | |||||
| node3 = Template(wraptext("foo"), [ | |||||
| pgenh("1", "foo"), | |||||
| Parameter(wraptext("2"), wrap([Template(wraptext("hello"))]), | |||||
| showkey=False), | |||||
| pgenh("3", "bar")]) | |||||
| self.assertEqual(None, node1.__strip__(keep_template_params=False)) | |||||
| self.assertEqual(None, node2.__strip__(keep_template_params=False)) | |||||
| self.assertEqual("", node1.__strip__(keep_template_params=True)) | |||||
| self.assertEqual("bar def", node2.__strip__(keep_template_params=True)) | |||||
| self.assertEqual("foo bar", node3.__strip__(keep_template_params=True)) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Template.__showtree__()""" | """test Template.__showtree__()""" | ||||
| @@ -49,9 +49,7 @@ class TestText(unittest.TestCase): | |||||
| def test_strip(self): | def test_strip(self): | ||||
| """test Text.__strip__()""" | """test Text.__strip__()""" | ||||
| node = Text("foobar") | node = Text("foobar") | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertIs(node, node.__strip__(a, b)) | |||||
| self.assertIs(node, node.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Text.__showtree__()""" | """test Text.__showtree__()""" | ||||
| @@ -433,7 +433,7 @@ class TestWikicode(TreeEqualityTestCase): | |||||
| """test Wikicode.strip_code()""" | """test Wikicode.strip_code()""" | ||||
| # Since individual nodes have test cases for their __strip__ methods, | # Since individual nodes have test cases for their __strip__ methods, | ||||
| # we're only going to do an integration test: | # we're only going to do an integration test: | ||||
| code = parse("Foo [[bar]]\n\n{{baz}}\n\n[[a|b]] Σ") | |||||
| code = parse("Foo [[bar]]\n\n{{baz|hello}}\n\n[[a|b]] Σ") | |||||
| self.assertEqual("Foo bar\n\nb Σ", | self.assertEqual("Foo bar\n\nb Σ", | ||||
| code.strip_code(normalize=True, collapse=True)) | code.strip_code(normalize=True, collapse=True)) | ||||
| self.assertEqual("Foo bar\n\n\n\nb Σ", | self.assertEqual("Foo bar\n\n\n\nb Σ", | ||||
| @@ -442,6 +442,9 @@ class TestWikicode(TreeEqualityTestCase): | |||||
| code.strip_code(normalize=False, collapse=True)) | code.strip_code(normalize=False, collapse=True)) | ||||
| self.assertEqual("Foo bar\n\n\n\nb Σ", | self.assertEqual("Foo bar\n\n\n\nb Σ", | ||||
| code.strip_code(normalize=False, collapse=False)) | code.strip_code(normalize=False, collapse=False)) | ||||
| self.assertEqual("Foo bar\n\nhello\n\nb Σ", | |||||
| code.strip_code(normalize=True, collapse=True, | |||||
| keep_template_params=True)) | |||||
| def test_get_tree(self): | def test_get_tree(self): | ||||
| """test Wikicode.get_tree()""" | """test Wikicode.get_tree()""" | ||||
| @@ -58,10 +58,8 @@ class TestWikilink(TreeEqualityTestCase): | |||||
| """test Wikilink.__strip__()""" | """test Wikilink.__strip__()""" | ||||
| node = Wikilink(wraptext("foobar")) | node = Wikilink(wraptext("foobar")) | ||||
| node2 = Wikilink(wraptext("foo"), wraptext("bar")) | node2 = Wikilink(wraptext("foo"), wraptext("bar")) | ||||
| for a in (True, False): | |||||
| for b in (True, False): | |||||
| self.assertEqual("foobar", node.__strip__(a, b)) | |||||
| self.assertEqual("bar", node2.__strip__(a, b)) | |||||
| self.assertEqual("foobar", node.__strip__()) | |||||
| self.assertEqual("bar", node2.__strip__()) | |||||
| def test_showtree(self): | def test_showtree(self): | ||||
| """test Wikilink.__showtree__()""" | """test Wikilink.__showtree__()""" | ||||