Skip to content

Commit adca84b

Browse files
committed
fixup! Use set instead of list for block level elements
1 parent 60c5355 commit adca84b

File tree

2 files changed

+168
-10
lines changed

2 files changed

+168
-10
lines changed

markdown/util.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def __delitem__(self, index: int, /) -> None:
7979
)
8080
element = self._list[index]
8181
del self._list[index]
82-
self._set.remove(element)
82+
# Only remove from set if absent from list.
83+
if element not in self._list:
84+
self._set.remove(element)
8385

8486
def __getitem__(self, index: int, /) -> str:
8587
warnings.warn(
@@ -163,7 +165,9 @@ def __setitem__(self, index: int, value: str, /) -> None:
163165
# In-place item-setting should update both list and set.
164166
old = self._list[index]
165167
self._list[index] = value
166-
self._set.discard(old)
168+
# Only remove from set if absent from list.
169+
if old not in self._list:
170+
self._set.discard(old)
167171
self._set.add(value)
168172

169173
def __str__(self) -> str:
@@ -262,16 +266,34 @@ def issubset(self, other: set[str], /) -> bool:
262266
def issuperset(self, other: set[str], /) -> bool:
263267
return self._set.issuperset(other)
264268

265-
def pop(self, index: int = -1, /) -> str:
269+
def pop(self, index: int | None = None, /) -> str:
266270
# In-place pop should update both list and set.
271+
if index is None:
272+
index = -1
273+
else:
274+
warnings.warn(
275+
"Using block level elements as a list is deprecated, use it as a set instead.",
276+
DeprecationWarning,
277+
)
267278
element = self._list.pop(index)
268-
self._set.remove(element)
279+
# Only remove from set if absent from list.
280+
if element not in self._list:
281+
self._set.remove(element)
269282
return element
270283

271284
def remove(self, element: str, /) -> None:
272285
# In-place removal should update both list and set.
273-
self._list.remove(element)
274-
self._set.remove(element)
286+
# We give precedence to set behavior, so we remove all occurrences from the list.
287+
while True:
288+
try:
289+
self._list.remove(element)
290+
except ValueError:
291+
break
292+
# We raise ValueError for backwards compatibility.
293+
try:
294+
self._set.remove(element)
295+
except KeyError:
296+
raise ValueError(f"{element!r} not in list") from None
275297

276298
def reverse(self) -> None:
277299
warnings.warn(

tests/test_block_level_elements.py

Lines changed: 140 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,25 @@ def test__init__(self):
4343
self.assertEqual(ble._list, [])
4444
self.assertEqual(ble._set, set())
4545

46+
def test__init__duplicates(self):
47+
ble = _BlockLevelElements(["a", "a", "b"])
48+
self.assertEqual(ble._list, ["a", "a", "b"])
49+
self.assertEqual(ble._set, {"a", "b"})
50+
4651
def test___add__(self):
47-
ble = _BlockLevelElements(["a", "b", ])
52+
ble = _BlockLevelElements(["a", "b"])
4853
with self.assertWarns(DeprecationWarning):
4954
ble2 = ble + ["c", "d"]
5055
self.assertIsInstance(ble2, list)
5156
self.assertEqual(ble2, ["a", "b", "c", "d"])
5257

58+
def test___add__duplicates(self):
59+
ble = _BlockLevelElements(["a", "b"])
60+
with self.assertWarns(DeprecationWarning):
61+
ble2 = ble + ["a", "b"]
62+
self.assertIsInstance(ble2, list)
63+
self.assertEqual(ble2, ["a", "b", "a", "b"])
64+
5365
def test___and__(self):
5466
ble = _BlockLevelElements(["a", "b"])
5567
ble2 = ble & {"b", "c"}
@@ -74,6 +86,13 @@ def test___delitem__(self):
7486
with self.assertWarns(DeprecationWarning):
7587
self.assertRaises(IndexError, ble.__delitem__, 10)
7688

89+
def test___delitem__duplicates(self):
90+
ble = _BlockLevelElements(["a", "a", "b"])
91+
with self.assertWarns(DeprecationWarning):
92+
del ble[0]
93+
self.assertEqual(ble._list, ["a", "b"])
94+
self.assertEqual(ble._set, {"a", "b"})
95+
7796
def test___getitem__(self):
7897
ble = _BlockLevelElements(["a", "b", "c"])
7998
with self.assertWarns(DeprecationWarning):
@@ -82,13 +101,27 @@ def test___getitem__(self):
82101
self.assertEqual(ble[2], "c")
83102
self.assertRaises(IndexError, ble.__getitem__, 10)
84103

104+
def test___getitem__duplicates(self):
105+
ble = _BlockLevelElements(["a", "a", "b"])
106+
with self.assertWarns(DeprecationWarning):
107+
self.assertEqual(ble[0], "a")
108+
self.assertEqual(ble[1], "a")
109+
self.assertEqual(ble[2], "b")
110+
85111
def test___iadd__(self):
86112
ble = _BlockLevelElements(["a", "b"])
87113
with self.assertWarns(DeprecationWarning):
88114
ble += ["c", "d"]
89115
self.assertEqual(ble._list, ["a", "b", "c", "d"])
90116
self.assertEqual(ble._set, {"a", "b", "c", "d"})
91117

118+
def test___iadd__duplicates(self):
119+
ble = _BlockLevelElements(["a", "b"])
120+
with self.assertWarns(DeprecationWarning):
121+
ble += ["a", "b"]
122+
self.assertEqual(ble._list, ["a", "b", "a", "b"])
123+
self.assertEqual(ble._set, {"a", "b"})
124+
92125
def test___iand__(self):
93126
ble = _BlockLevelElements(["a", "b"])
94127
ble &= {"b", "c"}
@@ -105,11 +138,19 @@ def test___iter__(self):
105138
ble = _BlockLevelElements(["a", "b", "c"])
106139
self.assertEqual(tuple(ble), ("a", "b", "c"))
107140

141+
def test___iter__duplicates(self):
142+
ble = _BlockLevelElements(["a", "a", "b"])
143+
self.assertEqual(tuple(ble), ("a", "a", "b"))
144+
108145
def test___len__(self):
109146
self.assertEqual(len(_BlockLevelElements([])), 0)
110-
self.assertEqual(len(_BlockLevelElements(["a", "a"])), 2)
147+
self.assertEqual(len(_BlockLevelElements(["a", "b"])), 2)
111148
self.assertEqual(len(_BlockLevelElements(["a", "b", "c"])), 3)
112149

150+
def test___len__duplicates(self):
151+
self.assertEqual(len(_BlockLevelElements(["a", "a"])), 2)
152+
self.assertEqual(len(_BlockLevelElements(["a", "a", "b"])), 3)
153+
113154
def test___or__(self):
114155
ble = _BlockLevelElements(["a", "b"])
115156
ble2 = ble | {"b", "c"}
@@ -157,6 +198,11 @@ def test___reversed__(self):
157198
with self.assertWarns(DeprecationWarning):
158199
self.assertEqual(tuple(reversed(ble)), ("c", "b", "a"))
159200

201+
def test___reversed__duplicates(self):
202+
ble = _BlockLevelElements(["a", "a", "b"])
203+
with self.assertWarns(DeprecationWarning):
204+
self.assertEqual(tuple(reversed(ble)), ("b", "a", "a"))
205+
160206
def test___setitem__(self):
161207
ble = _BlockLevelElements(["a", "b", "c"])
162208
with self.assertWarns(DeprecationWarning):
@@ -170,6 +216,17 @@ def test___setitem__(self):
170216
with self.assertWarns(DeprecationWarning):
171217
self.assertRaises(IndexError, ble.__setitem__, 10, "f")
172218

219+
def test___setitem__duplicates(self):
220+
ble = _BlockLevelElements(["a", "a", "b"])
221+
with self.assertWarns(DeprecationWarning):
222+
ble[0] = "b"
223+
self.assertEqual(ble._list, ["b", "a", "b"])
224+
self.assertEqual(ble._set, {"a", "b"})
225+
with self.assertWarns(DeprecationWarning):
226+
ble[1] = "b"
227+
self.assertEqual(ble._list, ["b", "b", "b"])
228+
self.assertEqual(ble._set, {"b"})
229+
173230
def test___str__(self):
174231
ble = _BlockLevelElements(["a"])
175232
self.assertEqual(str(ble), "{'a'}")
@@ -180,13 +237,26 @@ def test_add(self):
180237
self.assertEqual(ble._list, ["a", "b", "c"])
181238
self.assertEqual(ble._set, {"a", "b", "c"})
182239

240+
def test_add_duplicates(self):
241+
ble = _BlockLevelElements(["a", "b"])
242+
ble.add("a")
243+
self.assertEqual(ble._list, ["a", "b", "a"])
244+
self.assertEqual(ble._set, {"a", "b"})
245+
183246
def test_append(self):
184247
ble = _BlockLevelElements(["a", "b"])
185248
with self.assertWarns(DeprecationWarning):
186249
ble.append("c")
187250
self.assertEqual(ble._list, ["a", "b", "c"])
188251
self.assertEqual(ble._set, {"a", "b", "c"})
189252

253+
def test_append_duplicates(self):
254+
ble = _BlockLevelElements(["a", "b"])
255+
with self.assertWarns(DeprecationWarning):
256+
ble.append("a")
257+
self.assertEqual(ble._list, ["a", "b", "a"])
258+
self.assertEqual(ble._set, {"a", "b"})
259+
190260
def test_clear(self):
191261
ble = _BlockLevelElements(["a", "b"])
192262
ble.clear()
@@ -200,13 +270,25 @@ def test_copy(self):
200270
self.assertEqual(ble2._list, ["a", "b"])
201271
self.assertEqual(ble2._set, {"a", "b"})
202272

273+
def test_copy_duplicates(self):
274+
ble = _BlockLevelElements(["a", "a"])
275+
ble2 = ble.copy()
276+
self.assertIsNot(ble2, ble)
277+
self.assertEqual(ble2._list, ["a", "a"])
278+
self.assertEqual(ble2._set, {"a"})
279+
203280
def test_count(self):
204-
ble = _BlockLevelElements(["a", "b", "a"])
281+
ble = _BlockLevelElements(["a", "b"])
205282
with self.assertWarns(DeprecationWarning):
206-
self.assertEqual(ble.count("a"), 2)
283+
self.assertEqual(ble.count("a"), 1)
207284
self.assertEqual(ble.count("b"), 1)
208285
self.assertEqual(ble.count("c"), 0)
209286

287+
def test_count_duplicates(self):
288+
ble = _BlockLevelElements(["a", "a"])
289+
with self.assertWarns(DeprecationWarning):
290+
self.assertEqual(ble.count("a"), 2)
291+
210292
def test_difference(self):
211293
ble = _BlockLevelElements(["a", "b"])
212294
ble2 = ble.difference({"b", "c"})
@@ -222,6 +304,7 @@ def test_difference_update(self):
222304
def test_discard(self):
223305
ble = _BlockLevelElements(["a", "b"])
224306
ble.discard("b")
307+
ble.discard("b") # Assert no error.
225308
self.assertEqual(ble._list, ["a"])
226309
self.assertEqual(ble._set, {"a"})
227310

@@ -232,6 +315,13 @@ def test_extend(self):
232315
self.assertEqual(ble._list, ["a", "b", "c", "d"])
233316
self.assertEqual(ble._set, {"a", "b", "c", "d"})
234317

318+
def test_extend_duplicates(self):
319+
ble = _BlockLevelElements(["a", "b"])
320+
with self.assertWarns(DeprecationWarning):
321+
ble.extend(["a", "b"])
322+
self.assertEqual(ble._list, ["a", "b", "a", "b"])
323+
self.assertEqual(ble._set, {"a", "b"})
324+
235325
def test_index(self):
236326
ble = _BlockLevelElements(["a", "b", "c"])
237327
with self.assertWarns(DeprecationWarning):
@@ -240,6 +330,13 @@ def test_index(self):
240330
self.assertEqual(ble.index("c"), 2)
241331
self.assertRaises(ValueError, ble.index, "d")
242332

333+
def test_index_duplicates(self):
334+
ble = _BlockLevelElements(["a", "b", "b"])
335+
with self.assertWarns(DeprecationWarning):
336+
self.assertEqual(ble.index("b"), 1)
337+
with self.assertWarns(DeprecationWarning):
338+
self.assertEqual(ble.index("b", 2), 2)
339+
243340
def test_insert(self):
244341
ble = _BlockLevelElements(["a", "b", "c"])
245342
with self.assertWarns(DeprecationWarning):
@@ -250,6 +347,13 @@ def test_insert(self):
250347
ble.insert(100, "e")
251348
self.assertIn("e", ble)
252349

350+
def test_insert_duplicates(self):
351+
ble = _BlockLevelElements(["a", "a", "b"])
352+
with self.assertWarns(DeprecationWarning):
353+
ble.insert(1, "b")
354+
self.assertEqual(ble._list, ["a", "b", "a", "b"])
355+
self.assertEqual(ble._set, {"a", "b"})
356+
253357
def test_intersection(self):
254358
ble = _BlockLevelElements(["a", "b"])
255359
ble2 = ble.intersection({"b", "c"})
@@ -288,27 +392,59 @@ def test_pop(self):
288392
self.assertEqual(ble._set, {"b"})
289393
self.assertRaises(IndexError, ble.pop, 10)
290394

395+
def test_pop_duplicates(self):
396+
ble = _BlockLevelElements(["a", "a", "b", "b"])
397+
self.assertEqual(ble.pop(), "b")
398+
self.assertEqual(ble._list, ["a", "a", "b"])
399+
self.assertEqual(ble._set, {"a", "b"})
400+
self.assertEqual(ble.pop(0), "a")
401+
self.assertEqual(ble._list, ["a", "b"])
402+
self.assertEqual(ble._set, {"a", "b"})
403+
self.assertEqual(ble.pop(), "b")
404+
self.assertEqual(ble._list, ["a"])
405+
self.assertEqual(ble._set, {"a"})
406+
291407
def test_remove(self):
292408
ble = _BlockLevelElements(["a", "b", "c"])
293409
ble.remove("b")
294410
self.assertEqual(ble._list, ["a", "c"])
295411
self.assertEqual(ble._set, {"a", "c"})
296412
self.assertRaises(ValueError, ble.remove, "d")
297413

414+
def test_remove_duplicates(self):
415+
ble = _BlockLevelElements(["a", "a", "b"])
416+
ble.remove("a")
417+
self.assertEqual(ble._list, ["b"])
418+
self.assertEqual(ble._set, {"b"})
419+
298420
def test_reverse(self):
299421
ble = _BlockLevelElements(["a", "b", "c"])
300422
with self.assertWarns(DeprecationWarning):
301423
ble.reverse()
302424
self.assertEqual(ble._list, ["c", "b", "a"])
303425
self.assertEqual(ble._set, {"a", "b", "c"})
304426

427+
def test_reverse_duplicates(self):
428+
ble = _BlockLevelElements(["a", "a", "b"])
429+
with self.assertWarns(DeprecationWarning):
430+
ble.reverse()
431+
self.assertEqual(ble._list, ["b", "a", "a"])
432+
self.assertEqual(ble._set, {"a", "b"})
433+
305434
def test_sort(self):
306435
ble = _BlockLevelElements(["c", "a", "b"])
307436
with self.assertWarns(DeprecationWarning):
308437
ble.sort()
309438
self.assertEqual(ble._list, ["a", "b", "c"])
310439
self.assertEqual(ble._set, {"a", "b", "c"})
311440

441+
def test_sort_duplicates(self):
442+
ble = _BlockLevelElements(["b", "a", "b"])
443+
with self.assertWarns(DeprecationWarning):
444+
ble.sort()
445+
self.assertEqual(ble._list, ["a", "b", "b"])
446+
self.assertEqual(ble._set, {"a", "b"})
447+
312448
def test_symmetric_difference(self):
313449
ble = _BlockLevelElements(["a", "b"])
314450
ble2 = ble.symmetric_difference({"b", "c"})

0 commit comments

Comments
 (0)