Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Python 3.11 and 3.12 #217

Merged
merged 5 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ jobs:
max-parallel: 4
matrix:
os: [ubuntu-latest]
python-version: [3.7, 3.8, 3.9, '3.10']
python-version: [3.7, 3.8, 3.9, '3.10', 3.11, 3.12]

steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true

- name: Install tox
run: |
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Features
- Feature-rich (e.g. get the five largest keys in a sorted dict: d.keys()[-5:])
- Pragmatic design (e.g. SortedSet is a Python set with a SortedList index)
- Developed on Python 3.10
- Tested with CPython 3.7, 3.8, 3.9, 3.10 and PyPy3
- Tested with CPython 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 and PyPy3
- Tested on Linux, Mac OSX, and Windows

.. image:: https://github.com/grantjenks/python-sortedcontainers/workflows/integration/badge.svg
Expand Down
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def run_tests(self):
long_description=readme,
author='Grant Jenks',
author_email='[email protected]',
url='http://www.grantjenks.com/docs/sortedcontainers/',
url='https://www.grantjenks.com/docs/sortedcontainers/',
license='Apache 2.0',
package_dir={'': 'src'},
packages=['sortedcontainers'],
Expand All @@ -48,6 +48,8 @@ def run_tests(self):
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
],
Expand Down
50 changes: 7 additions & 43 deletions src/sortedcontainers/sorteddict.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,16 @@

"""

import sys
import warnings

from collections.abc import (
ItemsView, KeysView, Mapping, ValuesView, Sequence
)
from itertools import chain

from .sortedlist import SortedList, recursive_repr
from .sortedset import SortedSet

###############################################################################
# BEGIN Python 2/3 Shims
###############################################################################

try:
from collections.abc import (
ItemsView, KeysView, Mapping, ValuesView, Sequence
)
except ImportError:
from collections import ItemsView, KeysView, Mapping, ValuesView, Sequence

###############################################################################
# END Python 2/3 Shims
###############################################################################


class SortedDict(dict):
"""Sorted dict is a sorted mutable mapping.
Expand Down Expand Up @@ -383,30 +370,7 @@ def values(self):
"""
return SortedValuesView(self)


if sys.hexversion < 0x03000000:
def __make_raise_attributeerror(original, alternate):
# pylint: disable=no-self-argument
message = (
'SortedDict.{original}() is not implemented.'
' Use SortedDict.{alternate}() instead.'
).format(original=original, alternate=alternate)
def method(self):
# pylint: disable=missing-docstring,unused-argument
raise AttributeError(message)
method.__name__ = original # pylint: disable=non-str-assignment-to-dunder-name
method.__doc__ = message
return property(method)

iteritems = __make_raise_attributeerror('iteritems', 'items')
iterkeys = __make_raise_attributeerror('iterkeys', 'keys')
itervalues = __make_raise_attributeerror('itervalues', 'values')
viewitems = __make_raise_attributeerror('viewitems', 'items')
viewkeys = __make_raise_attributeerror('viewkeys', 'keys')
viewvalues = __make_raise_attributeerror('viewvalues', 'values')


class _NotGiven(object):
class _NotGiven:
# pylint: disable=too-few-public-methods
def __repr__(self):
return '<not-given>'
Expand Down Expand Up @@ -600,10 +564,10 @@ def __repr__(self):
"""
_key = self._key
type_name = type(self).__name__
key_arg = '' if _key is None else '{0!r}, '.format(_key)
item_format = '{0!r}: {1!r}'.format
key_arg = '' if _key is None else f'{_key!r}, '
item_format = '{!r}: {!r}'.format
items = ', '.join(item_format(key, self[key]) for key in self._list)
return '{0}({1}{{{2}}})'.format(type_name, key_arg, items)
return f'{type_name}({key_arg}{{{items}}})'


def _check(self):
Expand Down
102 changes: 24 additions & 78 deletions src/sortedcontainers/sortedlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,72 +14,18 @@

