A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

488 lines
24 KiB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22. from __future__ import unicode_literals
  23. from functools import partial
  24. import re
  25. from types import GeneratorType
  26. try:
  27. import unittest2 as unittest
  28. except ImportError:
  29. import unittest
  30. from mwparserfromhell.compat import py3k, str
  31. from mwparserfromhell.nodes import (Argument, Comment, Heading, HTMLEntity,
  32. Node, Tag, Template, Text, Wikilink)
  33. from mwparserfromhell.smart_list import SmartList
  34. from mwparserfromhell.wikicode import Wikicode
  35. from mwparserfromhell import parse
  36. from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext
  37. class TestWikicode(TreeEqualityTestCase):
  38. """Tests for the Wikicode class, which manages a list of nodes."""
  39. def test_unicode(self):
  40. """test Wikicode.__unicode__()"""
  41. code1 = parse("foobar")
  42. code2 = parse("Have a {{template}} and a [[page|link]]")
  43. self.assertEqual("foobar", str(code1))
  44. self.assertEqual("Have a {{template}} and a [[page|link]]", str(code2))
  45. def test_nodes(self):
  46. """test getter/setter for the nodes attribute"""
  47. code = parse("Have a {{template}}")
  48. self.assertEqual(["Have a ", "{{template}}"], code.nodes)
  49. L1 = SmartList([Text("foobar"), Template(wraptext("abc"))])
  50. L2 = [Text("barfoo"), Template(wraptext("cba"))]
  51. L3 = "abc{{def}}"
  52. code.nodes = L1
  53. self.assertIs(L1, code.nodes)
  54. code.nodes = L2
  55. self.assertIs(L2, code.nodes)
  56. code.nodes = L3
  57. self.assertEqual(["abc", "{{def}}"], code.nodes)
  58. self.assertRaises(ValueError, setattr, code, "nodes", object)
  59. def test_get(self):
  60. """test Wikicode.get()"""
  61. code = parse("Have a {{template}} and a [[page|link]]")
  62. self.assertIs(code.nodes[0], code.get(0))
  63. self.assertIs(code.nodes[2], code.get(2))
  64. self.assertRaises(IndexError, code.get, 4)
  65. def test_set(self):
  66. """test Wikicode.set()"""
  67. code = parse("Have a {{template}} and a [[page|link]]")
  68. code.set(1, "{{{argument}}}")
  69. self.assertEqual("Have a {{{argument}}} and a [[page|link]]", code)
  70. self.assertIsInstance(code.get(1), Argument)
  71. code.set(2, None)
  72. self.assertEqual("Have a {{{argument}}}[[page|link]]", code)
  73. code.set(-3, "This is an ")
  74. self.assertEqual("This is an {{{argument}}}[[page|link]]", code)
  75. self.assertRaises(ValueError, code.set, 1, "foo {{bar}}")
  76. self.assertRaises(IndexError, code.set, 3, "{{baz}}")
  77. self.assertRaises(IndexError, code.set, -4, "{{baz}}")
  78. def test_contains(self):
  79. """test Wikicode.contains()"""
  80. code = parse("Here is {{aaa|{{bbb|xyz{{ccc}}}}}} and a [[page|link]]")
  81. tmpl1, tmpl2, tmpl3 = code.filter_templates()
  82. tmpl4 = parse("{{ccc}}").filter_templates()[0]
  83. self.assertTrue(code.contains(tmpl1))
  84. self.assertTrue(code.contains(tmpl3))
  85. self.assertFalse(code.contains(tmpl4))
  86. self.assertTrue(code.contains(str(tmpl4)))
  87. self.assertTrue(code.contains(tmpl2.params[0].value))
  88. def test_index(self):
  89. """test Wikicode.index()"""
  90. code = parse("Have a {{template}} and a [[page|link]]")
  91. self.assertEqual(0, code.index("Have a "))
  92. self.assertEqual(3, code.index("[[page|link]]"))
  93. self.assertEqual(1, code.index(code.get(1)))
  94. self.assertRaises(ValueError, code.index, "foo")
  95. code = parse("{{foo}}{{bar|{{baz}}}}")
  96. self.assertEqual(1, code.index("{{bar|{{baz}}}}"))
  97. self.assertEqual(1, code.index("{{baz}}", recursive=True))
  98. self.assertEqual(1, code.index(code.get(1).get(1).value,
  99. recursive=True))
  100. self.assertRaises(ValueError, code.index, "{{baz}}", recursive=False)
  101. self.assertRaises(ValueError, code.index,
  102. code.get(1).get(1).value, recursive=False)
  103. def test_get_ancestors_parent(self):
  104. """test Wikicode.get_ancestors() and Wikicode.get_parent()"""
  105. code = parse("{{a|{{b|{{d|{{e}}{{f}}}}{{g}}}}}}{{c}}")
  106. tmpl = code.filter_templates(matches=lambda n: n.name == "f")[0]
  107. parent1 = code.filter_templates(matches=lambda n: n.name == "d")[0]
  108. parent2 = code.filter_templates(matches=lambda n: n.name == "b")[0]
  109. parent3 = code.filter_templates(matches=lambda n: n.name == "a")[0]
  110. fake = parse("{{f}}").get(0)
  111. self.assertEqual([parent3, parent2, parent1], code.get_ancestors(tmpl))
  112. self.assertIs(parent1, code.get_parent(tmpl))
  113. self.assertEqual([], code.get_ancestors(parent3))
  114. self.assertIs(None, code.get_parent(parent3))
  115. self.assertRaises(ValueError, code.get_ancestors, fake)
  116. self.assertRaises(ValueError, code.get_parent, fake)
  117. def test_insert(self):
  118. """test Wikicode.insert()"""
  119. code = parse("Have a {{template}} and a [[page|link]]")
  120. code.insert(1, "{{{argument}}}")
  121. self.assertEqual(
  122. "Have a {{{argument}}}{{template}} and a [[page|link]]", code)
  123. self.assertIsInstance(code.get(1), Argument)
  124. code.insert(2, None)
  125. self.assertEqual(
  126. "Have a {{{argument}}}{{template}} and a [[page|link]]", code)
  127. code.insert(-3, Text("foo"))
  128. self.assertEqual(
  129. "Have a {{{argument}}}foo{{template}} and a [[page|link]]", code)
  130. code2 = parse("{{foo}}{{bar}}{{baz}}")
  131. code2.insert(1, "abc{{def}}ghi[[jk]]")
  132. self.assertEqual("{{foo}}abc{{def}}ghi[[jk]]{{bar}}{{baz}}", code2)
  133. self.assertEqual(["{{foo}}", "abc", "{{def}}", "ghi", "[[jk]]",
  134. "{{bar}}", "{{baz}}"], code2.nodes)
  135. code3 = parse("{{foo}}bar")
  136. code3.insert(1000, "[[baz]]")
  137. code3.insert(-1000, "derp")
  138. self.assertEqual("derp{{foo}}bar[[baz]]", code3)
  139. def _test_search(self, meth, expected):
  140. """Base test for insert_before(), insert_after(), and replace()."""
  141. code = parse("{{a}}{{b}}{{c}}{{d}}{{e}}")
  142. func = partial(meth, code)
  143. func("{{b}}", "x", recursive=True)
  144. func("{{d}}", "[[y]]", recursive=False)
  145. func(code.get(2), "z")
  146. self.assertEqual(expected[0], code)
  147. self.assertRaises(ValueError, func, "{{r}}", "n", recursive=True)
  148. self.assertRaises(ValueError, func, "{{r}}", "n", recursive=False)
  149. fake = parse("{{a}}").get(0)
  150. self.assertRaises(ValueError, func, fake, "n", recursive=True)
  151. self.assertRaises(ValueError, func, fake, "n", recursive=False)
  152. code2 = parse("{{a}}{{a}}{{a}}{{b}}{{b}}{{b}}")
  153. func = partial(meth, code2)
  154. func(code2.get(1), "c", recursive=False)
  155. func("{{a}}", "d", recursive=False)
  156. func(code2.get(-1), "e", recursive=True)
  157. func("{{b}}", "f", recursive=True)
  158. self.assertEqual(expected[1], code2)
  159. code3 = parse("{{a|{{b}}|{{c|d={{f}}}}}}")
  160. func = partial(meth, code3)
  161. obj = code3.get(0).params[0].value.get(0)
  162. self.assertRaises(ValueError, func, obj, "x", recursive=False)
  163. func(obj, "x", recursive=True)
  164. self.assertRaises(ValueError, func, "{{f}}", "y", recursive=False)
  165. func("{{f}}", "y", recursive=True)
  166. self.assertEqual(expected[2], code3)
  167. code4 = parse("{{a}}{{b}}{{c}}{{d}}{{e}}{{f}}{{g}}{{h}}{{i}}{{j}}")
  168. func = partial(meth, code4)
  169. fake = parse("{{b}}{{c}}")
  170. self.assertRaises(ValueError, func, fake, "q", recursive=False)
  171. self.assertRaises(ValueError, func, fake, "q", recursive=True)
  172. func("{{b}}{{c}}", "w", recursive=False)
  173. func("{{d}}{{e}}", "x", recursive=True)
  174. func(wrap(code4.nodes[-2:]), "y", recursive=False)
  175. func(wrap(code4.nodes[-2:]), "z", recursive=True)
  176. self.assertEqual(expected[3], code4)
  177. self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=False)
  178. self.assertRaises(ValueError, func, "{{c}}{{d}}", "q", recursive=True)
  179. code5 = parse("{{a|{{b}}{{c}}|{{f|{{g}}={{h}}{{i}}}}}}")
  180. func = partial(meth, code5)
  181. self.assertRaises(ValueError, func, "{{b}}{{c}}", "x", recursive=False)
  182. func("{{b}}{{c}}", "x", recursive=True)
  183. obj = code5.get(0).params[1].value.get(0).params[0].value
  184. self.assertRaises(ValueError, func, obj, "y", recursive=False)
  185. func(obj, "y", recursive=True)
  186. self.assertEqual(expected[4], code5)
  187. code6 = parse("here is {{some text and a {{template}}}}")
  188. func = partial(meth, code6)
  189. self.assertRaises(ValueError, func, "text and", "ab", recursive=False)
  190. func("text and", "ab", recursive=True)
  191. self.assertRaises(ValueError, func, "is {{some", "cd", recursive=False)
  192. func("is {{some", "cd", recursive=True)
  193. self.assertEqual(expected[5], code6)
  194. code7 = parse("{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}")
  195. func = partial(meth, code7)
  196. obj = wrap([code7.get(0), code7.get(2)])
  197. self.assertRaises(ValueError, func, obj, "{{lol}}")
  198. func("{{foo}}{{baz}}", "{{lol}}")
  199. self.assertEqual(expected[6], code7)
  200. def test_insert_before(self):
  201. """test Wikicode.insert_before()"""
  202. meth = lambda code, *args, **kw: code.insert_before(*args, **kw)
  203. expected = [
  204. "{{a}}xz{{b}}{{c}}[[y]]{{d}}{{e}}",
  205. "d{{a}}cd{{a}}d{{a}}f{{b}}f{{b}}ef{{b}}",
  206. "{{a|x{{b}}|{{c|d=y{{f}}}}}}",
  207. "{{a}}w{{b}}{{c}}x{{d}}{{e}}{{f}}{{g}}{{h}}yz{{i}}{{j}}",
  208. "{{a|x{{b}}{{c}}|{{f|{{g}}=y{{h}}{{i}}}}}}",
  209. "here cdis {{some abtext and a {{template}}}}",
  210. "{{foo}}{{bar}}{{baz}}{{lol}}{{foo}}{{baz}}"]
  211. self._test_search(meth, expected)
  212. def test_insert_after(self):
  213. """test Wikicode.insert_after()"""
  214. meth = lambda code, *args, **kw: code.insert_after(*args, **kw)
  215. expected = [
  216. "{{a}}{{b}}xz{{c}}{{d}}[[y]]{{e}}",
  217. "{{a}}d{{a}}dc{{a}}d{{b}}f{{b}}f{{b}}fe",
  218. "{{a|{{b}}x|{{c|d={{f}}y}}}}",
  219. "{{a}}{{b}}{{c}}w{{d}}{{e}}x{{f}}{{g}}{{h}}{{i}}{{j}}yz",
  220. "{{a|{{b}}{{c}}x|{{f|{{g}}={{h}}{{i}}y}}}}",
  221. "here is {{somecd text andab a {{template}}}}",
  222. "{{foo}}{{bar}}{{baz}}{{foo}}{{baz}}{{lol}}"]
  223. self._test_search(meth, expected)
  224. def test_replace(self):
  225. """test Wikicode.replace()"""
  226. meth = lambda code, *args, **kw: code.replace(*args, **kw)
  227. expected = [
  228. "{{a}}xz[[y]]{{e}}", "dcdffe", "{{a|x|{{c|d=y}}}}",
  229. "{{a}}wx{{f}}{{g}}z", "{{a|x|{{f|{{g}}=y}}}}",
  230. "here cd ab a {{template}}}}", "{{foo}}{{bar}}{{baz}}{{lol}}"]
  231. self._test_search(meth, expected)
  232. def test_append(self):
  233. """test Wikicode.append()"""
  234. code = parse("Have a {{template}}")
  235. code.append("{{{argument}}}")
  236. self.assertEqual("Have a {{template}}{{{argument}}}", code)
  237. self.assertIsInstance(code.get(2), Argument)
  238. code.append(None)
  239. self.assertEqual("Have a {{template}}{{{argument}}}", code)
  240. code.append(Text(" foo"))
  241. self.assertEqual("Have a {{template}}{{{argument}}} foo", code)
  242. self.assertRaises(ValueError, code.append, slice(0, 1))
  243. def test_remove(self):
  244. """test Wikicode.remove()"""
  245. meth = lambda code, obj, value, **kw: code.remove(obj, **kw)
  246. expected = [
  247. "{{a}}{{c}}", "", "{{a||{{c|d=}}}}", "{{a}}{{f}}",
  248. "{{a||{{f|{{g}}=}}}}", "here a {{template}}}}",
  249. "{{foo}}{{bar}}{{baz}}"]
  250. self._test_search(meth, expected)
  251. def test_matches(self):
  252. """test Wikicode.matches()"""
  253. code1 = parse("Cleanup")
  254. code2 = parse("\nstub<!-- TODO: make more specific -->")
  255. code3 = parse("")
  256. self.assertTrue(code1.matches("Cleanup"))
  257. self.assertTrue(code1.matches("cleanup"))
  258. self.assertTrue(code1.matches(" cleanup\n"))
  259. self.assertFalse(code1.matches("CLEANup"))
  260. self.assertFalse(code1.matches("Blah"))
  261. self.assertTrue(code2.matches("stub"))
  262. self.assertTrue(code2.matches("Stub<!-- no, it's fine! -->"))
  263. self.assertFalse(code2.matches("StuB"))
  264. self.assertTrue(code1.matches(("cleanup", "stub")))
  265. self.assertTrue(code2.matches(("cleanup", "stub")))
  266. self.assertFalse(code2.matches(("StuB", "sTUb", "foobar")))
  267. self.assertFalse(code2.matches(["StuB", "sTUb", "foobar"]))
  268. self.assertTrue(code2.matches(("StuB", "sTUb", "foo", "bar", "Stub")))
  269. self.assertTrue(code2.matches(["StuB", "sTUb", "foo", "bar", "Stub"]))
  270. self.assertTrue(code3.matches(""))
  271. self.assertTrue(code3.matches("<!-- nothing -->"))
  272. self.assertTrue(code3.matches(("a", "b", "")))
  273. def test_filter_family(self):
  274. """test the Wikicode.i?filter() family of functions"""
  275. def genlist(gen):
  276. self.assertIsInstance(gen, GeneratorType)
  277. return list(gen)
  278. ifilter = lambda code: (lambda *a, **k: genlist(code.ifilter(*a, **k)))
  279. code = parse("a{{b}}c[[d]]{{{e}}}{{f}}[[g]]")
  280. for func in (code.filter, ifilter(code)):
  281. self.assertEqual(["a", "{{b}}", "b", "c", "[[d]]", "d", "{{{e}}}",
  282. "e", "{{f}}", "f", "[[g]]", "g"], func())
  283. self.assertEqual(["{{{e}}}"], func(forcetype=Argument))
  284. self.assertIs(code.get(4), func(forcetype=Argument)[0])
  285. self.assertEqual(list("abcdefg"), func(forcetype=Text))
  286. self.assertEqual([], func(forcetype=Heading))
  287. self.assertRaises(TypeError, func, forcetype=True)
  288. funcs = [
  289. lambda name, **kw: getattr(code, "filter_" + name)(**kw),
  290. lambda name, **kw: genlist(getattr(code, "ifilter_" + name)(**kw))
  291. ]
  292. for get_filter in funcs:
  293. self.assertEqual(["{{{e}}}"], get_filter("arguments"))
  294. self.assertIs(code.get(4), get_filter("arguments")[0])
  295. self.assertEqual([], get_filter("comments"))
  296. self.assertEqual([], get_filter("external_links"))
  297. self.assertEqual([], get_filter("headings"))
  298. self.assertEqual([], get_filter("html_entities"))
  299. self.assertEqual([], get_filter("tags"))
  300. self.assertEqual(["{{b}}", "{{f}}"], get_filter("templates"))
  301. self.assertEqual(list("abcdefg"), get_filter("text"))
  302. self.assertEqual(["[[d]]", "[[g]]"], get_filter("wikilinks"))
  303. code2 = parse("{{a|{{b}}|{{c|d={{f}}{{h}}}}}}")
  304. for func in (code2.filter, ifilter(code2)):
  305. self.assertEqual(["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"],
  306. func(recursive=False, forcetype=Template))
  307. self.assertEqual(["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}", "{{b}}",
  308. "{{c|d={{f}}{{h}}}}", "{{f}}", "{{h}}"],
  309. func(recursive=True, forcetype=Template))
  310. code3 = parse("{{foobar}}{{FOO}}{{baz}}{{bz}}{{barfoo}}")
  311. for func in (code3.filter, ifilter(code3)):
  312. self.assertEqual(["{{foobar}}", "{{barfoo}}"],
  313. func(False, matches=lambda node: "foo" in node))
  314. self.assertEqual(["{{foobar}}", "{{FOO}}", "{{barfoo}}"],
  315. func(False, matches=r"foo"))
  316. self.assertEqual(["{{foobar}}", "{{FOO}}"],
  317. func(matches=r"^{{foo.*?}}"))
  318. self.assertEqual(["{{foobar}}"],
  319. func(matches=r"^{{foo.*?}}", flags=re.UNICODE))
  320. self.assertEqual(["{{baz}}", "{{bz}}"], func(matches=r"^{{b.*?z"))
  321. self.assertEqual(["{{baz}}"], func(matches=r"^{{b.+?z}}"))
  322. exp_rec = ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}", "{{b}}",
  323. "{{c|d={{f}}{{h}}}}", "{{f}}", "{{h}}"]
  324. exp_unrec = ["{{a|{{b}}|{{c|d={{f}}{{h}}}}}}"]
  325. self.assertEqual(exp_rec, code2.filter_templates())
  326. self.assertEqual(exp_unrec, code2.filter_templates(recursive=False))
  327. self.assertEqual(exp_rec, code2.filter_templates(recursive=True))
  328. self.assertEqual(exp_rec, code2.filter_templates(True))
  329. self.assertEqual(exp_unrec, code2.filter_templates(False))
  330. self.assertEqual(["{{foobar}}"], code3.filter_templates(
  331. matches=lambda node: node.name.matches("Foobar")))
  332. self.assertEqual(["{{baz}}", "{{bz}}"],
  333. code3.filter_templates(matches=r"^{{b.*?z"))
  334. self.assertEqual([], code3.filter_tags(matches=r"^{{b.*?z"))
  335. self.assertEqual([], code3.filter_tags(matches=r"^{{b.*?z", flags=0))
  336. self.assertRaises(TypeError, code.filter_templates, a=42)
  337. self.assertRaises(TypeError, code.filter_templates, forcetype=Template)
  338. self.assertRaises(TypeError, code.filter_templates, 1, 0, 0, Template)
  339. code4 = parse("{{foo}}<b>{{foo|{{bar}}}}</b>")
  340. actual1 = code4.filter_templates(recursive=code4.RECURSE_OTHERS)
  341. actual2 = code4.filter_templates(code4.RECURSE_OTHERS)
  342. self.assertEqual(["{{foo}}", "{{foo|{{bar}}}}"], actual1)
  343. self.assertEqual(["{{foo}}", "{{foo|{{bar}}}}"], actual2)
  344. def test_get_sections(self):
  345. """test Wikicode.get_sections()"""
  346. page1 = parse("")
  347. page2 = parse("==Heading==")
  348. page3 = parse("===Heading===\nFoo bar baz\n====Gnidaeh====\n")
  349. p4_lead = "This is a lead.\n"
  350. p4_IA = "=== Section I.A ===\nSection I.A [[body]].\n"
  351. p4_IB1 = "==== Section I.B.1 ====\nSection I.B.1 body.\n\n&bull;Some content.\n\n"
  352. p4_IB = "=== Section I.B ===\n" + p4_IB1
  353. p4_I = "== Section I ==\nSection I body. {{and a|template}}\n" + p4_IA + p4_IB
  354. p4_II = "== Section II ==\nSection II body.\n\n"
  355. p4_IIIA1a = "===== Section III.A.1.a =====\nMore text.\n"
  356. p4_IIIA2ai1 = "======= Section III.A.2.a.i.1 =======\nAn invalid section!"
  357. p4_IIIA2 = "==== Section III.A.2 ====\nEven more text.\n" + p4_IIIA2ai1
  358. p4_IIIA = "=== Section III.A ===\nText.\n" + p4_IIIA1a + p4_IIIA2
  359. p4_III = "== Section III ==\n" + p4_IIIA
  360. page4 = parse(p4_lead + p4_I + p4_II + p4_III)
  361. self.assertEqual([""], page1.get_sections())
  362. self.assertEqual(["", "==Heading=="], page2.get_sections())
  363. self.assertEqual(["", "===Heading===\nFoo bar baz\n====Gnidaeh====\n",
  364. "====Gnidaeh====\n"], page3.get_sections())
  365. self.assertEqual([p4_lead, p4_I, p4_IA, p4_IB, p4_IB1, p4_II,
  366. p4_III, p4_IIIA, p4_IIIA1a, p4_IIIA2, p4_IIIA2ai1],
  367. page4.get_sections())
  368. self.assertEqual(["====Gnidaeh====\n"], page3.get_sections(levels=[4]))
  369. self.assertEqual(["===Heading===\nFoo bar baz\n====Gnidaeh====\n"],
  370. page3.get_sections(levels=(2, 3)))
  371. self.assertEqual(["===Heading===\nFoo bar baz\n"],
  372. page3.get_sections(levels=(2, 3), flat=True))
  373. self.assertEqual([], page3.get_sections(levels=[0]))
  374. self.assertEqual(["", "====Gnidaeh====\n"],
  375. page3.get_sections(levels=[4], include_lead=True))
  376. self.assertEqual(["===Heading===\nFoo bar baz\n====Gnidaeh====\n",
  377. "====Gnidaeh====\n"],
  378. page3.get_sections(include_lead=False))
  379. self.assertEqual(["===Heading===\nFoo bar baz\n", "====Gnidaeh====\n"],
  380. page3.get_sections(flat=True, include_lead=False))
  381. self.assertEqual([p4_IB1, p4_IIIA2], page4.get_sections(levels=[4]))
  382. self.assertEqual([p4_IA, p4_IB, p4_IIIA], page4.get_sections(levels=[3]))
  383. self.assertEqual([p4_IA, "=== Section I.B ===\n",
  384. "=== Section III.A ===\nText.\n"],
  385. page4.get_sections(levels=[3], flat=True))
  386. self.assertEqual(["", ""], page2.get_sections(include_headings=False))
  387. self.assertEqual(["\nSection I.B.1 body.\n\n&bull;Some content.\n\n",
  388. "\nEven more text.\n" + p4_IIIA2ai1],
  389. page4.get_sections(levels=[4],
  390. include_headings=False))
  391. self.assertEqual([], page4.get_sections(matches=r"body"))
  392. self.assertEqual([p4_I, p4_IA, p4_IB, p4_IB1],
  393. page4.get_sections(matches=r"Section\sI[.\s].*?"))
  394. self.assertEqual([p4_IA, p4_IIIA, p4_IIIA1a, p4_IIIA2, p4_IIIA2ai1],
  395. page4.get_sections(matches=r".*?a.*?"))
  396. self.assertEqual([p4_IIIA1a, p4_IIIA2ai1],
  397. page4.get_sections(matches=r".*?a.*?", flags=re.U))
  398. self.assertEqual(["\nMore text.\n", "\nAn invalid section!"],
  399. page4.get_sections(matches=r".*?a.*?", flags=re.U,
  400. include_headings=False))
  401. sections = page2.get_sections(include_headings=False)
  402. sections[0].append("Lead!\n")
  403. sections[1].append("\nFirst section!")
  404. self.assertEqual("Lead!\n==Heading==\nFirst section!", page2)
  405. page5 = parse("X\n== Foo ==\nBar\n== Baz ==\nBuzz")
  406. section = page5.get_sections(matches="Foo")[0]
  407. section.replace("\nBar\n", "\nBarf ")
  408. section.append("{{Haha}}\n")
  409. self.assertEqual("== Foo ==\nBarf {{Haha}}\n", section)
  410. self.assertEqual("X\n== Foo ==\nBarf {{Haha}}\n== Baz ==\nBuzz", page5)
  411. def test_strip_code(self):
  412. """test Wikicode.strip_code()"""
  413. # Since individual nodes have test cases for their __strip__ methods,
  414. # we're only going to do an integration test:
  415. code = parse("Foo [[bar]]\n\n{{baz|hello}}\n\n[[a|b]] &Sigma;")
  416. self.assertEqual("Foo bar\n\nb Σ",
  417. code.strip_code(normalize=True, collapse=True))
  418. self.assertEqual("Foo bar\n\n\n\nb Σ",
  419. code.strip_code(normalize=True, collapse=False))
  420. self.assertEqual("Foo bar\n\nb &Sigma;",
  421. code.strip_code(normalize=False, collapse=True))
  422. self.assertEqual("Foo bar\n\n\n\nb &Sigma;",
  423. code.strip_code(normalize=False, collapse=False))
  424. self.assertEqual("Foo bar\n\nhello\n\nb Σ",
  425. code.strip_code(normalize=True, collapse=True,
  426. keep_template_params=True))
  427. def test_get_tree(self):
  428. """test Wikicode.get_tree()"""
  429. # Since individual nodes have test cases for their __showtree___
  430. # methods, and the docstring covers all possibilities for the output of
  431. # __showtree__, we'll test it only:
  432. code = parse("Lorem ipsum {{foo|bar|{{baz}}|spam=eggs}}")
  433. expected = "Lorem ipsum \n{{\n\t foo\n\t| 1\n\t= bar\n\t| 2\n\t= " + \
  434. "{{\n\t\t\tbaz\n\t }}\n\t| spam\n\t= eggs\n}}"
  435. self.assertEqual(expected.expandtabs(4), code.get_tree())
  436. if __name__ == "__main__":
  437. unittest.main(verbosity=2)