Skip to content

Commit 60ef803

Browse files
committed
Switched SortedList base class to Sequence
Since SortedList does not implement the MutableSequence interface (many of MutableSequence's mixin methods return NotImplemented), it should not derive from MutableSequence. This change will allow type checkers to properly reject SortedList as an argument to a function that takes MutableSequence. This is a breaking change (albeit a minor one).
1 parent 22dec80 commit 60ef803

File tree

6 files changed

+62
-65
lines changed

6 files changed

+62
-65
lines changed

docs/introduction.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,19 +166,19 @@ at an index which is not supported by :class:`SortedList`.
166166
>>> sl.reverse()
167167
Traceback (most recent call last):
168168
...
169-
NotImplementedError: use ``reversed(sl)`` instead
169+
AttributeError: use ``reversed(sl)`` instead
170170
>>> sl.append('f')
171171
Traceback (most recent call last):
172172
...
173-
NotImplementedError: use ``sl.add(value)`` instead
173+
AttributeError: use ``sl.add(value)`` instead
174174
>>> sl.extend(['f', 'g', 'h'])
175175
Traceback (most recent call last):
176176
...
177-
NotImplementedError: use ``sl.update(values)`` instead
177+
AttributeError: use ``sl.update(values)`` instead
178178
>>> sl.insert(5, 'f')
179179
Traceback (most recent call last):
180180
...
181-
NotImplementedError: use ``sl.add(value)`` instead
181+
AttributeError: use ``sl.add(value)`` instead
182182

183183
Comparison with :class:`SortedList` uses lexicographical ordering as with other
184184
sequence types.

docs/sortedlist.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ SortedList
4141
.. automethod:: __repr__
4242
.. automethod:: _check
4343
.. automethod:: _reset
44-
.. automethod:: append
45-
.. automethod:: extend
46-
.. automethod:: insert
47-
.. automethod:: reverse
4844
.. automethod:: __setitem__
4945

5046

sortedcontainers/sortedlist.py

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
###############################################################################
3131

3232
try:
33-
from collections.abc import Sequence, MutableSequence
33+
from collections.abc import Sequence
3434
except ImportError:
35-
from collections import Sequence, MutableSequence
35+
from collections import Sequence
3636

3737
from functools import wraps
3838
from sys import hexversion
@@ -82,7 +82,7 @@ def wrapper(self):
8282
###############################################################################
8383

8484

85-
class SortedList(MutableSequence):
85+
class SortedList(Sequence):
8686
"""Sorted list is a sorted mutable sequence.
8787
8888
Sorted list values are maintained in sorted order.
@@ -134,8 +134,14 @@ class SortedList(MutableSequence):
134134
Sorted lists use lexicographical ordering semantics when compared to other
135135
sequences.
136136
137-
Some methods of mutable sequences are not supported and will raise
138-
not-implemented error.
137+
.. versionchanged:: 3.0
138+
139+
SortedLists are mutable sequences but they do not implement the
140+
:class:`collections.abc.MutableSequence` interface. In version 3.0, the
141+
base class was switched to :class:`collections.abc.Sequence` and the
142+
``append``, ``extend``, ``reverse`` and ``insert`` methods, which
143+
previously raised :py:exc:`NotImplementedError` when called, were
144+
removed.
139145
140146
"""
141147
DEFAULT_LOAD_FACTOR = 1000
@@ -932,24 +938,6 @@ def __reversed__(self):
932938
return chain.from_iterable(map(reversed, reversed(self._lists)))
933939

934940

935-
def reverse(self):
936-
"""Raise not-implemented error.
937-
938-
Sorted list maintains values in ascending sort order. Values may not be
939-
reversed in-place.
940-
941-
Use ``reversed(sl)`` for an iterator over values in descending sort
942-
order.
943-
944-
Implemented to override `MutableSequence.reverse` which provides an
945-
erroneous default implementation.
946-
947-
:raises NotImplementedError: use ``reversed(sl)`` instead
948-
949-
"""
950-
raise NotImplementedError('use ``reversed(sl)`` instead')
951-
952-
953941
def islice(self, start=None, stop=None, reverse=False):
954942
"""Return an iterator that slices sorted list from `start` to `stop`.
955943
@@ -1273,38 +1261,20 @@ def copy(self):
12731261

12741262
__copy__ = copy
12751263

1264+
def __getattr__(self, key):
1265+
if key == 'append':
1266+
msg = 'use ``sl.add(value)`` instead'
1267+
elif key == 'extend':
1268+
msg = 'use ``sl.update(values)`` instead'
1269+
elif key == 'reverse':
1270+
msg = 'use ``reversed(sl)`` instead'
1271+
elif key == 'insert':
1272+
msg = 'use ``sl.add(value)`` instead'
1273+
else:
1274+
msg = "'%s' object has no attribute '%s'" % (type(self).__name__,
1275+
key)
12761276

1277-
def append(self, value):
1278-
"""Raise not-implemented error.
1279-
1280-
Implemented to override `MutableSequence.append` which provides an
1281-
erroneous default implementation.
1282-
1283-
:raises NotImplementedError: use ``sl.add(value)`` instead
1284-
1285-
"""
1286-
raise NotImplementedError('use ``sl.add(value)`` instead')
1287-
1288-
1289-
def extend(self, values):
1290-
"""Raise not-implemented error.
1291-
1292-
Implemented to override `MutableSequence.extend` which provides an
1293-
erroneous default implementation.
1294-
1295-
:raises NotImplementedError: use ``sl.update(values)`` instead
1296-
1297-
"""
1298-
raise NotImplementedError('use ``sl.update(values)`` instead')
1299-
1300-
1301-
def insert(self, index, value):
1302-
"""Raise not-implemented error.
1303-
1304-
:raises NotImplementedError: use ``sl.add(value)`` instead
1305-
1306-
"""
1307-
raise NotImplementedError('use ``sl.add(value)`` instead')
1277+
raise AttributeError(msg)
13081278

13091279

13101280
def pop(self, index=-1):

tests/test_coverage_sortedkeylist_modulo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def test_reversed():
326326

327327
def test_reverse():
328328
slt = SortedKeyList(range(10000), key=modulo)
329-
with pytest.raises(NotImplementedError):
329+
with pytest.raises(AttributeError):
330330
slt.reverse()
331331

332332
def test_islice():

tests/test_coverage_sortedkeylist_negate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def test_reversed():
281281

282282
def test_reverse():
283283
slt = SortedKeyList(range(10000), key=negate)
284-
with pytest.raises(NotImplementedError):
284+
with pytest.raises(AttributeError):
285285
slt.reverse()
286286

287287
def test_islice():

tests/test_coverage_sortedlist.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
from itertools import chain
99
import pytest
1010

11+
try:
12+
from collections import abc
13+
except ImportError:
14+
import collections as abc
15+
1116
if hexversion < 0x03000000:
1217
from itertools import izip as zip
1318
range = xrange
@@ -266,7 +271,7 @@ def test_reversed():
266271

267272
def test_reverse():
268273
slt = SortedList(range(10000))
269-
with pytest.raises(NotImplementedError):
274+
with pytest.raises(AttributeError):
270275
slt.reverse()
271276

272277
def test_islice():
@@ -604,3 +609,29 @@ def test_check():
604609
slt._len = 5
605610
with pytest.raises(AssertionError):
606611
slt._check()
612+
613+
614+
@pytest.mark.parametrize("base_class_name",
615+
["Sequence", "Reversible", "Iterable",
616+
"Sized", "Collection"])
617+
def test_abstract_base_classes(base_class_name):
618+
# Some of these were introduced in later versions of Python
619+
base_class = getattr(abc, base_class_name, None)
620+
if base_class is None:
621+
pytest.skip("%s introduced in a later version of Python" %
622+
base_class_name)
623+
624+
sl = SortedList()
625+
assert isinstance(sl, base_class)
626+
627+
628+
def test_not_mutable_sequence_instance():
629+
"""
630+
SortedList does not implement the MutableSequence interface, but has custom
631+
behavior for all the methods of a MutableSquence, so we want to make sure
632+
that it still fails an `isinstance` check.
633+
"""
634+
635+
sl = SortedList()
636+
assert not isinstance(sl, abc.MutableSequence)
637+

0 commit comments

Comments
 (0)