Skip to content

Commit 6a4bfcb

Browse files
committed
fixup! Use set instead of list for block level elements
1 parent d4813dc commit 6a4bfcb

File tree

2 files changed

+375
-47
lines changed

2 files changed

+375
-47
lines changed

markdown/util.py

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import warnings
3030
from functools import wraps, lru_cache
3131
from itertools import count
32-
from typing import TYPE_CHECKING, Generic, Iterator, NamedTuple, TypeVar, TypedDict, overload
32+
from typing import TYPE_CHECKING, Callable, Generic, Iterator, NamedTuple, TypeVar, TypedDict, overload
3333

3434
if TYPE_CHECKING: # pragma: no cover
3535
from markdown import Markdown
@@ -47,26 +47,26 @@
4747
# TODO: Raise errors from list methods in the future.
4848
# Later, remove this class entirely and use a regular set.
4949
class _BlockLevelElements:
50-
def __init__(self, elements: list[str]) -> None:
50+
def __init__(self, elements: list[str], /) -> None:
5151
self._list = elements.copy()
5252
self._set = set(self._list)
5353

54-
def __add__(self, other: list[str]) -> list[str]:
54+
def __add__(self, other: list[str], /) -> list[str]:
5555
warnings.warn(
5656
"Using block level elements as a list is deprecated, use it as a set instead.",
5757
DeprecationWarning,
5858
)
5959
# Using `+` means user expects a list back.
6060
return self._list + other
6161

62-
def __and__(self, other: set[str]) -> set[str]:
62+
def __and__(self, other: set[str], /) -> set[str]:
6363
# Using `&` means user expects a set back.
6464
return self._set & other
6565

66-
def __contains__(self, item):
66+
def __contains__(self, item: str, /) -> bool:
6767
return item in self._set
6868

69-
def __delitem__(self, key):
69+
def __delitem__(self, key: int, /) -> None:
7070
warnings.warn(
7171
"Using block level elements as a list is deprecated, use it as a set instead.",
7272
DeprecationWarning,
@@ -75,33 +75,36 @@ def __delitem__(self, key):
7575
del self._list[key]
7676
self._set.remove(element)
7777

78-
def __getitem__(self, index):
78+
def __getitem__(self, index: int, /) -> str:
7979
warnings.warn(
8080
"Using block level elements as a list is deprecated, use it as a set instead.",
8181
DeprecationWarning,
8282
)
8383
return self._list[index]
8484

85-
def __iadd__(self, other: list[str]) -> None:
85+
def __iadd__(self, other: list[str], /) -> set[str]:
8686
warnings.warn(
8787
"Using block level elements as a list is deprecated, use it as a set instead.",
8888
DeprecationWarning,
8989
)
9090
# In-place addition should update both list and set.
9191
self._list += other
9292
self._set.update(set(other))
93+
return self # type: ignore[return-value]
9394

94-
def __iand__(self, other: set[str]) -> None:
95+
def __iand__(self, other: set[str], /) -> set[str]:
9596
# In-place intersection should update both list and set.
9697
self._list = [element for element in self._list if element in other]
9798
self._set &= other
99+
return self # type: ignore[return-value]
98100

99-
def __ior__(self, other: set[str]) -> None:
101+
def __ior__(self, other: set[str], /) -> set[str]:
100102
# In-place union should update both list and set.
101103
for element in other:
102104
if element not in self._set:
103105
self._list.append(element)
104106
self._set |= other
107+
return self # type: ignore[return-value]
105108

106109
def __iter__(self) -> Iterator[str]:
107110
return iter(self._list)
@@ -111,31 +114,31 @@ def __len__(self) -> int:
111114
# If used as a set, both lengths will be the same.
112115
return len(self._list)
113116

114-
def __or__(self, value: set[str]) -> set[str]:
117+
def __or__(self, value: set[str], /) -> set[str]:
115118
# Using `|` means user expects a set back.
116119
return self._set | value
117120

118-
def __rand__(self, value: set[str]) -> set[str]:
121+
def __rand__(self, value: set[str], /) -> set[str]:
119122
# Using `&` means user expects a set back.
120123
return value & self._set
121124

122-
def __ror__(self, value: set[str]) -> set[str]:
125+
def __ror__(self, value: set[str], /) -> set[str]:
123126
# Using `|` means user expects a set back.
124127
return value | self._set
125128

126-
def __rsub__(self, value: set[str]) -> set[str]:
129+
def __rsub__(self, value: set[str], /) -> set[str]:
127130
# Using `-` means user expects a set back.
128131
return value - self._set
129132

130-
def __rxor__(self, value: set[str]) -> set[str]:
133+
def __rxor__(self, value: set[str], /) -> set[str]:
131134
# Using `^` means user expects a set back.
132135
return value ^ self._set
133136

134-
def __sub__(self, value: set[str]) -> set[str]:
137+
def __sub__(self, value: set[str], /) -> set[str]:
135138
# Using `-` means user expects a set back.
136139
return self._set - value
137140

138-
def __xor__(self, value: set[str]) -> set[str]:
141+
def __xor__(self, value: set[str], /) -> set[str]:
139142
# Using `^` means user expects a set back.
140143
return self._set ^ value
141144

@@ -146,24 +149,26 @@ def __reversed__(self) -> Iterator[str]:
146149
)
147150
return reversed(self._list)
148151

149-
def __setitem__(self, key: int, value: str) -> None:
152+
def __setitem__(self, key: int, value: str, /) -> None:
150153
warnings.warn(
151154
"Using block level elements as a list is deprecated, use it as a set instead.",
152155
DeprecationWarning,
153156
)
154157
# In-place item-setting should update both list and set.
158+
old = self._list[key]
155159
self._list[key] = value
160+
self._set.discard(old)
156161
self._set.add(value)
157162

