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.

526 lines
25KB

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