A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

436 рядки
14 KiB

  1. # Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. # SOFTWARE.
  20. """
  21. Test cases for the Tag node.
  22. """
  23. from __future__ import annotations
  24. import pytest
  25. from mwparserfromhell.nodes import Tag, Template, Text
  26. from mwparserfromhell.nodes.extras import Attribute
  27. from .conftest import assert_wikicode_equal, wrap, wraptext
  28. def agen(name, value):
  29. return Attribute(wraptext(name), wraptext(value))
  30. def agennv(name):
  31. return Attribute(wraptext(name))
  32. def agennq(name, value):
  33. return Attribute(wraptext(name), wraptext(value), None)
  34. def agenp(name, v, a, b, c):
  35. return Attribute(wraptext(name), v, '"', a, b, c)
  36. def agenpnv(name, a, b, c):
  37. return Attribute(wraptext(name), None, '"', a, b, c)
  38. def test_str():
  39. """test Tag.__str__()"""
  40. node1 = Tag(wraptext("ref"))
  41. node2 = Tag(wraptext("span"), wraptext("foo"), [agen("style", "color: red;")])
  42. node3 = Tag(
  43. wraptext("ref"),
  44. attrs=[agennq("name", "foo"), agenpnv("some_attr", " ", "", "")],
  45. self_closing=True,
  46. )
  47. node4 = Tag(wraptext("br"), self_closing=True, padding=" ")
  48. node5 = Tag(wraptext("br"), self_closing=True, implicit=True)
  49. node6 = Tag(wraptext("br"), self_closing=True, invalid=True, implicit=True)
  50. node7 = Tag(wraptext("br"), self_closing=True, invalid=True, padding=" ")
  51. node8 = Tag(wraptext("hr"), wiki_markup="----", self_closing=True)
  52. node9 = Tag(wraptext("i"), wraptext("italics!"), wiki_markup="''")
  53. assert "<ref></ref>" == str(node1)
  54. assert '<span style="color: red;">foo</span>' == str(node2)
  55. assert "<ref name=foo some_attr/>" == str(node3)
  56. assert "<br />" == str(node4)
  57. assert "<br>" == str(node5)
  58. assert "</br>" == str(node6)
  59. assert "</br />" == str(node7)
  60. assert "----" == str(node8)
  61. assert "''italics!''" == str(node9)
  62. def test_children():
  63. """test Tag.__children__()"""
  64. # <ref>foobar</ref>
  65. node1 = Tag(wraptext("ref"), wraptext("foobar"))
  66. # '''bold text'''
  67. node2 = Tag(wraptext("b"), wraptext("bold text"), wiki_markup="'''")
  68. # <img id="foo" class="bar" selected />
  69. node3 = Tag(
  70. wraptext("img"),
  71. attrs=[agen("id", "foo"), agen("class", "bar"), agennv("selected")],
  72. self_closing=True,
  73. padding=" ",
  74. )
  75. gen1 = node1.__children__()
  76. gen2 = node2.__children__()
  77. gen3 = node3.__children__()
  78. assert node1.tag == next(gen1)
  79. assert node3.tag == next(gen3)
  80. assert node3.attributes[0].name == next(gen3)
  81. assert node3.attributes[0].value == next(gen3)
  82. assert node3.attributes[1].name == next(gen3)
  83. assert node3.attributes[1].value == next(gen3)
  84. assert node3.attributes[2].name == next(gen3)
  85. assert node1.contents == next(gen1)
  86. assert node2.contents == next(gen2)
  87. assert node1.closing_tag == next(gen1)
  88. with pytest.raises(StopIteration):
  89. next(gen1)
  90. with pytest.raises(StopIteration):
  91. next(gen2)
  92. with pytest.raises(StopIteration):
  93. next(gen3)
  94. def test_strip():
  95. """test Tag.__strip__()"""
  96. node1 = Tag(wraptext("i"), wraptext("foobar"))
  97. node2 = Tag(wraptext("math"), wraptext("foobar"))
  98. node3 = Tag(wraptext("br"), self_closing=True)
  99. assert "foobar" == node1.__strip__()
  100. assert node2.__strip__() is None
  101. assert node3.__strip__() is None
  102. def test_showtree():
  103. """test Tag.__showtree__()"""
  104. output = []
  105. getter, marker = object(), object()
  106. def get(code):
  107. return output.append((getter, code))
  108. def mark():
  109. return output.append(marker)
  110. node1 = Tag(
  111. wraptext("ref"), wraptext("text"), [agen("name", "foo"), agennv("selected")]
  112. )
  113. node2 = Tag(wraptext("br"), self_closing=True, padding=" ")
  114. node3 = Tag(
  115. wraptext("br"), self_closing=True, invalid=True, implicit=True, padding=" "
  116. )
  117. node1.__showtree__(output.append, get, mark)
  118. node2.__showtree__(output.append, get, mark)
  119. node3.__showtree__(output.append, get, mark)
  120. valid = [
  121. "<",
  122. (getter, node1.tag),
  123. (getter, node1.attributes[0].name),
  124. " = ",
  125. marker,
  126. (getter, node1.attributes[0].value),
  127. (getter, node1.attributes[1].name),
  128. ">",
  129. (getter, node1.contents),
  130. "</",
  131. (getter, node1.closing_tag),
  132. ">",
  133. "<",
  134. (getter, node2.tag),
  135. "/>",
  136. "</",
  137. (getter, node3.tag),
  138. ">",
  139. ]
  140. assert valid == output
  141. def test_tag():
  142. """test getter/setter for the tag attribute"""
  143. tag = wraptext("ref")
  144. node = Tag(tag, wraptext("text"))
  145. assert tag is node.tag
  146. assert tag is node.closing_tag
  147. node.tag = "span"
  148. assert_wikicode_equal(wraptext("span"), node.tag)
  149. assert_wikicode_equal(wraptext("span"), node.closing_tag)
  150. assert "<span>text</span>" == node
  151. def test_contents():
  152. """test getter/setter for the contents attribute"""
  153. contents = wraptext("text")
  154. node = Tag(wraptext("ref"), contents)
  155. assert contents is node.contents
  156. node.contents = "text and a {{template}}"
  157. parsed = wrap([Text("text and a "), Template(wraptext("template"))])
  158. assert_wikicode_equal(parsed, node.contents)
  159. assert "<ref>text and a {{template}}</ref>" == node
  160. def test_attributes():
  161. """test getter for the attributes attribute"""
  162. attrs = [agen("name", "bar")]
  163. node1 = Tag(wraptext("ref"), wraptext("foo"))
  164. node2 = Tag(wraptext("ref"), wraptext("foo"), attrs)
  165. assert [] == node1.attributes
  166. assert attrs is node2.attributes
  167. def test_wiki_markup():
  168. """test getter/setter for the wiki_markup attribute"""
  169. node = Tag(wraptext("i"), wraptext("italic text"))
  170. assert None is node.wiki_markup
  171. node.wiki_markup = "''"
  172. assert "''" == node.wiki_markup
  173. assert "''italic text''" == node
  174. node.wiki_markup = None
  175. assert node.wiki_markup is None
  176. assert "<i>italic text</i>" == node
  177. def test_self_closing():
  178. """test getter/setter for the self_closing attribute"""
  179. node = Tag(wraptext("ref"), wraptext("foobar"))
  180. assert node.self_closing is False
  181. node.self_closing = True
  182. assert node.self_closing is True
  183. assert "<ref/>" == node
  184. node.self_closing = False
  185. assert node.self_closing is False
  186. assert "<ref>foobar</ref>" == node
  187. def test_invalid():
  188. """test getter/setter for the invalid attribute"""
  189. node = Tag(wraptext("br"), self_closing=True, implicit=True)
  190. assert node.invalid is False
  191. node.invalid = True
  192. assert node.invalid is True
  193. assert "</br>" == node
  194. node.invalid = False
  195. assert node.invalid is False
  196. assert "<br>" == node
  197. def test_implicit():
  198. """test getter/setter for the implicit attribute"""
  199. node = Tag(wraptext("br"), self_closing=True)
  200. assert node.implicit is False
  201. node.implicit = True
  202. assert node.implicit is True
  203. assert "<br>" == node
  204. node.implicit = False
  205. assert node.implicit is False
  206. assert "<br/>" == node
  207. def test_padding():
  208. """test getter/setter for the padding attribute"""
  209. node = Tag(wraptext("ref"), wraptext("foobar"))
  210. assert "" == node.padding
  211. node.padding = " "
  212. assert " " == node.padding
  213. assert "<ref >foobar</ref>" == node
  214. node.padding = None
  215. assert "" == node.padding
  216. assert "<ref>foobar</ref>" == node
  217. with pytest.raises(ValueError):
  218. node.__setattr__("padding", True)
  219. def test_closing_tag():
  220. """test getter/setter for the closing_tag attribute"""
  221. tag = wraptext("ref")
  222. node = Tag(tag, wraptext("foobar"))
  223. assert tag is node.closing_tag
  224. node.closing_tag = "ref {{ignore me}}"
  225. parsed = wrap([Text("ref "), Template(wraptext("ignore me"))])
  226. assert_wikicode_equal(parsed, node.closing_tag)
  227. assert "<ref>foobar</ref {{ignore me}}>" == node
  228. def test_wiki_style_separator():
  229. """test getter/setter for wiki_style_separator attribute"""
  230. node = Tag(wraptext("table"), wraptext("\n"))
  231. assert None is node.wiki_style_separator
  232. node.wiki_style_separator = "|"
  233. assert "|" == node.wiki_style_separator
  234. node.wiki_markup = "{"
  235. assert "{|\n{" == node
  236. node2 = Tag(wraptext("table"), wraptext("\n"), wiki_style_separator="|")
  237. assert "|" == node2.wiki_style_separator
  238. def test_closing_wiki_markup():
  239. """test getter/setter for closing_wiki_markup attribute"""
  240. node = Tag(wraptext("table"), wraptext("\n"))
  241. assert None is node.closing_wiki_markup
  242. node.wiki_markup = "{|"
  243. assert "{|" == node.closing_wiki_markup
  244. node.closing_wiki_markup = "|}"
  245. assert "|}" == node.closing_wiki_markup
  246. assert "{|\n|}" == node
  247. node.wiki_markup = "!!"
  248. assert "|}" == node.closing_wiki_markup
  249. assert "!!\n|}" == node
  250. node.wiki_markup = None
  251. assert node.closing_wiki_markup is None
  252. assert "<table>\n</table>" == node
  253. node2 = Tag(
  254. wraptext("table"),
  255. wraptext("\n"),
  256. attrs=[agen("id", "foo")],
  257. wiki_markup="{|",
  258. closing_wiki_markup="|}",
  259. )
  260. assert "|}" == node2.closing_wiki_markup
  261. assert '{| id="foo"\n|}' == node2
  262. def test_has():
  263. """test Tag.has()"""
  264. node = Tag(wraptext("ref"), wraptext("cite"), [agen("name", "foo")])
  265. assert node.has("name") is True
  266. assert node.has(" name ") is True
  267. assert node.has(wraptext("name")) is True
  268. assert node.has("Name") is False
  269. assert node.has("foo") is False
  270. attrs = [
  271. agen("id", "foo"),
  272. agenp("class", "bar", " ", "\n", "\n"),
  273. agen("foo", "bar"),
  274. agenpnv("foo", " ", " \n ", " \t"),
  275. ]
  276. node2 = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  277. assert node2.has("id") is True
  278. assert node2.has("class") is True
  279. assert (
  280. node2.has(attrs[1].pad_first + str(attrs[1].name) + attrs[1].pad_before_eq)
  281. is True
  282. )
  283. assert node2.has(attrs[3]) is True
  284. assert node2.has(str(attrs[3])) is True
  285. assert node2.has("idclass") is False
  286. assert node2.has("id class") is False
  287. assert node2.has("id=foo") is False
  288. def test_get():
  289. """test Tag.get()"""
  290. attrs = [agen("name", "foo")]
  291. node = Tag(wraptext("ref"), wraptext("cite"), attrs)
  292. assert attrs[0] is node.get("name")
  293. assert attrs[0] is node.get(" name ")
  294. assert attrs[0] is node.get(wraptext("name"))
  295. with pytest.raises(ValueError):
  296. node.get("Name")
  297. with pytest.raises(ValueError):
  298. node.get("foo")
  299. attrs = [
  300. agen("id", "foo"),
  301. agenp("class", "bar", " ", "\n", "\n"),
  302. agen("foo", "bar"),
  303. agenpnv("foo", " ", " \n ", " \t"),
  304. ]
  305. node2 = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  306. assert attrs[0] is node2.get("id")
  307. assert attrs[1] is node2.get("class")
  308. assert attrs[1] is node2.get(
  309. attrs[1].pad_first + str(attrs[1].name) + attrs[1].pad_before_eq
  310. )
  311. assert attrs[3] is node2.get(attrs[3])
  312. assert attrs[3] is node2.get(str(attrs[3]))
  313. assert attrs[3] is node2.get(" foo")
  314. with pytest.raises(ValueError):
  315. node2.get("idclass")
  316. with pytest.raises(ValueError):
  317. node2.get("id class")
  318. with pytest.raises(ValueError):
  319. node2.get("id=foo")
  320. def test_add():
  321. """test Tag.add()"""
  322. node = Tag(wraptext("ref"), wraptext("cite"))
  323. node.add("name", "value")
  324. node.add("name", "value", quotes=None)
  325. node.add("name", "value", quotes="'")
  326. node.add("name")
  327. node.add(1, False)
  328. node.add("style", "{{foobar}}")
  329. node.add("name", "value", '"', "\n", " ", " ")
  330. attr1 = ' name="value"'
  331. attr2 = " name=value"
  332. attr3 = " name='value'"
  333. attr4 = " name"
  334. attr5 = ' 1="False"'
  335. attr6 = ' style="{{foobar}}"'
  336. attr7 = '\nname = "value"'
  337. assert attr1 == node.attributes[0]
  338. assert attr2 == node.attributes[1]
  339. assert attr3 == node.attributes[2]
  340. assert attr4 == node.attributes[3]
  341. assert attr5 == node.attributes[4]
  342. assert attr6 == node.attributes[5]
  343. assert attr7 == node.attributes[6]
  344. assert attr7 == node.get("name")
  345. assert_wikicode_equal(
  346. wrap([Template(wraptext("foobar"))]), node.attributes[5].value
  347. )
  348. assert (
  349. "".join(
  350. ("<ref", attr1, attr2, attr3, attr4, attr5, attr6, attr7, ">cite</ref>")
  351. )
  352. == node
  353. )
  354. with pytest.raises(ValueError):
  355. node.add("name", "foo", quotes="bar")
  356. with pytest.raises(ValueError):
  357. node.add("name", "a bc d", quotes=None)
  358. def test_remove():
  359. """test Tag.remove()"""
  360. attrs = [
  361. agen("id", "foo"),
  362. agenp("class", "bar", " ", "\n", "\n"),
  363. agen("foo", "bar"),
  364. agenpnv("foo", " ", " \n ", " \t"),
  365. ]
  366. node = Tag(wraptext("div"), attrs=attrs, self_closing=True)
  367. node.remove("class")
  368. assert '<div id="foo" foo="bar" foo \n />' == node
  369. node.remove("foo")
  370. assert '<div id="foo"/>' == node
  371. with pytest.raises(ValueError):
  372. node.remove("foo")
  373. node.remove("id")
  374. assert "<div/>" == node