2424
2525from __future__ import annotations
2626
27- from collections .abc import Callable
2827import re
2928import sys
3029import warnings
4544"""
4645
4746
48- class _BlockLevelElements (set ):
49- # ----------------------------------
50- # Methods common to `list` and `set`
51- # ----------------------------------
52- def copy (self ) -> set [str ]:
53- return _BlockLevelElements (super ().copy ())
47+ # TODO: Raise errors from list methods in the future.
48+ # Later, remove this class entirely and use a regular set.
49+ class _BlockLevelElements :
50+ def __init__ (self , elements : list [str ]) -> None :
51+ self ._list = elements .copy ()
52+ self ._set = set (self ._list )
53+
54+ def __add__ (self , other : list [str ]) -> list [str ]:
55+ warnings .warn (
56+ "Using block level elements as a list is deprecated, use it as a set instead." ,
57+ DeprecationWarning ,
58+ )
59+ # Using `+` means user expects a list back.
60+ return self ._list + other
61+
62+ def __and__ (self , other : set [str ]) -> set [str ]:
63+ # Using `&` means user expects a set back.
64+ return self ._set & other
65+
66+ def __contains__ (self , item ):
67+ return item in self ._set
68+
69+ def __delitem__ (self , key ):
70+ warnings .warn (
71+ "Using block level elements as a list is deprecated, use it as a set instead." ,
72+ DeprecationWarning ,
73+ )
74+ element = self ._list [key ]
75+ del self ._list [key ]
76+ self ._set .remove (element )
77+
78+ def __getitem__ (self , index ):
79+ warnings .warn (
80+ "Using block level elements as a list is deprecated, use it as a set instead." ,
81+ DeprecationWarning ,
82+ )
83+ return self ._list [index ]
84+
85+ def __iadd__ (self , other : list [str ]) -> None :
86+ warnings .warn (
87+ "Using block level elements as a list is deprecated, use it as a set instead." ,
88+ DeprecationWarning ,
89+ )
90+ # In-place addition should update both list and set.
91+ self ._list += other
92+ self ._set .update (set (other ))
93+
94+ def __iand__ (self , other : set [str ]) -> None :
95+ # In-place intersection should update both list and set.
96+ self ._list = [element for element in self ._list if element in other ]
97+ self ._set &= other
98+
99+ def __ior__ (self , other : set [str ]) -> None :
100+ # In-place union should update both list and set.
101+ for element in other :
102+ if element not in self ._set :
103+ self ._list .append (element )
104+ self ._set |= other
105+
106+ def __iter__ (self ) -> Iterator [str ]:
107+ return iter (self ._list )
54108
55- def pop (self , index : int | None = None , / ) -> str :
56- if index is not None :
57- warnings .warn ("The index argument is deprecated and will be removed in the future" , DeprecationWarning )
58- try :
59- return self .pop ()
60- except KeyError :
61- warnings .warn ("`pop` will raise a `KeyError` in the future" , DeprecationWarning )
62- raise IndexError ("pop from an empty set" ) from None
109+ def __len__ (self ) -> int :
110+ # Length of the list, for backwards compatibility.
111+ # If used as a set, both lengths will be the same.
112+ return len (self ._list )
113+
114+ def __or__ (self , value : set [str ]) -> set [str ]:
115+ # Using `|` means user expects a set back.
116+ return self ._set | value
117+
118+ def __rand__ (self , value : set [str ]) -> set [str ]:
119+ # Using `&` means user expects a set back.
120+ return value & self ._set
121+
122+ def __ror__ (self , value : set [str ]) -> set [str ]:
123+ # Using `|` means user expects a set back.
124+ return value | self ._set
125+
126+ def __rsub__ (self , value : set [str ]) -> set [str ]:
127+ # Using `-` means user expects a set back.
128+ return value - self ._set
129+
130+ def __rxor__ (self , value : set [str ]) -> set [str ]:
131+ # Using `^` means user expects a set back.
132+ return value ^ self ._set
133+
134+ def __sub__ (self , value : set [str ]) -> set [str ]:
135+ # Using `-` means user expects a set back.
136+ return self ._set - value
137+
138+ def __xor__ (self , value : set [str ]) -> set [str ]:
139+ # Using `^` means user expects a set back.
140+ return self ._set ^ value
141+
142+ def __reversed__ (self ) -> Iterator [str ]:
143+ warnings .warn (
144+ "Using block level elements as a list is deprecated, use it as a set instead." ,
145+ DeprecationWarning ,
146+ )
147+ return reversed (self ._list )
148+
149+ def __setitem__ (self , key : int , value : str ) -> None :
150+ warnings .warn (
151+ "Using block level elements as a list is deprecated, use it as a set instead." ,
152+ DeprecationWarning ,
153+ )
154+ # In-place item-setting should update both list and set.
155+ self ._list [key ] = value
156+ self ._set .add (value )
157+
158+ def __str__ (self ) -> str :
159+ return str (self ._set )
160+
161+ def add (self , element : str ) -> None :
162+ # In-place addition should update both list and set.
163+ self ._set .add (element )
164+ self ._list .append (element )
63165
64- def remove (self , element : object ) -> None :
65- try :
66- return self .remove (element )
67- except KeyError :
68- warnings .warn ("`remove` will raise a `KeyError` in the future" , DeprecationWarning )
69- raise ValueError (f"{ element !r} not in set" ) from None
70-
71- # --------------------------
72- # Methods specific to `list`
73- # --------------------------
74166 def append (self , element : str ) -> None :
75- warnings .warn ("method `append` will be removed in the future" , DeprecationWarning )
76- self .add (element )
167+ warnings .warn (
168+ "Using block level elements as a list is deprecated, use it as a set instead." ,
169+ DeprecationWarning ,
170+ )
171+ # In-place addition should update both list and set.
172+ self ._list .append (element )
173+ self ._set .add (element )
174+
175+ def clear (self ) -> None :
176+ self ._list .clear ()
177+ self ._set .clear ()
178+
179+ def copy (self ) -> _BlockLevelElements :
180+ # We're not sure yet whether the user wants to use it as a set or list.
181+ return _BlockLevelElements (self ._list )
77182
78183 def count (self , value : str ) -> int :
79- warnings .warn ("method `count` will be removed in the future" , DeprecationWarning )
80- return 1 if value in self else 0
184+ warnings .warn (
185+ "Using block level elements as a list is deprecated, use it as a set instead." ,
186+ DeprecationWarning ,
187+ )
188+ # Count in list, for backwards compatibility.
189+ # If used as a set, both counts will be the same (1).
190+ return self ._list .count (value )
191+
192+ def difference (self , * others : set [str ]) -> set [str ]:
193+ # User expects a set back.
194+ return self ._set .difference (* others )
195+
196+ def difference_update (self , * others ) -> None :
197+ # In-place difference should update both list and set.
198+ self ._set .difference_update (* others )
199+ for other in others :
200+ for element in other :
201+ self ._list .remove (element )
202+
203+ def discard (self , element : str ) -> None :
204+ # In-place discard should update both list and set.
205+ self ._set .discard (element )
206+ while True :
207+ try :
208+ self ._list .remove (element )
209+ except ValueError :
210+ break
81211
82212 def extend (self , elements : list [str ]) -> None :
83- warnings .warn ("method `extend` will be removed in the future" , DeprecationWarning )
84- self .update (elements )
85-
86- def index (self , value , start = 0 , stop = 0 , / ) -> int :
87- warnings .warn ("method `index` will be removed in the future" , DeprecationWarning )
88- return 0 if value in self else - 1
89-
90- def insert (self , index : int , element : str ) -> None :
91- warnings .warn ("method `insert` will be removed in the future" , DeprecationWarning )
92- self .add (element )
213+ warnings .warn (
214+ "Using block level elements as a list is deprecated, use it as a set instead." ,
215+ DeprecationWarning ,
216+ )
217+ # In-place extension should update both list and set.
218+ self ._list .extend (elements )
219+ self ._set .update (elements )
220+
221+ def index (self , value , start : int = 0 , stop : int = sys .maxsize , / ):
222+ warnings .warn (
223+ "Using block level elements as a list is deprecated, use it as a set instead." ,
224+ DeprecationWarning ,
225+ )
226+ return self ._list .index (value , start , stop )
227+
228+ def insert (self , index : int , element : str , / ) -> None :
229+ warnings .warn (
230+ "Using block level elements as a list is deprecated, use it as a set instead." ,
231+ DeprecationWarning ,
232+ )
233+ # In-place insertion should update both list and set.
234+ self ._list .insert (index , element )
235+ self ._set .add (element )
236+
237+ def intersection (self , * others : set [str ]) -> set [str ]:
238+ # User expects a set back.
239+ return self ._set .intersection (* others )
240+
241+ def intersection_update (self , * others : set [str ]) -> None :
242+ # In-place intersection should update both list and set.
243+ self ._set .intersection_update (* others )
244+ for element in reversed (self ._list ):
245+ if element not in self ._set :
246+ self ._list .remove (element )
247+
248+ def isdisjoint (self , other : set [str ]) -> bool :
249+ return self ._set .isdisjoint (other )
250+
251+ def issubset (self , other : set [str ]) -> bool :
252+ return self ._set .issubset (other )
253+
254+ def issuperset (self , other : set [str ]) -> bool :
255+ return self ._set .issuperset (other )
256+
257+ def pop (self , index : int = - 1 , / ) -> str :
258+ # In-place pop should update both list and set.
259+ element = self ._list .pop (index )
260+ self ._set .remove (element )
261+ return element
262+
263+ def remove (self , element : str ) -> None :
264+ # In-place removal should update both list and set.
265+ self ._list .remove (element )
266+ self ._set .remove (element )
93267
94268 def reverse (self ) -> None :
95- warnings .warn ("method `reverse` will be removed in the future" , DeprecationWarning )
96-
97- def sort (self , / , * , key : Callable | None = None , reverse : bool = False ) -> None :
98- warnings .warn ("method `sort` will be removed in the future" , DeprecationWarning )
99-
100-
101- BLOCK_LEVEL_ELEMENTS : set [str ] = _BlockLevelElements ({
269+ warnings .warn (
270+ "Using block level elements as a list is deprecated, use it as a set instead." ,
271+ DeprecationWarning ,
272+ )
273+ self ._list .reverse ()
274+
275+ def sort (self , / , * , key = None , reverse = False ) -> None :
276+ warnings .warn (
277+ "Using block level elements as a list is deprecated, use it as a set instead." ,
278+ DeprecationWarning ,
279+ )
280+ self ._list .sort (key = key , reverse = reverse )
281+
282+ def symmetric_difference (self , other : set [str ]) -> set [str ]:
283+ # User expects a set back.
284+ return self ._set .symmetric_difference (other )
285+
286+ def symmetric_difference_update (self , other : set [str ]) -> None :
287+ # In-place symmetric difference should update both list and set.
288+ 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+
295+ def union (self , * others : set [str ]) -> set [str ]:
296+ # User expects a set back.
297+ return self ._set .union (* others )
298+
299+ def update (self , * others : set [str ]) -> None :
300+ # In-place union should update both list and set.
301+ 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 )
306+
307+
308+ # Type it as `set[str]` to express our intent for it to be used as such.
309+ BLOCK_LEVEL_ELEMENTS : set [str ] = _BlockLevelElements ([
102310 # Elements which are invalid to wrap in a `<p>` tag.
103311 # See https://w3c.github.io/html/grouping-content.html#the-p-element
104312 'address' , 'article' , 'aside' , 'blockquote' , 'details' , 'div' , 'dl' ,
@@ -110,7 +318,7 @@ def sort(self, /, *, key: Callable | None = None, reverse: bool = False) -> None
110318 'math' , 'map' , 'noscript' , 'output' , 'object' , 'option' , 'progress' , 'script' ,
111319 'style' , 'summary' , 'tbody' , 'td' , 'textarea' , 'tfoot' , 'th' , 'thead' , 'tr' , 'video' ,
112320 'center'
113- })
321+ ]) # type: ignore[assignment]
114322"""
115323Set of HTML tags which get treated as block-level elements. Same as the `block_level_elements`
116324attribute of the [`Markdown`][markdown.Markdown] class. Generally one should use the
0 commit comments