"""
# pylint: disable=too-many-lines
from __future__ import print_function

import sys
import traceback

from bisect import bisect_left, bisect_right, insort
from collections.abc import Sequence, MutableSequence
from functools import reduce
from itertools import chain, repeat, starmap
from math import log
from operator import add, eq, ne, gt, ge, lt, le, iadd
from textwrap import dedent

###############################################################################
# BEGIN Python 2/3 Shims
###############################################################################

try:
from collections.abc import Sequence, MutableSequence
except ImportError:
from collections import Sequence, MutableSequence

from functools import wraps
from sys import hexversion

if hexversion < 0x03000000:
from itertools import imap as map # pylint: disable=redefined-builtin
from itertools import izip as zip # pylint: disable=redefined-builtin
try:
from thread import get_ident
except ImportError:
from dummy_thread import get_ident
else:
from functools import reduce
try:
from _thread import get_ident
except ImportError:
from _dummy_thread import get_ident


def recursive_repr(fillvalue='...'):
"Decorator to make a repr function return fillvalue for a recursive call."
# pylint: disable=missing-docstring
# Copied from reprlib in Python 3
# https://hg.python.org/cpython/file/3.6/Lib/reprlib.py

def decorating_function(user_function):
repr_running = set()

@wraps(user_function)
def wrapper(self):
key = id(self), get_ident()
if key in repr_running:
return fillvalue
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result

return wrapper

return decorating_function

###############################################################################
# END Python 2/3 Shims
###############################################################################
from reprlib import recursive_repr


class SortedList(MutableSequence):
Expand Down Expand Up @@ -446,20 +392,20 @@ def remove(self, value):
_maxes = self._maxes

if not _maxes:
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')

pos = bisect_left(_maxes, value)

if pos == len(_maxes):
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')

_lists = self._lists
idx = bisect_left(_lists[pos], value)

if _lists[pos][idx] == value:
self._delete(pos, idx)
else:
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')


def _delete(self, pos, idx):
Expand Down Expand Up @@ -1407,7 +1353,7 @@ def index(self, value, start=None, stop=None):
_len = self._len

if not _len:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

if start is None:
start = 0
Expand All @@ -1424,19 +1370,19 @@ def index(self, value, start=None, stop=None):
stop = _len

if stop <= start:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

_maxes = self._maxes
pos_left = bisect_left(_maxes, value)

if pos_left == len(_maxes):
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

_lists = self._lists
idx_left = bisect_left(_lists[pos_left], value)

if _lists[pos_left][idx_left] != value:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

stop -= 1
left = self._loc(pos_left, idx_left)
Expand All @@ -1450,7 +1396,7 @@ def index(self, value, start=None, stop=None):
if start <= right:
return start

raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')


def __add__(self, other):
Expand Down Expand Up @@ -1566,7 +1512,7 @@ def comparer(self, other):
return seq_op(self_len, len_other)

seq_op_name = seq_op.__name__
comparer.__name__ = '__{0}__'.format(seq_op_name)
comparer.__name__ = f'__{seq_op_name}__'
doc_str = """Return true if and only if sorted list is {0} `other`.

``sl.__{1}__(other)`` <==> ``sl {2} other``
Expand Down Expand Up @@ -1606,7 +1552,7 @@ def __repr__(self):
:return: string representation

"""
return '{0}({1!r})'.format(type(self).__name__, list(self))
return f'{type(self).__name__}({list(self)!r})'


def _check(self):
Expand Down Expand Up @@ -2022,13 +1968,13 @@ def remove(self, value):
_maxes = self._maxes

if not _maxes:
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')

key = self._key(value)
pos = bisect_left(_maxes, key)

if pos == len(_maxes):
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')

_lists = self._lists
_keys = self._keys
Expand All @@ -2038,15 +1984,15 @@ def remove(self, value):

while True:
if _keys[pos][idx] != key:
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')
if _lists[pos][idx] == value:
self._delete(pos, idx)
return
idx += 1
if idx == len_sublist:
pos += 1
if pos == len_keys:
raise ValueError('{0!r} not in list'.format(value))
raise ValueError(f'{value!r} not in list')
len_sublist = len(_keys[pos])
idx = 0

Expand Down Expand Up @@ -2443,7 +2389,7 @@ def index(self, value, start=None, stop=None):
_len = self._len

if not _len:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

if start is None:
start = 0
Expand All @@ -2460,14 +2406,14 @@ def index(self, value, start=None, stop=None):
stop = _len

if stop <= start:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

_maxes = self._maxes
key = self._key(value)
pos = bisect_left(_maxes, key)

if pos == len(_maxes):
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')

stop -= 1
_lists = self._lists
Expand All @@ -2478,7 +2424,7 @@ def index(self, value, start=None, stop=None):

while True:
if _keys[pos][idx] != key:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')
if _lists[pos][idx] == value:
loc = self._loc(pos, idx)
if start <= loc <= stop:
Expand All @@ -2489,11 +2435,11 @@ def index(self, value, start=None, stop=None):
if idx == len_sublist:
pos += 1
if pos == len_keys:
raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')
len_sublist = len(_keys[pos])
idx = 0

raise ValueError('{0!r} is not in list'.format(value))
raise ValueError(f'{value!r} is not in list')


def __add__(self, other):
Expand Down Expand Up @@ -2557,7 +2503,7 @@ def __repr__(self):

"""
type_name = type(self).__name__
return '{0}({1!r}, key={2!r})'.format(type_name, list(self), self._key)
return f'{type_name}({list(self)!r}, key={self._key!r})'


def _check(self):
Expand Down
Loading
Loading