From e7bce336fc9325026d3690d20855e2a6f9543819 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 9 Feb 2023 17:32:00 +0200 Subject: [PATCH 1/5] Add support for Python 3.11 and test 3.12-dev --- .github/workflows/integration.yml | 2 +- README.rst | 2 +- setup.py | 1 + tox.ini | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 9d47cd9..eb0e20c 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -40,7 +40,7 @@ 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-dev] steps: - uses: actions/checkout@v3 diff --git a/README.rst b/README.rst index 225d47a..0c371e8 100644 --- a/README.rst +++ b/README.rst @@ -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 diff --git a/setup.py b/setup.py index 2203137..5ea47de 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ 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 :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], diff --git a/tox.ini b/tox.ini index 7337af9..4849bbd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py37,py38,py39,p310,pypy3,pylint +envlist=py{37,38,39,310,311,312,py3},pylint skip_missing_interpreters=True [testenv] From 50f59e86f30449fdee3a60df350ee5db83fb5d3c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 9 Feb 2023 17:45:02 +0200 Subject: [PATCH 2/5] Remove redudant code for EOL Python versions --- src/sortedcontainers/sorteddict.py | 42 ++------------- src/sortedcontainers/sortedlist.py | 59 ++------------------- src/sortedcontainers/sortedset.py | 14 +---- tests/benchmark.py | 22 +++----- tests/benchmark_sortedset.py | 3 -- tests/test_coverage_sorteddict.py | 21 +------- tests/test_coverage_sortedkeylist_modulo.py | 6 --- tests/test_coverage_sortedkeylist_negate.py | 7 +-- tests/test_coverage_sortedlist.py | 5 -- tests/test_coverage_sortedset.py | 6 --- tests/test_stress_sorteddict.py | 13 ----- tests/test_stress_sortedkeylist.py | 5 -- tests/test_stress_sortedlist.py | 5 -- tests/test_stress_sortedset.py | 6 --- 14 files changed, 16 insertions(+), 198 deletions(-) diff --git a/src/sortedcontainers/sorteddict.py b/src/sortedcontainers/sorteddict.py index 320b861..f5e9df3 100644 --- a/src/sortedcontainers/sorteddict.py +++ b/src/sortedcontainers/sorteddict.py @@ -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. @@ -383,29 +370,6 @@ 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): # pylint: disable=too-few-public-methods def __repr__(self): diff --git a/src/sortedcontainers/sortedlist.py b/src/sortedcontainers/sortedlist.py index e3b58eb..d48cd76 100644 --- a/src/sortedcontainers/sortedlist.py +++ b/src/sortedcontainers/sortedlist.py @@ -20,66 +20,13 @@ 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): diff --git a/src/sortedcontainers/sortedset.py b/src/sortedcontainers/sortedset.py index be2b899..dfa3f27 100644 --- a/src/sortedcontainers/sortedset.py +++ b/src/sortedcontainers/sortedset.py @@ -13,25 +13,13 @@ """ +from collections.abc import MutableSet, Sequence, Set from itertools import chain from operator import eq, ne, gt, ge, lt, le from textwrap import dedent from .sortedlist import SortedList, recursive_repr -############################################################################### -# BEGIN Python 2/3 Shims -############################################################################### - -try: - from collections.abc import MutableSet, Sequence, Set -except ImportError: - from collections import MutableSet, Sequence, Set - -############################################################################### -# END Python 2/3 Shims -############################################################################### - class SortedSet(MutableSet, Sequence): """Sorted set is a sorted mutable set. diff --git a/tests/benchmark.py b/tests/benchmark.py index a267702..f3396d5 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -1,20 +1,10 @@ -import sys -from sys import hexversion +import argparse +import random import logging +import time +from collections import OrderedDict -import time, random, argparse -from functools import partial -try: - from collections import OrderedDict -except: - from ordereddict import OrderedDict - -if hexversion < 0x03000000: - range = xrange - name_attr = 'func_name' -else: - name_attr = '__name__' def detail(*values, **kwargs): if not args.bare: @@ -58,11 +48,11 @@ def ctor_load(): times.append(measure(test, func, size)) times.sort() - print(getattr(test, name_attr), name + args.suffix, size, times[0], + print(getattr(test, '__name__'), name + args.suffix, size, times[0], times[-1], times[2], sum(times) / len(times)) def register_test(func): - tests[getattr(func, name_attr)] = func + tests[getattr(func, '__name__')] = func return func def limit(test, kind, value): diff --git a/tests/benchmark_sortedset.py b/tests/benchmark_sortedset.py index d560bb5..312d1bb 100644 --- a/tests/benchmark_sortedset.py +++ b/tests/benchmark_sortedset.py @@ -3,12 +3,9 @@ """ import warnings -from sys import hexversion from .benchmark import * -if hexversion < 0x03000000: - range = xrange # Tests. diff --git a/tests/test_coverage_sorteddict.py b/tests/test_coverage_sorteddict.py index 31c0f24..39e939e 100644 --- a/tests/test_coverage_sorteddict.py +++ b/tests/test_coverage_sorteddict.py @@ -1,17 +1,13 @@ # -*- coding: utf-8 -*- import platform -import random import string import warnings from sortedcontainers import SortedDict import pytest -from sys import hexversion import gc -if hexversion < 0x03000000: - range = xrange def negate(value): return -value @@ -20,16 +16,10 @@ def modulo(value): return value % 10 def get_keysview(dic): - if hexversion < 0x03000000: - return dic.viewkeys() - else: - return dic.keys() + return dic.keys() def get_itemsview(dic): - if hexversion < 0x03000000: - return dic.viewitems() - else: - return dic.items() + return dic.items() def test_init(): temp = SortedDict() @@ -197,13 +187,6 @@ def test_get(): assert temp.get('a') == 0 assert temp.get('A', -1) == -1 -def test_has_key(): - if hexversion > 0x03000000: - return - mapping = [(val, pos) for pos, val in enumerate(string.ascii_lowercase)] - temp = SortedDict(mapping) - assert temp.has_key('a') - def test_items(): mapping = [(val, pos) for pos, val in enumerate(string.ascii_lowercase)] temp = SortedDict(mapping) diff --git a/tests/test_coverage_sortedkeylist_modulo.py b/tests/test_coverage_sortedkeylist_modulo.py index 001db99..1d64cf6 100644 --- a/tests/test_coverage_sortedkeylist_modulo.py +++ b/tests/test_coverage_sortedkeylist_modulo.py @@ -1,15 +1,9 @@ # -*- coding: utf-8 -*- -from sys import hexversion - import random from sortedcontainers import SortedList, SortedKeyList -from itertools import chain, repeat import pytest -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange def modulo(val): return val % 10 diff --git a/tests/test_coverage_sortedkeylist_negate.py b/tests/test_coverage_sortedkeylist_negate.py index bfda7cd..6dbbba2 100644 --- a/tests/test_coverage_sortedkeylist_negate.py +++ b/tests/test_coverage_sortedkeylist_negate.py @@ -1,15 +1,10 @@ # -*- coding: utf-8 -*- -from sys import hexversion - import random from sortedcontainers import SortedKeyList, SortedListWithKey -from itertools import chain, repeat +from itertools import chain import pytest -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange def negate(val): return -val diff --git a/tests/test_coverage_sortedlist.py b/tests/test_coverage_sortedlist.py index 6179638..5e568b8 100644 --- a/tests/test_coverage_sortedlist.py +++ b/tests/test_coverage_sortedlist.py @@ -1,15 +1,10 @@ # -*- coding: utf-8 -*- -from sys import hexversion - import random from sortedcontainers import SortedList from itertools import chain import pytest -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange def test_init(): slt = SortedList() diff --git a/tests/test_coverage_sortedset.py b/tests/test_coverage_sortedset.py index 4b3f1c4..46d2d13 100644 --- a/tests/test_coverage_sortedset.py +++ b/tests/test_coverage_sortedset.py @@ -1,13 +1,7 @@ # -*- coding: utf-8 -*- -from sys import hexversion - -import random from sortedcontainers import SortedSet -import pytest -if hexversion < 0x03000000: - range = xrange def negate(value): return -value diff --git a/tests/test_stress_sorteddict.py b/tests/test_stress_sorteddict.py index a48655c..358e39e 100644 --- a/tests/test_stress_sorteddict.py +++ b/tests/test_stress_sorteddict.py @@ -1,15 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import print_function -from sys import hexversion import random from sortedcontainers import SortedDict -from functools import wraps -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange random.seed(0) actions = [] @@ -83,14 +78,6 @@ def stress_get(sdict): else: assert sdict.get(key, 1) == 1 -@actor -def stress_has_key(sdict): - if hexversion > 0x03000000: - return - keys = list(range(100)) - for key in keys: - assert all((key in sdict) == (sdict.has_key(key)) for key in sdict) - @actor def stress_items_keys_values(sdict): items = sdict.items() diff --git a/tests/test_stress_sortedkeylist.py b/tests/test_stress_sortedkeylist.py index 9a1ef2c..05dc428 100644 --- a/tests/test_stress_sortedkeylist.py +++ b/tests/test_stress_sortedkeylist.py @@ -1,17 +1,12 @@ # -*- coding: utf-8 -*- from __future__ import print_function -from sys import hexversion -import copy import bisect import random from sortedcontainers import SortedKeyList from functools import wraps -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange random.seed(0) actions = [] diff --git a/tests/test_stress_sortedlist.py b/tests/test_stress_sortedlist.py index 0cf3742..7df6d2c 100644 --- a/tests/test_stress_sortedlist.py +++ b/tests/test_stress_sortedlist.py @@ -1,17 +1,12 @@ # -*- coding: utf-8 -*- from __future__ import print_function -from sys import hexversion -import copy import bisect import random from sortedcontainers import SortedList from functools import wraps -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange random.seed(0) actions = [] diff --git a/tests/test_stress_sortedset.py b/tests/test_stress_sortedset.py index c10741b..74cd442 100644 --- a/tests/test_stress_sortedset.py +++ b/tests/test_stress_sortedset.py @@ -1,16 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import print_function -from sys import hexversion import random from sortedcontainers import SortedSet -from functools import wraps -import operator -if hexversion < 0x03000000: - from itertools import izip as zip - range = xrange random.seed(0) actions = [] From 5218343932f4268dc0e5ed259de226dc0ce3a143 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 9 Feb 2023 17:46:35 +0200 Subject: [PATCH 3/5] Upgrade Python syntax with pyupgrade --py37-plus --- docs/conf.py | 1 - src/sortedcontainers/sorteddict.py | 8 ++-- src/sortedcontainers/sortedlist.py | 43 ++++++++++----------- src/sortedcontainers/sortedset.py | 6 +-- tests/benchmark.py | 1 - tests/benchmark_plot.py | 3 +- tests/benchmark_scale.py | 3 +- tests/benchmark_splits_fill.py | 5 +-- tests/plot_lengths_histogram_add.py | 1 - tests/plot_lengths_histogram_delitem.py | 1 - tests/sortedcollection.py | 14 +++---- tests/test_coverage_sorteddict.py | 4 +- tests/test_coverage_sortedkeylist_modulo.py | 2 - tests/test_coverage_sortedkeylist_negate.py | 4 +- tests/test_coverage_sortedlist.py | 4 +- tests/test_coverage_sortedset.py | 4 +- tests/test_stress_sorteddict.py | 8 +--- tests/test_stress_sortedkeylist.py | 8 +--- tests/test_stress_sortedlist.py | 8 +--- tests/test_stress_sortedset.py | 4 -- 20 files changed, 49 insertions(+), 83 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0e1d568..3f2533f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # diff --git a/src/sortedcontainers/sorteddict.py b/src/sortedcontainers/sorteddict.py index f5e9df3..9f3f648 100644 --- a/src/sortedcontainers/sorteddict.py +++ b/src/sortedcontainers/sorteddict.py @@ -370,7 +370,7 @@ def values(self): """ return SortedValuesView(self) - class _NotGiven(object): + class _NotGiven: # pylint: disable=too-few-public-methods def __repr__(self): return '' @@ -564,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): diff --git a/src/sortedcontainers/sortedlist.py b/src/sortedcontainers/sortedlist.py index d48cd76..d2c8215 100644 --- a/src/sortedcontainers/sortedlist.py +++ b/src/sortedcontainers/sortedlist.py @@ -14,7 +14,6 @@ """ # pylint: disable=too-many-lines -from __future__ import print_function import sys import traceback @@ -393,12 +392,12 @@ 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) @@ -406,7 +405,7 @@ def remove(self, 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): @@ -1354,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 @@ -1371,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) @@ -1397,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): @@ -1513,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`` @@ -1553,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): @@ -1969,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 @@ -1985,7 +1984,7 @@ 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 @@ -1993,7 +1992,7 @@ def remove(self, value): 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 @@ -2390,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 @@ -2407,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 @@ -2425,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: @@ -2436,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): @@ -2504,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): diff --git a/src/sortedcontainers/sortedset.py b/src/sortedcontainers/sortedset.py index dfa3f27..1e13e7a 100644 --- a/src/sortedcontainers/sortedset.py +++ b/src/sortedcontainers/sortedset.py @@ -265,7 +265,7 @@ def comparer(self, other): return NotImplemented set_op_name = set_op.__name__ - comparer.__name__ = '__{0}__'.format(set_op_name) + comparer.__name__ = f'__{set_op_name}__' doc_str = """Return true if and only if sorted set is {0} `other`. ``ss.__{1}__(other)`` <==> ``ss {2} other`` @@ -703,9 +703,9 @@ def __repr__(self): """ _key = self._key - key = '' if _key is None else ', key={0!r}'.format(_key) + key = '' if _key is None else f', key={_key!r}' type_name = type(self).__name__ - return '{0}({1!r}{2})'.format(type_name, list(self), key) + return f'{type_name}({list(self)!r}{key})' def _check(self): diff --git a/tests/benchmark.py b/tests/benchmark.py index f3396d5..121cbad 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -1,4 +1,3 @@ - import argparse import random import logging diff --git a/tests/benchmark_plot.py b/tests/benchmark_plot.py index 41d59b8..9e9ef81 100644 --- a/tests/benchmark_plot.py +++ b/tests/benchmark_plot.py @@ -21,7 +21,6 @@ """ -from __future__ import print_function import argparse import matplotlib @@ -96,6 +95,6 @@ def kind_plot(test, kind, zorder): plt.show() if args.save: - plt.savefig('{0}{1}-{2}.png'.format(args.name, args.suffix, test)) + plt.savefig(f'{args.name}{args.suffix}-{test}.png') plt.close() diff --git a/tests/benchmark_scale.py b/tests/benchmark_scale.py index b86b941..a4d28e0 100644 --- a/tests/benchmark_scale.py +++ b/tests/benchmark_scale.py @@ -59,7 +59,6 @@ NUMA node0 CPU(s): 0-31 """ -from __future__ import print_function import argparse import collections as co @@ -119,7 +118,7 @@ def init_sorted_list(sl, size, moment=5, fraction=0.1): sigma = load * fraction total = 0 - class WhileIterator(object): + class WhileIterator: "Convert for-loop to while-loop with length estimate." def __iter__(self): while total < size: diff --git a/tests/benchmark_splits_fill.py b/tests/benchmark_splits_fill.py index 2149fb3..ac71e9e 100644 --- a/tests/benchmark_splits_fill.py +++ b/tests/benchmark_splits_fill.py @@ -57,7 +57,6 @@ """ -from __future__ import print_function import sortedcontainers as sc import random @@ -72,12 +71,12 @@ class SortedListWithSplits(sc.SortedList): def __init__(self, *args, **kwargs): self.splits = 0 - super(SortedListWithSplits, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _expand(self, pos): if len(self._lists[pos]) > self._twice: self.splits += 1 - super(SortedListWithSplits, self)._expand(pos) + super()._expand(pos) def init_sorted_list(sl, size): diff --git a/tests/plot_lengths_histogram_add.py b/tests/plot_lengths_histogram_add.py index 9ac44f0..ad4686b 100644 --- a/tests/plot_lengths_histogram_add.py +++ b/tests/plot_lengths_histogram_add.py @@ -27,7 +27,6 @@ """ -from __future__ import division, print_function import argparse import itertools as it diff --git a/tests/plot_lengths_histogram_delitem.py b/tests/plot_lengths_histogram_delitem.py index fae0ba5..8ddc3d2 100644 --- a/tests/plot_lengths_histogram_delitem.py +++ b/tests/plot_lengths_histogram_delitem.py @@ -2,7 +2,6 @@ """ -from __future__ import division, print_function import argparse import itertools as it diff --git a/tests/sortedcollection.py b/tests/sortedcollection.py index b3afb5c..0d68fc7 100644 --- a/tests/sortedcollection.py +++ b/tests/sortedcollection.py @@ -7,7 +7,7 @@ from bisect import bisect_left, bisect_right -class SortedCollection(object): +class SortedCollection: '''Sequence sorted by a key function. SortedCollection() is much easier to work with than using bisect() directly. @@ -115,7 +115,7 @@ def __reversed__(self): return reversed(self._items) def __repr__(self): - return '%s(%r, key=%s)' % ( + return '{}({!r}, key={})'.format( self.__class__.__name__, self._items, getattr(self._given_key, '__name__', repr(self._given_key)) @@ -169,35 +169,35 @@ def find(self, k): i = bisect_left(self._keys, k) if i != len(self) and self._keys[i] == k: return self._items[i] - raise ValueError('No item found with key equal to: %r' % (k,)) + raise ValueError('No item found with key equal to: {!r}'.format(k)) def find_le(self, k): 'Return last item with a key <= k. Raise ValueError if not found.' i = bisect_right(self._keys, k) if i: return self._items[i-1] - raise ValueError('No item found with key at or below: %r' % (k,)) + raise ValueError('No item found with key at or below: {!r}'.format(k)) def find_lt(self, k): 'Return last item with a key < k. Raise ValueError if not found.' i = bisect_left(self._keys, k) if i: return self._items[i-1] - raise ValueError('No item found with key below: %r' % (k,)) + raise ValueError('No item found with key below: {!r}'.format(k)) def find_ge(self, k): 'Return first item with a key >= equal to k. Raise ValueError if not found' i = bisect_left(self._keys, k) if i != len(self): return self._items[i] - raise ValueError('No item found with key at or above: %r' % (k,)) + raise ValueError('No item found with key at or above: {!r}'.format(k)) def find_gt(self, k): 'Return first item with a key > k. Raise ValueError if not found' i = bisect_right(self._keys, k) if i != len(self): return self._items[i] - raise ValueError('No item found with key above: %r' % (k,)) + raise ValueError('No item found with key above: {!r}'.format(k)) # GrantJ 05/16/18 -- Additions for benchmarking. diff --git a/tests/test_coverage_sorteddict.py b/tests/test_coverage_sorteddict.py index 39e939e..5c5f0d2 100644 --- a/tests/test_coverage_sorteddict.py +++ b/tests/test_coverage_sorteddict.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import platform import string import warnings @@ -276,7 +274,7 @@ def test_repr(): temp = SortedDict({'alice': 3, 'bob': 1, 'carol': 2, 'dave': 4}) assert repr(temp) == "SortedDict({'alice': 3, 'bob': 1, 'carol': 2, 'dave': 4})" -class Identity(object): +class Identity: def __call__(self, value): return value def __repr__(self): diff --git a/tests/test_coverage_sortedkeylist_modulo.py b/tests/test_coverage_sortedkeylist_modulo.py index 1d64cf6..9242ac5 100644 --- a/tests/test_coverage_sortedkeylist_modulo.py +++ b/tests/test_coverage_sortedkeylist_modulo.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import random from sortedcontainers import SortedList, SortedKeyList import pytest diff --git a/tests/test_coverage_sortedkeylist_negate.py b/tests/test_coverage_sortedkeylist_negate.py index 6dbbba2..0d1545f 100644 --- a/tests/test_coverage_sortedkeylist_negate.py +++ b/tests/test_coverage_sortedkeylist_negate.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import random from sortedcontainers import SortedKeyList, SortedListWithKey from itertools import chain @@ -497,7 +495,7 @@ def test_mul(): that = this * 5 this._check() that._check() - assert this == list(reversed((range(10)))) + assert this == list(reversed(range(10))) assert that == list(sorted(list(range(10)) * 5, reverse=True)) assert this != that diff --git a/tests/test_coverage_sortedlist.py b/tests/test_coverage_sortedlist.py index 5e568b8..b54e921 100644 --- a/tests/test_coverage_sortedlist.py +++ b/tests/test_coverage_sortedlist.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import random from sortedcontainers import SortedList from itertools import chain @@ -434,7 +432,7 @@ def test_index(): assert slt.index(99, 0, 1000) == 99 - slt = SortedList((0 for rpt in range(100))) + slt = SortedList(0 for rpt in range(100)) slt._reset(17) for start in range(100): diff --git a/tests/test_coverage_sortedset.py b/tests/test_coverage_sortedset.py index 46d2d13..9c550e9 100644 --- a/tests/test_coverage_sortedset.py +++ b/tests/test_coverage_sortedset.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from sortedcontainers import SortedSet @@ -442,7 +440,7 @@ def test_ior(): temp |= range(90, 100) assert all(temp[val] == val for val in range(100)) -class Identity(object): +class Identity: def __call__(self, value): return value def __repr__(self): diff --git a/tests/test_stress_sorteddict.py b/tests/test_stress_sorteddict.py index 358e39e..e0706d8 100644 --- a/tests/test_stress_sorteddict.py +++ b/tests/test_stress_sorteddict.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import print_function - import random from sortedcontainers import SortedDict @@ -35,7 +31,7 @@ def test_init(): @actor def stress_contains(sdict): keys = list(sdict) - assert all((key in sdict for key in keys)) + assert all(key in sdict for key in keys) @actor def stress_delitem(sdict): @@ -52,7 +48,7 @@ def stress_getitem(sdict): @actor def stress_eq(sdict): - that = dict((key, value) for key, value in sdict.items()) + that = {key: value for key, value in sdict.items()} assert sdict == that @actor diff --git a/tests/test_stress_sortedkeylist.py b/tests/test_stress_sortedkeylist.py index 05dc428..9064891 100644 --- a/tests/test_stress_sortedkeylist.py +++ b/tests/test_stress_sortedkeylist.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import print_function - import bisect import random from sortedcontainers import SortedKeyList @@ -48,7 +44,7 @@ def stress_add(slt): @actor(1) def stress_update(slt): - slt.update((random.random() for rpt in range(350))) + slt.update(random.random() for rpt in range(350)) @actor(1) @not_empty @@ -237,7 +233,7 @@ def stress_lt(slt): assert not (slt < values) def test_stress(repeat=1000): - slt = SortedKeyList((random.random() for rpt in range(1000))) + slt = SortedKeyList(random.random() for rpt in range(1000)) for rpt in range(repeat): action = random.choice(actions) diff --git a/tests/test_stress_sortedlist.py b/tests/test_stress_sortedlist.py index 7df6d2c..4dc7ce1 100644 --- a/tests/test_stress_sortedlist.py +++ b/tests/test_stress_sortedlist.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import print_function - import bisect import random from sortedcontainers import SortedList @@ -48,7 +44,7 @@ def stress_add(slt): @actor(1) def stress_update(slt): - slt.update((random.random() for rpt in range(350))) + slt.update(random.random() for rpt in range(350)) @actor(1) @not_empty @@ -256,7 +252,7 @@ def stress_lt(slt): assert not (slt < values) def test_stress(repeat=1000): - slt = SortedList((random.random() for rpt in range(1000))) + slt = SortedList(random.random() for rpt in range(1000)) slt._reset(23) for rpt in range(repeat): diff --git a/tests/test_stress_sortedset.py b/tests/test_stress_sortedset.py index 74cd442..60056e0 100644 --- a/tests/test_stress_sortedset.py +++ b/tests/test_stress_sortedset.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import print_function - import random from sortedcontainers import SortedSet From 9224cb7d4d16fafc5883550a6526e3aa1d2f0a73 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 26 Aug 2023 10:31:47 +0300 Subject: [PATCH 4/5] Add support for Python 3.12 --- .github/workflows/integration.yml | 3 ++- setup.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index eb0e20c..27d9bd8 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -40,7 +40,7 @@ jobs: max-parallel: 4 matrix: os: [ubuntu-latest] - python-version: [3.7, 3.8, 3.9, '3.10', 3.11, 3.12-dev] + python-version: [3.7, 3.8, 3.9, '3.10', 3.11, 3.12] steps: - uses: actions/checkout@v3 @@ -48,6 +48,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true - name: Install tox run: | diff --git a/setup.py b/setup.py index 5ea47de..938e65b 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ def run_tests(self): long_description=readme, author='Grant Jenks', author_email='contact@grantjenks.com', - url='http://www.grantjenks.com/docs/sortedcontainers/', + url='https://www.grantjenks.com/docs/sortedcontainers/', license='Apache 2.0', package_dir={'': 'src'}, packages=['sortedcontainers'], @@ -49,6 +49,7 @@ def run_tests(self): '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', ], From c7b69f053c4329557eb388fca293e0bc8e9bb5b5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 26 Aug 2023 10:32:02 +0300 Subject: [PATCH 5/5] Upgrade Python syntax with pyupgrade --py37-plus --- tests/sortedcollection.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/sortedcollection.py b/tests/sortedcollection.py index 0d68fc7..ba4c900 100644 --- a/tests/sortedcollection.py +++ b/tests/sortedcollection.py @@ -169,35 +169,35 @@ def find(self, k): i = bisect_left(self._keys, k) if i != len(self) and self._keys[i] == k: return self._items[i] - raise ValueError('No item found with key equal to: {!r}'.format(k)) + raise ValueError(f'No item found with key equal to: {k!r}') def find_le(self, k): 'Return last item with a key <= k. Raise ValueError if not found.' i = bisect_right(self._keys, k) if i: return self._items[i-1] - raise ValueError('No item found with key at or below: {!r}'.format(k)) + raise ValueError(f'No item found with key at or below: {k!r}') def find_lt(self, k): 'Return last item with a key < k. Raise ValueError if not found.' i = bisect_left(self._keys, k) if i: return self._items[i-1] - raise ValueError('No item found with key below: {!r}'.format(k)) + raise ValueError(f'No item found with key below: {k!r}') def find_ge(self, k): 'Return first item with a key >= equal to k. Raise ValueError if not found' i = bisect_left(self._keys, k) if i != len(self): return self._items[i] - raise ValueError('No item found with key at or above: {!r}'.format(k)) + raise ValueError(f'No item found with key at or above: {k!r}') def find_gt(self, k): 'Return first item with a key > k. Raise ValueError if not found' i = bisect_right(self._keys, k) if i != len(self): return self._items[i] - raise ValueError('No item found with key above: {!r}'.format(k)) + raise ValueError(f'No item found with key above: {k!r}') # GrantJ 05/16/18 -- Additions for benchmarking.