158163
def __str__(self) -> str:
159164
return str(self._set)
160165

161-
def add(self, element: str) -> None:
166+
def add(self, element: str, /) -> None:
162167
# In-place addition should update both list and set.
163168
self._set.add(element)
164169
self._list.append(element)
165170

166-
def append(self, element: str) -> None:
171+
def append(self, element: str, /) -> None:
167172
warnings.warn(
168173
"Using block level elements as a list is deprecated, use it as a set instead.",
169174
DeprecationWarning,
@@ -180,7 +185,7 @@ def copy(self) -> _BlockLevelElements:
180185
# We're not sure yet whether the user wants to use it as a set or list.
181186
return _BlockLevelElements(self._list)
182187

183-
def count(self, value: str) -> int:
188+
def count(self, value: str, /) -> int:
184189
warnings.warn(
185190
"Using block level elements as a list is deprecated, use it as a set instead.",
186191
DeprecationWarning,
@@ -193,14 +198,13 @@ def difference(self, *others: set[str]) -> set[str]:
193198
# User expects a set back.
194199
return self._set.difference(*others)
195200

196-
def difference_update(self, *others) -> None:
201+
def difference_update(self, *others: set[str]) -> None:
197202
# In-place difference should update both list and set.
198203
self._set.difference_update(*others)
199-
for other in others:
200-
for element in other:
201-
self._list.remove(element)
204+
self._list.clear()
205+
self._list.extend(sorted(self._set))
202206

203-
def discard(self, element: str) -> None:
207+
def discard(self, element: str, /) -> None:
204208
# In-place discard should update both list and set.
205209
self._set.discard(element)
206210
while True:
@@ -209,13 +213,15 @@ def discard(self, element: str) -> None:
209213
except ValueError:
210214
break
211215

212-
def extend(self, elements: list[str]) -> None:
216+
def extend(self, elements: list[str], /) -> None:
213217
warnings.warn(
214218
"Using block level elements as a list is deprecated, use it as a set instead.",
215219
DeprecationWarning,
216220
)
217221
# In-place extension should update both list and set.
218-
self._list.extend(elements)
222+
for element in elements:
223+
if element not in self._list:
224+
self._list.append(element)
219225
self._set.update(elements)
220226

221227
def index(self, value, start: int = 0, stop: int = sys.maxsize, /):
@@ -241,17 +247,16 @@ def intersection(self, *others: set[str]) -> set[str]:
241247
def intersection_update(self, *others: set[str]) -> None:
242248
# In-place intersection should update both list and set.
243249
self._set.intersection_update(*others)
244-
for element in reversed(self._list):
245-
if element not in self._set:
246-
self._list.remove(element)
250+
self._list.clear()
251+
self._list.extend(sorted(self._set))
247252

248-
def isdisjoint(self, other: set[str]) -> bool:
253+
def isdisjoint(self, other: set[str], /) -> bool:
249254
return self._set.isdisjoint(other)
250255

251-
def issubset(self, other: set[str]) -> bool:
256+
def issubset(self, other: set[str], /) -> bool:
252257
return self._set.issubset(other)
253258

254-
def issuperset(self, other: set[str]) -> bool:
259+
def issuperset(self, other: set[str], /) -> bool:
255260
return self._set.issuperset(other)
256261

257262
def pop(self, index: int = -1, /) -> str:
@@ -260,7 +265,7 @@ def pop(self, index: int = -1, /) -> str:
260265
self._set.remove(element)
261266
return element
262267

263-
def remove(self, element: str) -> None:
268+
def remove(self, element: str, /) -> None:
264269
# In-place removal should update both list and set.
265270
self._list.remove(element)
266271
self._set.remove(element)
@@ -272,25 +277,22 @@ def reverse(self) -> None:
272277
)
273278
self._list.reverse()
274279

275-
def sort(self, /, *, key=None, reverse=False) -> None:
280+
def sort(self, /, *, key: Callable | None = None, reverse: bool = False) -> None:
276281
warnings.warn(
277282
"Using block level elements as a list is deprecated, use it as a set instead.",
278283
DeprecationWarning,
279284
)
280285
self._list.sort(key=key, reverse=reverse)
281286

282-
def symmetric_difference(self, other: set[str]) -> set[str]:
287+
def symmetric_difference(self, other: set[str], /) -> set[str]:
283288
# User expects a set back.
284289
return self._set.symmetric_difference(other)
285290

286-
def symmetric_difference_update(self, other: set[str]) -> None:
291+
def symmetric_difference_update(self, other: set[str], /) -> None:
287292
# In-place symmetric difference should update both list and set.
288293
self._set.symmetric_difference_update(other)
289-
for element in other:
290-
if element in self._set:
291-
self._list.remove(element)
292-
else:
293-
self._list.append(element)
294+
self._list.clear()
295+
self._list.extend(sorted(self._set))
294296

295297
def union(self, *others: set[str]) -> set[str]:
296298
# User expects a set back.
@@ -299,10 +301,8 @@ def union(self, *others: set[str]) -> set[str]:
299301
def update(self, *others: set[str]) -> None:
300302
# In-place union should update both list and set.
301303
self._set.update(*others)
302-
for other in others:
303-
for element in other:
304-
if element not in self._set:
305-
self._list.append(element)
304+
self._list.clear()
305+
self._list.extend(sorted(self._set))
306306

307307

308308
# Type it as `set[str]` to express our intent for it to be used as such.

0 commit comments

Comments
 (0)