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):

Reply via email to