A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

463 linhas
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 SmartList class and its child, ListProxy.
  22. """
  23. from __future__ import annotations
  24. import pickle
  25. import pytest
  26. from mwparserfromhell.smart_list import SmartList
  27. from mwparserfromhell.smart_list.list_proxy import ListProxy
  28. def _test_get_set_del_item(builder):
  29. """Run tests on __get/set/delitem__ of a list built with *builder*."""
  30. list1 = builder([0, 1, 2, 3, "one", "two"])
  31. list2 = builder(list(range(10)))
  32. assert 1 == list1[1]
  33. assert "one" == list1[-2]
  34. assert [2, 3] == list1[2:4]
  35. with pytest.raises(IndexError):
  36. list1[6]
  37. with pytest.raises(IndexError):
  38. list1[-7]
  39. assert [0, 1, 2] == list1[:3]
  40. assert [0, 1, 2, 3, "one", "two"] == list1[:]
  41. assert [3, "one", "two"] == list1[3:]
  42. assert [3, "one", "two"] == list1[3:100]
  43. assert ["one", "two"] == list1[-2:]
  44. assert [0, 1] == list1[:-4]
  45. assert [] == list1[6:]
  46. assert [] == list1[4:2]
  47. assert [0, 2, "one"] == list1[0:5:2]
  48. assert [0, 2] == list1[0:-3:2]
  49. assert [0, 1, 2, 3, "one", "two"] == list1[::]
  50. assert [2, 3, "one", "two"] == list1[2::]
  51. assert [0, 1, 2, 3] == list1[:4:]
  52. assert [2, 3] == list1[2:4:]
  53. assert [0, 2, 4, 6, 8] == list2[::2]
  54. assert [2, 5, 8] == list2[2::3]
  55. assert [0, 3] == list2[:6:3]
  56. assert [2, 5, 8] == list2[-8:9:3]
  57. assert [] == list2[100000:1000:-100]
  58. list1[3] = 100
  59. assert 100 == list1[3]
  60. list1[-3] = 101
  61. assert [0, 1, 2, 101, "one", "two"] == list1
  62. list1[5:] = [6, 7, 8]
  63. assert [6, 7, 8] == list1[5:]
  64. assert [0, 1, 2, 101, "one", 6, 7, 8] == list1
  65. list1[2:4] = [-1, -2, -3, -4, -5]
  66. assert [0, 1, -1, -2, -3, -4, -5, "one", 6, 7, 8] == list1
  67. list1[0:-3] = [99]
  68. assert [99, 6, 7, 8] == list1
  69. list2[0:6:2] = [100, 102, 104]
  70. assert [100, 1, 102, 3, 104, 5, 6, 7, 8, 9] == list2
  71. list2[::3] = [200, 203, 206, 209]
  72. assert [200, 1, 102, 203, 104, 5, 206, 7, 8, 209] == list2
  73. list2[::] = range(7)
  74. assert [0, 1, 2, 3, 4, 5, 6] == list2
  75. with pytest.raises(ValueError):
  76. list2[0:5:2] = [100, 102, 104, 106]
  77. with pytest.raises(IndexError):
  78. list2[7] = "foo"
  79. with pytest.raises(IndexError):
  80. list2[-8] = "foo"
  81. del list2[2]
  82. assert [0, 1, 3, 4, 5, 6] == list2
  83. del list2[-3]
  84. assert [0, 1, 3, 5, 6] == list2
  85. with pytest.raises(IndexError):
  86. del list2[100]
  87. with pytest.raises(IndexError):
  88. del list2[-6]
  89. list2[:] = range(10)
  90. del list2[3:6]
  91. assert [0, 1, 2, 6, 7, 8, 9] == list2
  92. del list2[-2:]
  93. assert [0, 1, 2, 6, 7] == list2
  94. del list2[:2]
  95. assert [2, 6, 7] == list2
  96. list2[:] = range(10)
  97. del list2[2:8:2]
  98. assert [0, 1, 3, 5, 7, 8, 9] == list2
  99. def _test_add_radd_iadd(builder):
  100. """Run tests on __r/i/add__ of a list built with *builder*."""
  101. list1 = builder(range(5))
  102. list2 = builder(range(5, 10))
  103. assert [0, 1, 2, 3, 4, 5, 6] == list1 + [5, 6]
  104. assert [0, 1, 2, 3, 4] == list1
  105. assert list(range(10)) == list1 + list2
  106. assert [-2, -1, 0, 1, 2, 3, 4], [-2, -1] + list1
  107. assert [0, 1, 2, 3, 4] == list1
  108. list1 += ["foo", "bar", "baz"]
  109. assert [0, 1, 2, 3, 4, "foo", "bar", "baz"] == list1
  110. def _test_other_magic_methods(builder):
  111. """Run tests on other magic methods of a list built with *builder*."""
  112. list1 = builder([0, 1, 2, 3, "one", "two"])
  113. list2 = builder([])
  114. list3 = builder([0, 2, 3, 4])
  115. list4 = builder([0, 1, 2])
  116. assert "[0, 1, 2, 3, 'one', 'two']" == str(list1)
  117. assert b"\x00\x01\x02" == bytes(list4)
  118. assert "[0, 1, 2, 3, 'one', 'two']" == repr(list1)
  119. assert list1 < list3
  120. assert list1 <= list3
  121. assert list1 != list3
  122. assert list1 != list3
  123. assert list1 <= list3
  124. assert list1 < list3
  125. other1 = [0, 2, 3, 4]
  126. assert list1 < other1
  127. assert list1 <= other1
  128. assert list1 != other1
  129. assert list1 != other1
  130. assert list1 <= other1
  131. assert list1 < other1
  132. other2 = [0, 0, 1, 2]
  133. assert list1 >= other2
  134. assert list1 > other2
  135. assert list1 != other2
  136. assert list1 != other2
  137. assert list1 > other2
  138. assert list1 >= other2
  139. other3 = [0, 1, 2, 3, "one", "two"]
  140. assert list1 >= other3
  141. assert list1 <= other3
  142. assert list1 == other3
  143. assert list1 == other3
  144. assert list1 <= other3
  145. assert list1 >= other3
  146. assert bool(list1) is True
  147. assert bool(list2) is False
  148. assert 6 == len(list1)
  149. assert 0 == len(list2)
  150. out = []
  151. for obj in list1:
  152. out.append(obj)
  153. assert [0, 1, 2, 3, "one", "two"] == out
  154. out = []
  155. for ch in list2:
  156. out.append(ch)
  157. assert [] == out
  158. gen1 = iter(list1)
  159. out = []
  160. for _ in range(len(list1)):
  161. out.append(next(gen1))
  162. with pytest.raises(StopIteration):
  163. next(gen1)
  164. assert [0, 1, 2, 3, "one", "two"] == out
  165. gen2 = iter(list2)
  166. with pytest.raises(StopIteration):
  167. next(gen2)
  168. assert ["two", "one", 3, 2, 1, 0] == list(reversed(list1))
  169. assert [] == list(reversed(list2))
  170. assert "one" in list1
  171. assert 3 in list1
  172. assert 10 not in list1
  173. assert 0 not in list2
  174. assert [] == list2 * 5
  175. assert [] == 5 * list2
  176. assert [0, 1, 2, 0, 1, 2, 0, 1, 2] == list4 * 3
  177. assert [0, 1, 2, 0, 1, 2, 0, 1, 2] == 3 * list4
  178. list4 *= 2
  179. assert [0, 1, 2, 0, 1, 2] == list4
  180. def _test_list_methods(builder):
  181. """Run tests on the public methods of a list built with *builder*."""
  182. list1 = builder(range(5))
  183. list2 = builder(["foo"])
  184. list3 = builder([("a", 5), ("d", 2), ("b", 8), ("c", 3)])
  185. list1.append(5)
  186. list1.append(1)
  187. list1.append(2)
  188. assert [0, 1, 2, 3, 4, 5, 1, 2] == list1
  189. assert 0 == list1.count(6)
  190. assert 2 == list1.count(1)
  191. list1.extend(range(5, 8))
  192. assert [0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7] == list1
  193. assert 1 == list1.index(1)
  194. assert 6 == list1.index(1, 3)
  195. assert 6 == list1.index(1, 3, 7)
  196. with pytest.raises(ValueError):
  197. list1.index(1, 3, 5)
  198. list1.insert(0, -1)
  199. assert [-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7] == list1
  200. list1.insert(-1, 6.5)
  201. assert [-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7] == list1
  202. list1.insert(13, 8)
  203. assert [-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7, 8] == list1
  204. assert 8 == list1.pop()
  205. assert 7 == list1.pop()
  206. assert [-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5] == list1
  207. assert -1 == list1.pop(0)
  208. assert 5 == list1.pop(5)
  209. assert 6.5 == list1.pop(-1)
  210. assert [0, 1, 2, 3, 4, 1, 2, 5, 6] == list1
  211. assert "foo" == list2.pop()
  212. with pytest.raises(IndexError):
  213. list2.pop()
  214. assert [] == list2
  215. list1.remove(6)
  216. assert [0, 1, 2, 3, 4, 1, 2, 5] == list1
  217. list1.remove(1)
  218. assert [0, 2, 3, 4, 1, 2, 5] == list1
  219. list1.remove(1)
  220. assert [0, 2, 3, 4, 2, 5] == list1
  221. with pytest.raises(ValueError):
  222. list1.remove(1)
  223. list1.reverse()
  224. assert [5, 2, 4, 3, 2, 0] == list1
  225. list1.sort()
  226. assert [0, 2, 2, 3, 4, 5] == list1
  227. list1.sort(reverse=True)
  228. assert [5, 4, 3, 2, 2, 0] == list1
  229. list3.sort(key=lambda i: i[1])
  230. assert [("d", 2), ("c", 3), ("a", 5), ("b", 8)] == list3
  231. list3.sort(key=lambda i: i[1], reverse=True)
  232. assert [("b", 8), ("a", 5), ("c", 3), ("d", 2)] == list3
  233. def _dispatch_test_for_children(meth):
  234. """Run a test method on various different types of children."""
  235. meth(lambda L: SmartList(list(L))[:])
  236. meth(lambda L: SmartList([999] + list(L))[1:])
  237. meth(lambda L: SmartList(list(L) + [999])[:-1])
  238. meth(lambda L: SmartList([101, 102] + list(L) + [201, 202])[2:-2])
  239. def test_docs():
  240. """make sure the methods of SmartList/ListProxy have docstrings"""
  241. methods = [
  242. "append",
  243. "count",
  244. "extend",
  245. "index",
  246. "insert",
  247. "pop",
  248. "remove",
  249. "reverse",
  250. "sort",
  251. ]
  252. for meth in methods:
  253. expected = getattr(list, meth).__doc__
  254. smartlist_doc = getattr(SmartList, meth).__doc__
  255. listproxy_doc = getattr(ListProxy, meth).__doc__
  256. assert expected == smartlist_doc
  257. assert expected == listproxy_doc
  258. def test_doctest():
  259. """make sure the test embedded in SmartList's docstring passes"""
  260. parent = SmartList([0, 1, 2, 3])
  261. assert [0, 1, 2, 3] == parent
  262. child = parent[2:]
  263. assert [2, 3] == child
  264. child.append(4)
  265. assert [2, 3, 4] == child
  266. assert [0, 1, 2, 3, 4] == parent
  267. def test_parent_get_set_del():
  268. """make sure SmartList's getitem/setitem/delitem work"""
  269. _test_get_set_del_item(SmartList)
  270. def test_parent_add():
  271. """make sure SmartList's add/radd/iadd work"""
  272. _test_add_radd_iadd(SmartList)
  273. def test_parent_other_magics():
  274. """make sure SmartList's other magically implemented features work"""
  275. _test_other_magic_methods(SmartList)
  276. def test_parent_methods():
  277. """make sure SmartList's non-magic methods work, like append()"""
  278. _test_list_methods(SmartList)
  279. def test_child_get_set_del():
  280. """make sure ListProxy's getitem/setitem/delitem work"""
  281. _dispatch_test_for_children(_test_get_set_del_item)
  282. def test_child_add():
  283. """make sure ListProxy's add/radd/iadd work"""
  284. _dispatch_test_for_children(_test_add_radd_iadd)
  285. def test_child_other_magics():
  286. """make sure ListProxy's other magically implemented features work"""
  287. _dispatch_test_for_children(_test_other_magic_methods)
  288. def test_child_methods():
  289. """make sure ListProxy's non-magic methods work, like append()"""
  290. _dispatch_test_for_children(_test_list_methods)
  291. def test_influence():
  292. """make sure changes are propagated from parents to children"""
  293. parent = SmartList([0, 1, 2, 3, 4, 5])
  294. child1 = parent[2:]
  295. child2 = parent[2:5]
  296. assert [0, 1, 2, 3, 4, 5] == parent
  297. assert [2, 3, 4, 5] == child1
  298. assert [2, 3, 4] == child2
  299. assert 2 == len(parent._children)
  300. parent.append(6)
  301. child1.append(7)
  302. child2.append(4.5)
  303. assert [0, 1, 2, 3, 4, 4.5, 5, 6, 7] == parent
  304. assert [2, 3, 4, 4.5, 5, 6, 7] == child1
  305. assert [2, 3, 4, 4.5] == child2
  306. parent.insert(0, -1)
  307. parent.insert(4, 2.5)
  308. parent.insert(10, 6.5)
  309. assert [-1, 0, 1, 2, 2.5, 3, 4, 4.5, 5, 6, 6.5, 7] == parent
  310. assert [2, 2.5, 3, 4, 4.5, 5, 6, 6.5, 7] == child1
  311. assert [2, 2.5, 3, 4, 4.5] == child2
  312. assert 7 == parent.pop()
  313. assert 6.5 == child1.pop()
  314. assert 4.5 == child2.pop()
  315. assert [-1, 0, 1, 2, 2.5, 3, 4, 5, 6] == parent
  316. assert [2, 2.5, 3, 4, 5, 6] == child1
  317. assert [2, 2.5, 3, 4] == child2
  318. parent.remove(-1)
  319. child1.remove(2.5)
  320. assert [0, 1, 2, 3, 4, 5, 6] == parent
  321. assert [2, 3, 4, 5, 6] == child1
  322. assert [2, 3, 4] == child2
  323. assert 0 == parent.pop(0)
  324. assert [1, 2, 3, 4, 5, 6] == parent
  325. assert [2, 3, 4, 5, 6] == child1
  326. assert [2, 3, 4] == child2
  327. child2.reverse()
  328. assert [1, 4, 3, 2, 5, 6] == parent
  329. assert [4, 3, 2, 5, 6] == child1
  330. assert [4, 3, 2] == child2
  331. parent.extend([7, 8])
  332. child1.extend([8.1, 8.2])
  333. child2.extend([1.9, 1.8])
  334. assert [1, 4, 3, 2, 1.9, 1.8, 5, 6, 7, 8, 8.1, 8.2] == parent
  335. assert [4, 3, 2, 1.9, 1.8, 5, 6, 7, 8, 8.1, 8.2] == child1
  336. assert [4, 3, 2, 1.9, 1.8] == child2
  337. child3 = parent[9:]
  338. assert [8, 8.1, 8.2] == child3
  339. del parent[8:]
  340. assert [1, 4, 3, 2, 1.9, 1.8, 5, 6] == parent
  341. assert [4, 3, 2, 1.9, 1.8, 5, 6] == child1
  342. assert [4, 3, 2, 1.9, 1.8] == child2
  343. assert [] == child3
  344. assert 0 == len(child3)
  345. del child1
  346. assert [1, 4, 3, 2, 1.9, 1.8, 5, 6] == parent
  347. assert [4, 3, 2, 1.9, 1.8] == child2
  348. assert [] == child3
  349. assert 2 == len(parent._children)
  350. del child3
  351. assert [1, 4, 3, 2, 1.9, 1.8, 5, 6] == parent
  352. assert [4, 3, 2, 1.9, 1.8] == child2
  353. assert 1 == len(parent._children)
  354. parent.remove(1.9)
  355. parent.remove(1.8)
  356. assert [1, 4, 3, 2, 5, 6] == parent
  357. assert [4, 3, 2] == child2
  358. parent.reverse()
  359. assert [6, 5, 2, 3, 4, 1] == parent
  360. assert [4, 3, 2] == child2
  361. assert 0 == len(parent._children)
  362. @pytest.mark.parametrize("protocol", range(pickle.HIGHEST_PROTOCOL + 1))
  363. def test_pickling(protocol: int):
  364. """test SmartList objects behave properly when pickling"""
  365. parent = SmartList([0, 1, 2, 3, 4, 5])
  366. enc = pickle.dumps(parent, protocol=protocol)
  367. assert pickle.loads(enc) == parent
  368. child = parent[1:3]
  369. assert len(parent._children) == 1
  370. assert list(parent._children.values())[0][0]() is child
  371. enc = pickle.dumps(parent, protocol=protocol)
  372. parent2 = pickle.loads(enc)
  373. assert parent2 == parent
  374. assert parent2._children == {}
  375. enc = pickle.dumps(child, protocol=protocol)
  376. child2 = pickle.loads(enc)
  377. assert child2 == child
  378. assert child2._parent == parent
  379. assert child2._parent is not parent
  380. assert len(child2._parent._children) == 1
  381. assert list(child2._parent._children.values())[0][0]() is child2