Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-more-itertools for
openSUSE:Factory checked in at 2026-06-15 19:40:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-more-itertools (Old)
and /work/SRC/openSUSE:Factory/.python-more-itertools.new.1981 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-more-itertools"
Mon Jun 15 19:40:15 2026 rev:32 rq:1359270 version:11.1.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-more-itertools/python-more-itertools.changes
2026-04-13 23:18:12.582448766 +0200
+++
/work/SRC/openSUSE:Factory/.python-more-itertools.new.1981/python-more-itertools.changes
2026-06-15 19:41:48.563304658 +0200
@@ -1,0 +2,15 @@
+Sun Jun 14 15:49:47 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 11.1.0:
+ * `numeric_range` was updated to fix its handling of empty
+ ranges (thanks to rhettinger)
+ * `peekable` was updated to fix typing issues (thanks to
+ DORI2001, powellnorma, Pandede, m9810223, and rhettinger)
+ * `islice_extended` was optimized for memory usage and speed
+ (thanks to ben42code, rhettinger, and pochmann)
+ * `serialize` now supports the generator methods `throw`,
+ `send`, and `close` (thanks to rhettinger)
+ * `seekable` now supports implements `__getitem__` for cached
+ elements (thanks to SAY-5, jenstroeger, and JamesParrott)
+
+-------------------------------------------------------------------
Old:
----
more_itertools-11.0.2.tar.gz
New:
----
more_itertools-11.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-more-itertools.spec ++++++
--- /var/tmp/diff_new_pack.uCuRmT/_old 2026-06-15 19:41:50.423382602 +0200
+++ /var/tmp/diff_new_pack.uCuRmT/_new 2026-06-15 19:41:50.423382602 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-more-itertools
-Version: 11.0.2
+Version: 11.1.0
Release: 0
Summary: More routines for operating on iterables, beyond itertools
License: MIT
++++++ more_itertools-11.0.2.tar.gz -> more_itertools-11.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/PKG-INFO
new/more_itertools-11.1.0/PKG-INFO
--- old/more_itertools-11.0.2/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ new/more_itertools-11.1.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: more-itertools
-Version: 11.0.2
+Version: 11.1.0
Summary: More routines for operating on iterables, beyond itertools
Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked
Author-email: Erik Rose <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/docs/versions.rst
new/more_itertools-11.1.0/docs/versions.rst
--- old/more_itertools-11.0.2/docs/versions.rst 2026-04-09 16:58:16.828557700
+0200
+++ new/more_itertools-11.1.0/docs/versions.rst 2026-05-22 16:12:15.295961600
+0200
@@ -5,6 +5,16 @@
.. automodule:: more_itertools
:noindex:
+11.1.0
+------
+
+* Changes to existing functions:
+ * :func:`numeric_range` was updated to fix its handling of empty ranges
(thanks to rhettinger)
+ * :func:`peekable` was updated to fix typing issues (thanks to DORI2001,
powellnorma, Pandede, m9810223, and rhettinger)
+ * :func:`islice_extended` was optimized for memory usage and speed (thanks
to ben42code, rhettinger, and pochmann)
+ * :func:`serialize` now supports the generator methods ``throw``,
``send``, and ``close`` (thanks to rhettinger)
+ * :func:`seekable` now supports implements ``__getitem__`` for cached
elements (thanks to SAY-5, jenstroeger, and JamesParrott)
+
11.0.2
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/more_itertools/__init__.py
new/more_itertools-11.1.0/more_itertools/__init__.py
--- old/more_itertools-11.0.2/more_itertools/__init__.py 2026-04-09
16:58:16.828927300 +0200
+++ new/more_itertools-11.1.0/more_itertools/__init__.py 2026-05-22
16:12:15.296526700 +0200
@@ -3,4 +3,4 @@
from .more import * # noqa
from .recipes import * # noqa
-__version__ = '11.0.2'
+__version__ = '11.1.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/more_itertools/more.py
new/more_itertools-11.1.0/more_itertools/more.py
--- old/more_itertools-11.0.2/more_itertools/more.py 2026-04-09
16:58:16.829475900 +0200
+++ new/more_itertools-11.1.0/more_itertools/more.py 2026-05-22
16:12:15.297827000 +0200
@@ -1,4 +1,5 @@
import math
+import types
from collections import Counter, defaultdict, deque
from collections.abc import Sequence
@@ -434,6 +435,8 @@
"""
self._cache.extendleft(reversed(items))
+ __class_getitem__ = classmethod(types.GenericAlias)
+
def __next__(self):
if self._cache:
return self._cache.popleft()
@@ -2402,10 +2405,14 @@
)
def __reversed__(self):
+ # Empty iterator
+ try:
+ start = self._get_by_index(-1)
+ except IndexError:
+ return iter([])
+
return iter(
- numeric_range(
- self._get_by_index(-1), self._start - self._step, -self._step
- )
+ numeric_range(start, self._start - self._step, -self._step)
)
def count(self, value):
@@ -2663,8 +2670,10 @@
if start < 0:
# Consume all but the last -start items
- cache = deque(enumerate(it, 1), maxlen=-start)
- len_iter = cache[-1][0] if cache else 0
+ counter = count(1)
+ wrapper = compress(it, counter)
+ cache = deque(wrapper, maxlen=-start)
+ len_iter = next(counter) - 1
# Adjust start to be positive
i = max(len_iter + start, 0)
@@ -2687,7 +2696,7 @@
# pop and yield the item.
# We don't want to use an intermediate variable
# it would extend the lifetime of the current item
- yield cache.popleft()[1]
+ yield cache.popleft()
else:
# just pop and discard the item
cache.popleft()
@@ -2718,8 +2727,10 @@
if (stop is not None) and (stop < 0):
# Consume all but the last items
n = -stop - 1
- cache = deque(enumerate(it, 1), maxlen=n)
- len_iter = cache[-1][0] if cache else 0
+ counter = count(1)
+ wrapper = compress(it, counter)
+ cache = deque(wrapper, maxlen=n)
+ len_iter = next(counter) - 1
# If start and stop are both negative they are comparable and
# we can just slice. Otherwise we can adjust start to be negative
@@ -2729,8 +2740,7 @@
else:
i, j = min(start - len_iter, -1), None
- for index, item in list(cache)[i:j:step]:
- yield item
+ yield from list(cache)[i:j:step]
else:
# Advance to the stop position
if stop is not None:
@@ -2997,6 +3007,16 @@
>>> elements
SequenceView(['0', '1', '2', '3'])
+ Indexing the :class:`seekable` directly returns items from the cache:
+
+ >>> it = seekable((str(n) for n in range(10)))
+ >>> next(it), next(it), next(it)
+ ('0', '1', '2')
+ >>> it[-1]
+ '2'
+ >>> it[0]
+ '0'
+
By default, the cache grows as the source iterable progresses, so beware of
wrapping very large or infinite iterables. Supply *maxlen* to limit the
size of the cache (this of course limits how far back you can seek).
@@ -3074,6 +3094,9 @@
self.seek(max(self._index + count, 0))
+ def __getitem__(self, index):
+ return self._cache[index]
+
class run_length:
"""
@@ -5364,18 +5387,42 @@
threads.
"""
- __slots__ = ('iterator', 'lock')
+ __slots__ = ('_iterator', '_lock')
def __init__(self, iterable):
- self.iterator = iter(iterable)
- self.lock = Lock()
+ self._iterator = iter(iterable)
+ self._lock = Lock()
def __iter__(self):
return self
def __next__(self):
- with self.lock:
- return next(self.iterator)
+ with self._lock:
+ return next(self._iterator)
+
+ def send(self, value, /):
+ """Send a value to a generator.
+
+ Raises AttributeError if not a generator.
+ """
+ with self._lock:
+ return self._iterator.send(value)
+
+ def throw(self, *args):
+ """Call throw() on a generator.
+
+ Raises AttributeError if not a generator.
+ """
+ with self._lock:
+ return self._iterator.throw(*args)
+
+ def close(self):
+ """Call close() on a generator.
+
+ Raises AttributeError if not a generator.
+ """
+ with self._lock:
+ return self._iterator.close()
def synchronized(func):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/more_itertools/more.pyi
new/more_itertools-11.1.0/more_itertools/more.pyi
--- old/more_itertools-11.0.2/more_itertools/more.pyi 2026-04-09
16:58:16.829783000 +0200
+++ new/more_itertools-11.1.0/more_itertools/more.pyi 2026-05-18
22:41:10.001954600 +0200
@@ -212,6 +212,8 @@
def __getitem__(self, index: int) -> _T: ...
@overload
def __getitem__(self, index: slice) -> list[_T]: ...
+ @classmethod
+ def __class_getitem__(cls, item: Any, /) -> types.GenericAlias: ...
def consumer(func: _GenFn) -> _GenFn: ...
def ilen(iterable: Iterable[_T]) -> int: ...
@@ -591,6 +593,7 @@
def elements(self) -> SequenceView[_T]: ...
def seek(self, index: int) -> None: ...
def relative_seek(self, count: int) -> None: ...
+ def __getitem__(self, index: int) -> _T: ...
class run_length:
@staticmethod
@@ -954,6 +957,9 @@
def __init__(self, iterable: Iterable[_T]) -> None: ...
def __iter__(self) -> serialize[_T]: ...
def __next__(self) -> _T: ...
+ def send(self, value: object, /) -> _T: ...
+ def throw(self, *args: object) -> None: ...
+ def close(self) -> None: ...
def concurrent_tee(
iterable: Iterable[_T], n: int = ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/setup.cfg
new/more_itertools-11.1.0/setup.cfg
--- old/more_itertools-11.0.2/setup.cfg 2026-04-09 16:58:16.830700200 +0200
+++ new/more_itertools-11.1.0/setup.cfg 2026-05-22 16:12:15.298221800 +0200
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 11.0.2
+current_version = 11.1.0
commit = True
tag = False
files = more_itertools/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/setup.py
new/more_itertools-11.1.0/setup.py
--- old/more_itertools-11.0.2/setup.py 1970-01-01 01:00:00.000000000 +0100
+++ new/more_itertools-11.1.0/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -10,7 +10,7 @@
{'': ['*']}
setup(name='more-itertools',
- version='11.0.2',
+ version='11.1.0',
description='More routines for operating on iterables, beyond itertools',
author=None,
author_email='Erik Rose <[email protected]>',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more_itertools-11.0.2/tests/test_more.py
new/more_itertools-11.1.0/tests/test_more.py
--- old/more_itertools-11.0.2/tests/test_more.py 2026-04-02
17:02:36.663890100 +0200
+++ new/more_itertools-11.1.0/tests/test_more.py 2026-05-18
22:41:10.002604700 +0200
@@ -3,6 +3,7 @@
import cmath
import gc
import platform
+import types
import weakref
from collections import Counter, deque
@@ -472,6 +473,14 @@
expected = [12, 11, 10, 0, 1, 2]
self.assertEqual(actual, expected)
+ def test_class_getitem(self):
+ """peekable[T] should return a GenericAlias, matching the behaviour of
+ list[T], allowing use in generic type annotations at runtime."""
+ alias = mi.peekable[str]
+ self.assertIsInstance(alias, types.GenericAlias)
+ self.assertIs(alias.__origin__, mi.peekable)
+ self.assertEqual(alias.__args__, (str,))
+
class ConsumerTests(TestCase):
"""Tests for ``consumer()``"""
@@ -3137,6 +3146,9 @@
self.assertTrue(dumps(r)) # assert not empty
self.assertEqual(r, loads(dumps(r)))
+ def test_empty_reversed(self):
+ self.assertEqual(list(reversed(mi.numeric_range(0))), [])
+
class CountCycleTests(TestCase):
def test_basic(self):
@@ -3613,6 +3625,23 @@
s.relative_seek(10) # Lower bound
self.assertEqual(list(s.elements()), [str(x) for x in range(5)])
+ def test_getitem(self):
+ s = mi.seekable(str(n) for n in range(10))
+ with self.assertRaises(IndexError):
+ s[0]
+ mi.take(3, s)
+ self.assertEqual(s[-1], '2')
+ self.assertEqual(s[0], '0')
+ self.assertEqual(s[2], '2')
+ with self.assertRaises(IndexError):
+ s[3]
+
+ def test_getitem_maxlen(self):
+ s = mi.seekable((str(n) for n in range(10)), maxlen=2)
+ mi.take(5, s)
+ self.assertEqual(s[-1], '4')
+ self.assertEqual(s[0], '3')
+
class SequenceViewTests(TestCase):
def test_init(self):
@@ -6564,6 +6593,81 @@
self.assertEqual(result, limit * (limit - 1) // 2)
+ def test_serialize_generator_methods(self):
+ # A generator that yields and receives
+ def echo():
+ try:
+ while True:
+ val = yield "ready"
+ yield f"received {val}"
+ except ValueError:
+ yield "caught"
+
+ it = mi.serialize(echo())
+
+ # Test __next__
+ self.assertEqual(next(it), "ready")
+
+ # Test send()
+ self.assertEqual(it.send("hello"), "received hello")
+ self.assertEqual(next(it), "ready")
+
+ # Test throw()
+ self.assertEqual(it.throw(ValueError), "caught")
+
+ # Test close()
+ it.close()
+ with self.assertRaises(StopIteration):
+ next(it)
+
+ def test_serialize_methods_attribute_error(self):
+ # A standard iterator that does not have send/throw/close
+ # should raise AttributeError when called.
+ standard_it = mi.serialize([1, 2, 3])
+
+ with self.assertRaises(AttributeError):
+ standard_it.send("foo")
+
+ with self.assertRaises(AttributeError):
+ standard_it.throw(ValueError)
+
+ with self.assertRaises(AttributeError):
+ standard_it.close()
+
+ def test_serialize_generator_methods_locking(self):
+ # Verifies that generator methods also acquire the lock.
+ # We can test this by checking if the lock is held during the call.
+
+ class LockCheckingGenerator:
+ def __init__(self, lock):
+ self.lock = lock
+
+ def __iter__(self):
+ return self
+
+ def send(self, value):
+ if not self.lock.locked():
+ raise RuntimeError("Lock not held during send()")
+ return value
+
+ def throw(self, *args):
+ if not self.lock.locked():
+ raise RuntimeError("Lock not held during throw()")
+
+ def close(self):
+ if not self.lock.locked():
+ raise RuntimeError("Lock not held during close()")
+
+ # Manually create the serialize object to inspect the lock
+ it = mi.serialize([])
+ mock_gen = LockCheckingGenerator(it._lock)
+ it._iterator = mock_gen
+
+ # These should not raise RuntimeError
+ it.send(1)
+ it.throw(ValueError)
+ it.close()
+
class TestSynchronized(TestCase):
def test_concurrent_calls(self):