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 2023-03-08 14:51:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-more-itertools (Old) and /work/SRC/openSUSE:Factory/.python-more-itertools.new.31432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-more-itertools" Wed Mar 8 14:51:59 2023 rev:22 rq:1069920 version:9.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-more-itertools/python-more-itertools.changes 2022-11-01 13:41:20.055502214 +0100 +++ /work/SRC/openSUSE:Factory/.python-more-itertools.new.31432/python-more-itertools.changes 2023-03-08 14:52:01.046494662 +0100 @@ -1,0 +2,6 @@ +Tue Mar 7 12:12:32 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 9.1.0: + * See PR #678 for details. + +------------------------------------------------------------------- Old: ---- more-itertools-9.0.0.tar.gz New: ---- more-itertools-9.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-more-itertools.spec ++++++ --- /var/tmp/diff_new_pack.oSeeLC/_old 2023-03-08 14:52:01.622497799 +0100 +++ /var/tmp/diff_new_pack.oSeeLC/_new 2023-03-08 14:52:01.626497821 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-more-itertools # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 Name: python-more-itertools -Version: 9.0.0 +Version: 9.1.0 Release: 0 Summary: More routines for operating on iterables, beyond itertools License: MIT ++++++ more-itertools-9.0.0.tar.gz -> more-itertools-9.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/.github/workflows/python-app.yml new/more-itertools-9.1.0/.github/workflows/python-app.yml --- old/more-itertools-9.0.0/.github/workflows/python-app.yml 2022-10-18 15:38:19.069835000 +0200 +++ new/more-itertools-9.1.0/.github/workflows/python-app.yml 2023-02-21 18:00:55.991196000 +0100 @@ -8,12 +8,12 @@ runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11.0-rc.2", "pypy-3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12.0-alpha.1", "pypy-3.8"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/PKG-INFO new/more-itertools-9.1.0/PKG-INFO --- old/more-itertools-9.0.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/more-itertools-9.1.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: more-itertools -Version: 9.0.0 +Version: 9.1.0 Summary: More routines for operating on iterables, beyond itertools Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked Author-email: Erik Rose <erikr...@grinchcentral.com> @@ -52,6 +52,7 @@ | | `batched <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.batched>`_, | | | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, | | | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ | +| | `transpose <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.transpose>`_ | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, | | | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, | @@ -139,6 +140,7 @@ | | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, | | | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, | | | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, | +| | `gray_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.gray_product>`_, | | | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, | | | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, | | | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, | @@ -166,11 +168,14 @@ | | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, | | | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, | | | `map_if <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_if>`_, | +| | `iter_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_index>`_, | | | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, | | | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, | -| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ | -| | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_ | +| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_, | +| | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_, | | | `sieve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sieve>`_ | +| | `factor <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.factor>`_ | +| | `matmul <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.matmul>`_ | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/README.rst new/more-itertools-9.1.0/README.rst --- old/more-itertools-9.0.0/README.rst 2022-10-18 15:38:19.069835000 +0200 +++ new/more-itertools-9.1.0/README.rst 2023-02-27 15:41:12.542422800 +0100 @@ -28,6 +28,7 @@ | | `batched <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.batched>`_, | | | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, | | | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ | +| | `transpose <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.transpose>`_ | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, | | | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, | @@ -115,6 +116,7 @@ | | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, | | | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, | | | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, | +| | `gray_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.gray_product>`_, | | | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, | | | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, | | | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, | @@ -142,11 +144,14 @@ | | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, | | | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, | | | `map_if <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_if>`_, | +| | `iter_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_index>`_, | | | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, | | | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, | -| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ | -| | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_ | +| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_, | +| | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_, | | | `sieve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sieve>`_ | +| | `factor <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.factor>`_ | +| | `matmul <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.matmul>`_ | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/docs/api.rst new/more-itertools-9.1.0/docs/api.rst --- old/more-itertools-9.0.0/docs/api.rst 2022-10-18 15:37:02.101069200 +0200 +++ new/more-itertools-9.1.0/docs/api.rst 2023-02-21 16:06:20.592258000 +0100 @@ -35,6 +35,7 @@ .. autofunction:: batched .. autofunction:: grouper .. autofunction:: partition +.. autofunction:: transpose Lookahead and lookback @@ -224,6 +225,7 @@ .. autofunction:: product_index .. autofunction:: combination_index .. autofunction:: permutation_index +.. autofunction:: gray_product ---- @@ -286,8 +288,11 @@ **Itertools recipes** +.. autofunction:: iter_index .. autofunction:: consume .. autofunction:: tabulate .. autofunction:: repeatfunc .. autofunction:: polynomial_from_roots .. autofunction:: sieve +.. autofunction:: factor +.. autofunction:: matmul diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/docs/versions.rst new/more-itertools-9.1.0/docs/versions.rst --- old/more-itertools-9.0.0/docs/versions.rst 2022-10-18 15:38:19.069835000 +0200 +++ new/more-itertools-9.1.0/docs/versions.rst 2023-02-27 15:41:12.546871400 +0100 @@ -5,6 +5,27 @@ .. automodule:: more_itertools :noindex: +8.14.0 +------ + +* New functions + * :func:`iter_index` (from the Python itertools docs) + * :func:`transpose` (from the Python itertools docs) + * :func:`matmul` (from the Python itertools docs) + * :func:`factor` (from the Python itertools docs) + * :func:`gray_product` (thanks to haukex) + +* Changes to existing functions + * :func:`sieve` was updated to match the Python itertools docs + * :func:`maxsplit` was updated to fix a bug (thanks to abingham) + * :func:`sliced` had its `type hint <https://github.com/more-itertools/more-itertools/pull/667>`__ updated (thanks to ad-chaos) + + +* Other changes + * The ``batched`` function is marked as deprecated and will be removed in a future major release. For Python 3.12 and above, use ``itertools.batched`` instead. (thanks to neutrinoceros) + * The type hints now used postponed evaluation of annotations from PEP 563 (thanks to Isira-Seneviratne) + * Some documentation issues were fixed (thanks to Voskov and jdkandersson) + 9.0.0 ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/more_itertools/__init__.py new/more-itertools-9.1.0/more_itertools/__init__.py --- old/more-itertools-9.0.0/more_itertools/__init__.py 2022-10-18 15:38:19.069835000 +0200 +++ new/more-itertools-9.1.0/more_itertools/__init__.py 2023-02-27 15:41:12.550684500 +0100 @@ -3,4 +3,4 @@ from .more import * # noqa from .recipes import * # noqa -__version__ = '9.0.0' +__version__ = '9.1.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/more_itertools/more.py new/more-itertools-9.1.0/more_itertools/more.py --- old/more-itertools-9.0.0/more_itertools/more.py 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/more_itertools/more.py 2023-02-21 16:05:46.935729700 +0100 @@ -68,6 +68,7 @@ 'exactly_n', 'filter_except', 'first', + 'gray_product', 'groupby_transform', 'ichunked', 'iequals', @@ -658,6 +659,7 @@ [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] """ + # Algorithm: https://w.wiki/Qai def _full(A): while True: @@ -1301,7 +1303,7 @@ [[0], [2], [4, 5, 6, 7, 8, 9]] By default, the delimiting items are not included in the output. - The include them, set *keep_separator* to ``True``. + To include them, set *keep_separator* to ``True``. >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] @@ -1391,7 +1393,9 @@ if pred(item) and buf: yield buf if maxsplit == 1: - yield list(it) + buf = list(it) + if buf: + yield buf return buf = [] maxsplit -= 1 @@ -2914,6 +2918,7 @@ '7' """ + # See https://sites.google.com/site/bbayles/index/decorator_factory for # notes on how this works. def decorator(*wrapping_args, **wrapping_kwargs): @@ -3464,7 +3469,6 @@ next_index = k + floor(log(random()) / log(1 - W)) for index, element in enumerate(iterable, k): - if index == next_index: reservoir[randrange(k)] = element # The new W is the largest in a sample of k U(0, `old_W`) numbers @@ -4284,7 +4288,6 @@ lo_key = hi_key = key(lo) for x, y in zip_longest(it, it, fillvalue=lo): - x_key, y_key = key(x), key(y) if y_key < x_key: @@ -4345,3 +4348,45 @@ if batch: yield tuple(batch) + + +def gray_product(*iterables): + """Like :func:`itertools.product`, but return tuples in an order such + that only one element in the generated tuple changes from one iteration + to the next. + + >>> list(gray_product('AB','CD')) + [('A', 'C'), ('B', 'C'), ('B', 'D'), ('A', 'D')] + + This function consumes all of the input iterables before producing output. + If any of the input iterables have fewer than two items, ``ValueError`` + is raised. + + For information on the algorithm, see + `this section <https://www-cs-faculty.stanford.edu/~knuth/fasc2a.ps.gz>`__ + of Donald Knuth's *The Art of Computer Programming*. + """ + all_iterables = tuple(tuple(x) for x in iterables) + iterable_count = len(all_iterables) + for iterable in all_iterables: + if len(iterable) < 2: + raise ValueError("each iterable must have two or more items") + + # This is based on "Algorithm H" from section 7.2.1.1, page 20. + # a holds the indexes of the source iterables for the n-tuple to be yielded + # f is the array of "focus pointers" + # o is the array of "directions" + a = [0] * iterable_count + f = list(range(iterable_count + 1)) + o = [1] * iterable_count + while True: + yield tuple(all_iterables[i][a[i]] for i in range(iterable_count)) + j = f[0] + f[0] = 0 + if j == iterable_count: + break + a[j] = a[j] + o[j] + if a[j] == 0 or a[j] == len(all_iterables[j]) - 1: + o[j] = -o[j] + f[j] = f[j + 1] + f[j + 1] = j + 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/more_itertools/more.pyi new/more-itertools-9.1.0/more_itertools/more.pyi --- old/more-itertools-9.0.0/more_itertools/more.pyi 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/more_itertools/more.pyi 2023-02-21 16:05:46.938101000 +0100 @@ -1,26 +1,25 @@ """Stubs for more_itertools.more""" +from __future__ import annotations +from types import TracebackType from typing import ( Any, Callable, Container, - Dict, + ContextManager, Generic, Hashable, Iterable, Iterator, - List, - Optional, + overload, Reversible, Sequence, Sized, - Tuple, - Union, + Type, TypeVar, type_check_only, ) -from types import TracebackType -from typing_extensions import ContextManager, Protocol, Type, overload +from typing_extensions import Protocol # Type and type variable definitions _T = TypeVar('_T') @@ -31,7 +30,7 @@ _W = TypeVar('_W') _T_co = TypeVar('_T_co', covariant=True) _GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) -_Raisable = Union[BaseException, 'Type[BaseException]'] +_Raisable = BaseException | Type[BaseException] @type_check_only class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... @@ -39,23 +38,25 @@ @type_check_only class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... +@type_check_only +class _SupportsSlicing(Protocol[_T_co]): + def __getitem__(self, __k: slice) -> _T_co: ... + def chunked( - iterable: Iterable[_T], n: Optional[int], strict: bool = ... -) -> Iterator[List[_T]]: ... + iterable: Iterable[_T], n: int | None, strict: bool = ... +) -> Iterator[list[_T]]: ... @overload def first(iterable: Iterable[_T]) -> _T: ... @overload -def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +def first(iterable: Iterable[_T], default: _U) -> _T | _U: ... @overload def last(iterable: Iterable[_T]) -> _T: ... @overload -def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +def last(iterable: Iterable[_T], default: _U) -> _T | _U: ... @overload def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... @overload -def nth_or_last( - iterable: Iterable[_T], n: int, default: _U -) -> Union[_T, _U]: ... +def nth_or_last(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... class peekable(Generic[_T], Iterator[_T]): def __init__(self, iterable: Iterable[_T]) -> None: ... @@ -64,13 +65,13 @@ @overload def peek(self) -> _T: ... @overload - def peek(self, default: _U) -> Union[_T, _U]: ... + def peek(self, default: _U) -> _T | _U: ... def prepend(self, *items: _T) -> None: ... def __next__(self) -> _T: ... @overload def __getitem__(self, index: int) -> _T: ... @overload - def __getitem__(self, index: slice) -> List[_T]: ... + def __getitem__(self, index: slice) -> list[_T]: ... def consumer(func: _GenFn) -> _GenFn: ... def ilen(iterable: Iterable[object]) -> int: ... @@ -80,42 +81,42 @@ ) -> Iterator[_T]: ... def one( iterable: Iterable[_T], - too_short: Optional[_Raisable] = ..., - too_long: Optional[_Raisable] = ..., + too_short: _Raisable | None = ..., + too_long: _Raisable | None = ..., ) -> _T: ... def raise_(exception: _Raisable, *args: Any) -> None: ... def strictly_n( iterable: Iterable[_T], n: int, - too_short: Optional[_GenFn] = ..., - too_long: Optional[_GenFn] = ..., -) -> List[_T]: ... + too_short: _GenFn | None = ..., + too_long: _GenFn | None = ..., +) -> list[_T]: ... def distinct_permutations( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Iterator[Tuple[_T, ...]]: ... + iterable: Iterable[_T], r: int | None = ... +) -> Iterator[tuple[_T, ...]]: ... def intersperse( e: _U, iterable: Iterable[_T], n: int = ... -) -> Iterator[Union[_T, _U]]: ... -def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... +) -> Iterator[_T | _U]: ... +def unique_to_each(*iterables: Iterable[_T]) -> list[list[_T]]: ... @overload def windowed( seq: Iterable[_T], n: int, *, step: int = ... -) -> Iterator[Tuple[Optional[_T], ...]]: ... +) -> Iterator[tuple[_T | None, ...]]: ... @overload def windowed( seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... -def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +) -> Iterator[tuple[_T | _U, ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... def substrings_indexes( seq: Sequence[_T], reverse: bool = ... -) -> Iterator[Tuple[Sequence[_T], int, int]]: ... +) -> Iterator[tuple[Sequence[_T], int, int]]: ... class bucket(Generic[_T, _U], Container[_U]): def __init__( self, iterable: Iterable[_T], key: Callable[[_T], _U], - validator: Optional[Callable[[object], object]] = ..., + validator: Callable[[object], object] | None = ..., ) -> None: ... def __contains__(self, value: object) -> bool: ... def __iter__(self) -> Iterator[_U]: ... @@ -123,109 +124,105 @@ def spy( iterable: Iterable[_T], n: int = ... -) -> Tuple[List[_T], Iterator[_T]]: ... +) -> tuple[list[_T], Iterator[_T]]: ... def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... def interleave_evenly( - iterables: List[Iterable[_T]], lengths: Optional[List[int]] = ... + iterables: list[Iterable[_T]], lengths: list[int] | None = ... ) -> Iterator[_T]: ... def collapse( iterable: Iterable[Any], - base_type: Optional[type] = ..., - levels: Optional[int] = ..., + base_type: type | None = ..., + levels: int | None = ..., ) -> Iterator[Any]: ... @overload def side_effect( func: Callable[[_T], object], iterable: Iterable[_T], chunk_size: None = ..., - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., ) -> Iterator[_T]: ... @overload def side_effect( - func: Callable[[List[_T]], object], + func: Callable[[list[_T]], object], iterable: Iterable[_T], chunk_size: int, - before: Optional[Callable[[], object]] = ..., - after: Optional[Callable[[], object]] = ..., + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., ) -> Iterator[_T]: ... def sliced( - seq: Sequence[_T], n: int, strict: bool = ... -) -> Iterator[Sequence[_T]]: ... + seq: _SupportsSlicing[_T], n: int, strict: bool = ... +) -> Iterator[_T]: ... def split_at( iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ..., keep_separator: bool = ..., -) -> Iterator[List[_T]]: ... +) -> Iterator[list[_T]]: ... def split_before( iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... +) -> Iterator[list[_T]]: ... def split_after( iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... -) -> Iterator[List[_T]]: ... +) -> Iterator[list[_T]]: ... def split_when( iterable: Iterable[_T], pred: Callable[[_T, _T], object], maxsplit: int = ..., -) -> Iterator[List[_T]]: ... +) -> Iterator[list[_T]]: ... def split_into( - iterable: Iterable[_T], sizes: Iterable[Optional[int]] -) -> Iterator[List[_T]]: ... + iterable: Iterable[_T], sizes: Iterable[int | None] +) -> Iterator[list[_T]]: ... @overload def padded( iterable: Iterable[_T], *, - n: Optional[int] = ..., + n: int | None = ..., next_multiple: bool = ..., -) -> Iterator[Optional[_T]]: ... +) -> Iterator[_T | None]: ... @overload def padded( iterable: Iterable[_T], fillvalue: _U, - n: Optional[int] = ..., + n: int | None = ..., next_multiple: bool = ..., -) -> Iterator[Union[_T, _U]]: ... +) -> Iterator[_T | _U]: ... @overload def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... @overload -def repeat_last( - iterable: Iterable[_T], default: _U -) -> Iterator[Union[_T, _U]]: ... -def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +def repeat_last(iterable: Iterable[_T], default: _U) -> Iterator[_T | _U]: ... +def distribute(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... @overload def stagger( iterable: Iterable[_T], offsets: _SizedIterable[int] = ..., longest: bool = ..., -) -> Iterator[Tuple[Optional[_T], ...]]: ... +) -> Iterator[tuple[_T | None, ...]]: ... @overload def stagger( iterable: Iterable[_T], offsets: _SizedIterable[int] = ..., longest: bool = ..., fillvalue: _U = ..., -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +) -> Iterator[tuple[_T | _U, ...]]: ... class UnequalIterablesError(ValueError): - def __init__( - self, details: Optional[Tuple[int, int, int]] = ... - ) -> None: ... + def __init__(self, details: tuple[int, int, int] | None = ...) -> None: ... @overload -def zip_equal(__iter1: Iterable[_T1]) -> Iterator[Tuple[_T1]]: ... +def zip_equal(__iter1: Iterable[_T1]) -> Iterator[tuple[_T1]]: ... @overload def zip_equal( __iter1: Iterable[_T1], __iter2: Iterable[_T2] -) -> Iterator[Tuple[_T1, _T2]]: ... +) -> Iterator[tuple[_T1, _T2]]: ... @overload def zip_equal( __iter1: Iterable[_T], __iter2: Iterable[_T], __iter3: Iterable[_T], *iterables: Iterable[_T], -) -> Iterator[Tuple[_T, ...]]: ... +) -> Iterator[tuple[_T, ...]]: ... @overload def zip_offset( __iter1: Iterable[_T1], @@ -233,7 +230,7 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: None = None, -) -> Iterator[Tuple[Optional[_T1]]]: ... +) -> Iterator[tuple[_T1 | None]]: ... @overload def zip_offset( __iter1: Iterable[_T1], @@ -242,7 +239,7 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: None = None, -) -> Iterator[Tuple[Optional[_T1], Optional[_T2]]]: ... +) -> Iterator[tuple[_T1 | None, _T2 | None]]: ... @overload def zip_offset( __iter1: Iterable[_T], @@ -252,7 +249,7 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: None = None, -) -> Iterator[Tuple[Optional[_T], ...]]: ... +) -> Iterator[tuple[_T | None, ...]]: ... @overload def zip_offset( __iter1: Iterable[_T1], @@ -260,7 +257,7 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: _U, -) -> Iterator[Tuple[Union[_T1, _U]]]: ... +) -> Iterator[tuple[_T1 | _U]]: ... @overload def zip_offset( __iter1: Iterable[_T1], @@ -269,7 +266,7 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: _U, -) -> Iterator[Tuple[Union[_T1, _U], Union[_T2, _U]]]: ... +) -> Iterator[tuple[_T1 | _U, _T2 | _U]]: ... @overload def zip_offset( __iter1: Iterable[_T], @@ -279,82 +276,80 @@ offsets: _SizedIterable[int], longest: bool = ..., fillvalue: _U, -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +) -> Iterator[tuple[_T | _U, ...]]: ... def sort_together( iterables: Iterable[Iterable[_T]], key_list: Iterable[int] = ..., - key: Optional[Callable[..., Any]] = ..., + key: Callable[..., Any] | None = ..., reverse: bool = ..., -) -> List[Tuple[_T, ...]]: ... -def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... -def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +) -> list[tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... def always_iterable( obj: object, - base_type: Union[ - type, Tuple[Union[type, Tuple[Any, ...]], ...], None - ] = ..., + base_type: type | tuple[type | tuple[Any, ...], ...] | None = ..., ) -> Iterator[Any]: ... def adjacent( predicate: Callable[[_T], bool], iterable: Iterable[_T], distance: int = ..., -) -> Iterator[Tuple[bool, _T]]: ... +) -> Iterator[tuple[bool, _T]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: None = None, valuefunc: None = None, reducefunc: None = None, -) -> Iterator[Tuple[_T, Iterator[_T]]]: ... +) -> Iterator[tuple[_T, Iterator[_T]]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: None, reducefunc: None, -) -> Iterator[Tuple[_U, Iterator[_T]]]: ... +) -> Iterator[tuple[_U, Iterator[_T]]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: None, valuefunc: Callable[[_T], _V], reducefunc: None, -) -> Iterable[Tuple[_T, Iterable[_V]]]: ... +) -> Iterable[tuple[_T, Iterable[_V]]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: Callable[[_T], _V], reducefunc: None, -) -> Iterable[Tuple[_U, Iterator[_V]]]: ... +) -> Iterable[tuple[_U, Iterator[_V]]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: None, valuefunc: None, reducefunc: Callable[[Iterator[_T]], _W], -) -> Iterable[Tuple[_T, _W]]: ... +) -> Iterable[tuple[_T, _W]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: None, reducefunc: Callable[[Iterator[_T]], _W], -) -> Iterable[Tuple[_U, _W]]: ... +) -> Iterable[tuple[_U, _W]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: None, valuefunc: Callable[[_T], _V], reducefunc: Callable[[Iterable[_V]], _W], -) -> Iterable[Tuple[_T, _W]]: ... +) -> Iterable[tuple[_T, _W]]: ... @overload def groupby_transform( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: Callable[[_T], _V], reducefunc: Callable[[Iterable[_V]], _W], -) -> Iterable[Tuple[_U, _W]]: ... +) -> Iterable[tuple[_U, _W]]: ... class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): @overload @@ -375,22 +370,22 @@ def __len__(self) -> int: ... def __reduce__( self, - ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... + ) -> tuple[Type[numeric_range[_T, _U]], tuple[_T, _T, _U]]: ... def __repr__(self) -> str: ... def __reversed__(self) -> Iterator[_T]: ... def count(self, value: _T) -> int: ... def index(self, value: _T) -> int: ... # type: ignore def count_cycle( - iterable: Iterable[_T], n: Optional[int] = ... -) -> Iterable[Tuple[int, _T]]: ... + iterable: Iterable[_T], n: int | None = ... +) -> Iterable[tuple[int, _T]]: ... def mark_ends( iterable: Iterable[_T], -) -> Iterable[Tuple[bool, bool, _T]]: ... +) -> Iterable[tuple[bool, bool, _T]]: ... def locate( iterable: Iterable[object], pred: Callable[..., Any] = ..., - window_size: Optional[int] = ..., + window_size: int | None = ..., ) -> Iterator[int]: ... def lstrip( iterable: Iterable[_T], pred: Callable[[_T], object] @@ -403,9 +398,7 @@ ) -> Iterator[_T]: ... class islice_extended(Generic[_T], Iterator[_T]): - def __init__( - self, iterable: Iterable[_T], *args: Optional[int] - ) -> None: ... + def __init__(self, iterable: Iterable[_T], *args: int | None) -> None: ... def __iter__(self) -> islice_extended[_T]: ... def __next__(self) -> _T: ... def __getitem__(self, index: slice) -> islice_extended[_T]: ... @@ -420,7 +413,7 @@ func: Callable[[_T, _T], _U] = ..., *, initial: None = ..., -) -> Iterator[Union[_T, _U]]: ... +) -> Iterator[_T | _U]: ... @overload def difference( iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U @@ -436,7 +429,7 @@ class seekable(Generic[_T], Iterator[_T]): def __init__( - self, iterable: Iterable[_T], maxlen: Optional[int] = ... + self, iterable: Iterable[_T], maxlen: int | None = ... ) -> None: ... def __iter__(self) -> seekable[_T]: ... def __next__(self) -> _T: ... @@ -444,20 +437,20 @@ @overload def peek(self) -> _T: ... @overload - def peek(self, default: _U) -> Union[_T, _U]: ... + def peek(self, default: _U) -> _T | _U: ... def elements(self) -> SequenceView[_T]: ... def seek(self, index: int) -> None: ... class run_length: @staticmethod - def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... + def encode(iterable: Iterable[_T]) -> Iterator[tuple[_T, int]]: ... @staticmethod - def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... + def decode(iterable: Iterable[tuple[_T, int]]) -> Iterator[_T]: ... def exactly_n( iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... ) -> bool: ... -def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... +def circular_shifts(iterable: Iterable[_T]) -> list[tuple[_T, ...]]: ... def make_decorator( wrapping_func: Callable[..., _U], result_index: int = ... ) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... @@ -467,44 +460,44 @@ keyfunc: Callable[[_T], _U], valuefunc: None = ..., reducefunc: None = ..., -) -> Dict[_U, List[_T]]: ... +) -> dict[_U, list[_T]]: ... @overload def map_reduce( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: Callable[[_T], _V], reducefunc: None = ..., -) -> Dict[_U, List[_V]]: ... +) -> dict[_U, list[_V]]: ... @overload def map_reduce( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: None = ..., - reducefunc: Callable[[List[_T]], _W] = ..., -) -> Dict[_U, _W]: ... + reducefunc: Callable[[list[_T]], _W] = ..., +) -> dict[_U, _W]: ... @overload def map_reduce( iterable: Iterable[_T], keyfunc: Callable[[_T], _U], valuefunc: Callable[[_T], _V], - reducefunc: Callable[[List[_V]], _W], -) -> Dict[_U, _W]: ... + reducefunc: Callable[[list[_V]], _W], +) -> dict[_U, _W]: ... def rlocate( iterable: Iterable[_T], pred: Callable[..., object] = ..., - window_size: Optional[int] = ..., + window_size: int | None = ..., ) -> Iterator[int]: ... def replace( iterable: Iterable[_T], pred: Callable[..., object], substitutes: Iterable[_U], - count: Optional[int] = ..., + count: int | None = ..., window_size: int = ..., -) -> Iterator[Union[_T, _U]]: ... -def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... +) -> Iterator[_T | _U]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[list[list[_T]]]: ... def set_partitions( - iterable: Iterable[_T], k: Optional[int] = ... -) -> Iterator[List[List[_T]]]: ... + iterable: Iterable[_T], k: int | None = ... +) -> Iterator[list[list[_T]]]: ... class time_limited(Generic[_T], Iterator[_T]): def __init__( @@ -515,16 +508,16 @@ @overload def only( - iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... -) -> Optional[_T]: ... + iterable: Iterable[_T], *, too_long: _Raisable | None = ... +) -> _T | None: ... @overload def only( - iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... -) -> Union[_T, _U]: ... + iterable: Iterable[_T], default: _U, too_long: _Raisable | None = ... +) -> _T | _U: ... def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... def distinct_combinations( iterable: Iterable[_T], r: int -) -> Iterator[Tuple[_T, ...]]: ... +) -> Iterator[tuple[_T, ...]]: ... def filter_except( validator: Callable[[Any], object], iterable: Iterable[_T], @@ -539,16 +532,16 @@ iterable: Iterable[Any], pred: Callable[[Any], bool], func: Callable[[Any], Any], - func_else: Optional[Callable[[Any], Any]] = ..., + func_else: Callable[[Any], Any] | None = ..., ) -> Iterator[Any]: ... def sample( iterable: Iterable[_T], k: int, - weights: Optional[Iterable[float]] = ..., -) -> List[_T]: ... + weights: Iterable[float] | None = ..., +) -> list[_T]: ... def is_sorted( iterable: Iterable[_T], - key: Optional[Callable[[_T], _U]] = ..., + key: Callable[[_T], _U] | None = ..., reverse: bool = False, strict: bool = False, ) -> bool: ... @@ -566,10 +559,10 @@ def __enter__(self) -> callback_iter[_T]: ... def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> Optional[bool]: ... + exc_type: Type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> bool | None: ... def __iter__(self) -> callback_iter[_T]: ... def __next__(self) -> _T: ... def _reader(self) -> Iterator[_T]: ... @@ -580,15 +573,15 @@ def windowed_complete( iterable: Iterable[_T], n: int -) -> Iterator[Tuple[_T, ...]]: ... +) -> Iterator[tuple[_T, ...]]: ... def all_unique( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... ) -> bool: ... -def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... +def nth_product(index: int, *args: Iterable[_T]) -> tuple[_T, ...]: ... def nth_permutation( iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... +) -> tuple[_T, ...]: ... +def value_chain(*args: _T | Iterable[_T]) -> Iterable[_T]: ... def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... def combination_index( element: Iterable[_T], iterable: Iterable[_T] @@ -603,22 +596,20 @@ def __iter__(self) -> countable[_T]: ... def __next__(self) -> _T: ... -def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[List[_T]]: ... +def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[list[_T]]: ... def zip_broadcast( - *objects: Union[_T, Iterable[_T]], - scalar_types: Union[ - type, Tuple[Union[type, Tuple[Any, ...]], ...], None - ] = ..., + *objects: _T | Iterable[_T], + scalar_types: type | tuple[type | tuple[Any, ...], ...] | None = ..., strict: bool = ..., -) -> Iterable[Tuple[_T, ...]]: ... +) -> Iterable[tuple[_T, ...]]: ... def unique_in_window( - iterable: Iterable[_T], n: int, key: Optional[Callable[[_T], _U]] = ... + iterable: Iterable[_T], n: int, key: Callable[[_T], _U] | None = ... ) -> Iterator[_T]: ... def duplicates_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... ) -> Iterator[_T]: ... def duplicates_justseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... ) -> Iterator[_T]: ... class _SupportsLessThan(Protocol): @@ -629,38 +620,38 @@ @overload def minmax( iterable_or_value: Iterable[_SupportsLessThanT], *, key: None = None -) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... @overload def minmax( iterable_or_value: Iterable[_T], *, key: Callable[[_T], _SupportsLessThan] -) -> Tuple[_T, _T]: ... +) -> tuple[_T, _T]: ... @overload def minmax( iterable_or_value: Iterable[_SupportsLessThanT], *, key: None = None, default: _U, -) -> Union[_U, Tuple[_SupportsLessThanT, _SupportsLessThanT]]: ... +) -> _U | tuple[_SupportsLessThanT, _SupportsLessThanT]: ... @overload def minmax( iterable_or_value: Iterable[_T], *, key: Callable[[_T], _SupportsLessThan], default: _U, -) -> Union[_U, Tuple[_T, _T]]: ... +) -> _U | tuple[_T, _T]: ... @overload def minmax( iterable_or_value: _SupportsLessThanT, __other: _SupportsLessThanT, *others: _SupportsLessThanT, -) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... @overload def minmax( iterable_or_value: _T, __other: _T, *others: _T, key: Callable[[_T], _SupportsLessThan], -) -> Tuple[_T, _T]: ... +) -> tuple[_T, _T]: ... def longest_common_prefix( iterables: Iterable[Iterable[_T]], ) -> Iterator[_T]: ... @@ -668,7 +659,8 @@ def constrained_batches( iterable: Iterable[object], max_size: int, - max_count: Optional[int] = ..., + max_count: int | None = ..., get_len: Callable[[_T], object] = ..., strict: bool = ..., -) -> Iterator[Tuple[_T]]: ... +) -> Iterator[tuple[_T]]: ... +def gray_product(*iterables: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/more_itertools/recipes.py new/more-itertools-9.1.0/more_itertools/recipes.py --- old/more-itertools-9.0.0/more_itertools/recipes.py 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/more_itertools/recipes.py 2023-02-21 16:05:46.940384600 +0100 @@ -9,6 +9,7 @@ """ import math import operator +import warnings from collections import deque from collections.abc import Sized @@ -21,12 +22,14 @@ cycle, groupby, islice, + product, repeat, starmap, tee, zip_longest, ) from random import randrange, sample, choice +from sys import hexversion __all__ = [ 'all_equal', @@ -36,9 +39,12 @@ 'convolve', 'dotproduct', 'first_true', + 'factor', 'flatten', 'grouper', 'iter_except', + 'iter_index', + 'matmul', 'ncycles', 'nth', 'nth_combination', @@ -62,6 +68,7 @@ 'tabulate', 'tail', 'take', + 'transpose', 'triplewise', 'unique_everseen', 'unique_justseen', @@ -808,6 +815,35 @@ ] +def iter_index(iterable, value, start=0): + """Yield the index of each place in *iterable* that *value* occurs, + beginning with index *start*. + + See :func:`locate` for a more general means of finding the indexes + associated with particular values. + + >>> list(iter_index('AABCADEAF', 'A')) + [0, 1, 4, 7] + """ + try: + seq_index = iterable.index + except AttributeError: + # Slow path for general iterables + it = islice(iterable, start, None) + for i, element in enumerate(it, start): + if element is value or element == value: + yield i + else: + # Fast path for sequences + i = start - 1 + try: + while True: + i = seq_index(value, i + 1) + yield i + except ValueError: + pass + + def sieve(n): """Yield the primes less than n. @@ -815,13 +851,13 @@ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] """ isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x))) + data = bytearray((0, 1)) * (n // 2) + data[:3] = 0, 0, 0 limit = isqrt(n) + 1 - data = bytearray([1]) * n - data[:2] = 0, 0 for p in compress(range(limit), data): - data[p + p : n : p] = bytearray(len(range(p + p, n, p))) - - return compress(count(), data) + data[p * p : n : p + p] = bytes(len(range(p * p, n, p + p))) + data[2] = 1 + return iter_index(data, 1) if n > 2 else iter([]) def batched(iterable, n): @@ -833,9 +869,62 @@ This recipe is from the ``itertools`` docs. This library also provides :func:`chunked`, which has a different implementation. """ + if hexversion >= 0x30C00A0: # Python 3.12.0a0 + warnings.warn( + ( + 'batched will be removed in a future version of ' + 'more-itertools. Use the standard library ' + 'itertools.batched function instead' + ), + DeprecationWarning, + ) + it = iter(iterable) while True: batch = list(islice(it, n)) if not batch: break yield batch + + +def transpose(it): + """Swap the rows and columns of the input. + + >>> list(transpose([(1, 2, 3), (11, 22, 33)])) + [(1, 11), (2, 22), (3, 33)] + + The caller should ensure that the dimensions of the input are compatible. + """ + # TODO: when 3.9 goes end-of-life, add stric=True to this. + return zip(*it) + + +def matmul(m1, m2): + """Multiply two matrices. + >>> list(matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)])) + [[49, 80], [41, 60]] + + The caller should ensure that the dimensions of the input matrices are + compatible with each other. + """ + n = len(m2[0]) + return batched(starmap(dotproduct, product(m1, transpose(m2))), n) + + +def factor(n): + """Yield the prime factors of n. + >>> list(factor(360)) + [2, 2, 2, 3, 3, 5] + """ + isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x))) + for prime in sieve(isqrt(n) + 1): + while True: + quotient, remainder = divmod(n, prime) + if remainder: + break + yield prime + n = quotient + if n == 1: + return + if n >= 2: + yield n diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/more_itertools/recipes.pyi new/more-itertools-9.1.0/more_itertools/recipes.pyi --- old/more-itertools-9.0.0/more_itertools/recipes.pyi 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/more_itertools/recipes.pyi 2023-02-21 16:05:46.943320300 +0100 @@ -1,110 +1,119 @@ """Stubs for more_itertools.recipes""" +from __future__ import annotations + from typing import ( Any, Callable, Iterable, Iterator, - List, - Optional, + overload, Sequence, - Tuple, + Type, TypeVar, - Union, ) -from typing_extensions import overload, Type # Type and type variable definitions _T = TypeVar('_T') _U = TypeVar('_U') -def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... +def take(n: int, iterable: Iterable[_T]) -> list[_T]: ... def tabulate( function: Callable[[int], _T], start: int = ... ) -> Iterator[_T]: ... def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... -def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... +def consume(iterator: Iterable[object], n: int | None = ...) -> None: ... @overload -def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... +def nth(iterable: Iterable[_T], n: int) -> _T | None: ... @overload -def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... +def nth(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... def all_equal(iterable: Iterable[object]) -> bool: ... def quantify( iterable: Iterable[_T], pred: Callable[[_T], bool] = ... ) -> int: ... -def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... -def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[_T | None]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[_T | None]: ... def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... def repeatfunc( - func: Callable[..., _U], times: Optional[int] = ..., *args: Any + func: Callable[..., _U], times: int | None = ..., *args: Any ) -> Iterator[_U]: ... -def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T]]: ... def grouper( iterable: Iterable[_T], n: int, incomplete: str = ..., fillvalue: _U = ..., -) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +) -> Iterator[tuple[_T | _U, ...]]: ... def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... def partition( - pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] -) -> Tuple[Iterator[_T], Iterator[_T]]: ... -def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... + pred: Callable[[_T], object] | None, iterable: Iterable[_T] +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... def unique_everseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... ) -> Iterator[_T]: ... def unique_justseen( - iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... + iterable: Iterable[_T], key: Callable[[_T], object] | None = ... ) -> Iterator[_T]: ... @overload def iter_except( func: Callable[[], _T], - exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], + exception: Type[BaseException] | tuple[Type[BaseException], ...], first: None = ..., ) -> Iterator[_T]: ... @overload def iter_except( func: Callable[[], _T], - exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], + exception: Type[BaseException] | tuple[Type[BaseException], ...], first: Callable[[], _U], -) -> Iterator[Union[_T, _U]]: ... +) -> Iterator[_T | _U]: ... @overload def first_true( - iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... -) -> Optional[_T]: ... + iterable: Iterable[_T], *, pred: Callable[[_T], object] | None = ... +) -> _T | None: ... @overload def first_true( iterable: Iterable[_T], default: _U, - pred: Optional[Callable[[_T], object]] = ..., -) -> Union[_T, _U]: ... + pred: Callable[[_T], object] | None = ..., +) -> _T | _U: ... def random_product( *args: Iterable[_T], repeat: int = ... -) -> Tuple[_T, ...]: ... +) -> tuple[_T, ...]: ... def random_permutation( - iterable: Iterable[_T], r: Optional[int] = ... -) -> Tuple[_T, ...]: ... -def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... + iterable: Iterable[_T], r: int | None = ... +) -> tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> tuple[_T, ...]: ... def random_combination_with_replacement( iterable: Iterable[_T], r: int -) -> Tuple[_T, ...]: ... +) -> tuple[_T, ...]: ... def nth_combination( iterable: Iterable[_T], r: int, index: int -) -> Tuple[_T, ...]: ... -def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... +) -> tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[_T | _U]: ... def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... def before_and_after( predicate: Callable[[_T], bool], it: Iterable[_T] -) -> Tuple[Iterator[_T], Iterator[_T]]: ... -def triplewise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T, _T]]: ... +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def triplewise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T, _T]]: ... def sliding_window( iterable: Iterable[_T], n: int -) -> Iterator[Tuple[_T, ...]]: ... -def subslices(iterable: Iterable[_T]) -> Iterator[List[_T]]: ... -def polynomial_from_roots(roots: Sequence[int]) -> List[int]: ... +) -> Iterator[tuple[_T, ...]]: ... +def subslices(iterable: Iterable[_T]) -> Iterator[list[_T]]: ... +def polynomial_from_roots(roots: Sequence[int]) -> list[int]: ... +def iter_index( + iterable: Iterable[object], + value: Any, + start: int | None = ..., +) -> Iterator[int]: ... def sieve(n: int) -> Iterator[int]: ... def batched( iterable: Iterable[_T], n: int, -) -> Iterator[List[_T]]: ... +) -> Iterator[list[_T]]: ... +def transpose( + it: Iterable[Iterable[_T]], +) -> tuple[Iterator[_T], ...]: ... +def matmul(m1: Sequence[_T], m2: Sequence[_T]) -> Iterator[list[_T]]: ... +def factor(n: int) -> Iterator[int]: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/setup.cfg new/more-itertools-9.1.0/setup.cfg --- old/more-itertools-9.0.0/setup.cfg 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/setup.cfg 2023-02-27 15:41:12.554733800 +0100 @@ -1,5 +1,5 @@ [bumpversion] -current_version = 9.0.0 +current_version = 9.1.0 commit = True tag = False files = more_itertools/__init__.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/tests/test_more.py new/more-itertools-9.1.0/tests/test_more.py --- old/more-itertools-9.0.0/tests/test_more.py 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/tests/test_more.py 2023-01-13 21:05:28.316245600 +0100 @@ -1448,6 +1448,10 @@ ('a,b,c,d', lambda c: c != ',', 2), [['a'], [',', 'b'], [',', 'c', ',', 'd']], ), + ( + ([1], lambda x: x == 1, 1), + [[1]], + ), ]: actual = list(mi.split_after(*args)) self.assertEqual(actual, expected) @@ -5174,3 +5178,53 @@ ), [(record_3, record_5), (record_10,), (record_2,)], ) + + +class GrayProductTests(TestCase): + def test_basic(self): + self.assertEqual( + tuple(mi.gray_product(('a', 'b', 'c'), range(1, 3))), + (("a", 1), ("b", 1), ("c", 1), ("c", 2), ("b", 2), ("a", 2)), + ) + out = mi.gray_product(('foo', 'bar'), (3, 4, 5, 6), ['quz', 'baz']) + self.assertEqual(next(out), ('foo', 3, 'quz')) + self.assertEqual( + list(out), + [ + ('bar', 3, 'quz'), + ('bar', 4, 'quz'), + ('foo', 4, 'quz'), + ('foo', 5, 'quz'), + ('bar', 5, 'quz'), + ('bar', 6, 'quz'), + ('foo', 6, 'quz'), + ('foo', 6, 'baz'), + ('bar', 6, 'baz'), + ('bar', 5, 'baz'), + ('foo', 5, 'baz'), + ('foo', 4, 'baz'), + ('bar', 4, 'baz'), + ('bar', 3, 'baz'), + ('foo', 3, 'baz'), + ], + ) + self.assertEqual(tuple(mi.gray_product()), ((),)) + self.assertEqual(tuple(mi.gray_product((1, 2))), ((1,), (2,))) + + def test_errors(self): + with self.assertRaises(ValueError): + list(mi.gray_product((1, 2), ())) + with self.assertRaises(ValueError): + list(mi.gray_product((1, 2), (2,))) + + def test_vs_product(self): + iters = ( + ("a", "b"), + range(3, 6), + [None, None], + {"i", "j", "k", "l"}, + "XYZ", + ) + self.assertEqual( + sorted(product(*iters)), sorted(mi.gray_product(*iters)) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/more-itertools-9.0.0/tests/test_recipes.py new/more-itertools-9.1.0/tests/test_recipes.py --- old/more-itertools-9.0.0/tests/test_recipes.py 2022-10-18 15:38:19.073834700 +0200 +++ new/more-itertools-9.1.0/tests/test_recipes.py 2023-02-21 16:05:46.945628600 +0100 @@ -1,6 +1,7 @@ from doctest import DocTestSuite from functools import reduce from itertools import combinations, count, permutations +from operator import mul from math import factorial from unittest import TestCase @@ -880,6 +881,35 @@ self.assertEqual(actual, expected) +class IterIndexTests(TestCase): + def test_basic(self): + iterable = 'AABCADEAF' + for wrapper in (list, iter): + with self.subTest(wrapper=wrapper): + actual = list(mi.iter_index(wrapper(iterable), 'A')) + expected = [0, 1, 4, 7] + self.assertEqual(actual, expected) + + def test_start(self): + for wrapper in (list, iter): + with self.subTest(wrapper=wrapper): + iterable = 'AABCADEAF' + i = -1 + actual = [] + while True: + try: + i = next( + mi.iter_index(wrapper(iterable), 'A', start=i + 1) + ) + except StopIteration: + break + else: + actual.append(i) + + expected = [0, 1, 4, 7] + self.assertEqual(actual, expected) + + class SieveTests(TestCase): def test_basic(self): self.assertEqual( @@ -942,3 +972,72 @@ with self.subTest(n=n): actual = list(mi.batched(iterable, n)) self.assertEqual(actual, expected) + + +class TransposeTests(TestCase): + def test_empty(self): + it = [] + actual = list(mi.transpose(it)) + expected = [] + self.assertEqual(actual, expected) + + def test_basic(self): + it = [(10, 11, 12), (20, 21, 22), (30, 31, 32)] + actual = list(mi.transpose(it)) + expected = [(10, 20, 30), (11, 21, 31), (12, 22, 32)] + self.assertEqual(actual, expected) + + def test_incompatible(self): + it = [(10, 11, 12, 13), (20, 21, 22), (30, 31, 32)] + actual = list(mi.transpose(it)) + expected = [(10, 20, 30), (11, 21, 31), (12, 22, 32)] + self.assertEqual(actual, expected) + + +class MatMulTests(TestCase): + def test_n_by_n(self): + actual = list(mi.matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]])) + expected = [[49, 80], [41, 60]] + self.assertEqual(actual, expected) + + def test_m_by_n(self): + m1 = [[2, 5], [7, 9], [3, 4]] + m2 = [[7, 11, 5, 4, 9], [3, 5, 2, 6, 3]] + actual = list(mi.matmul(m1, m2)) + expected = [ + [29, 47, 20, 38, 33], + [76, 122, 53, 82, 90], + [33, 53, 23, 36, 39], + ] + self.assertEqual(actual, expected) + + +class FactorTests(TestCase): + def test_basic(self): + for n, expected in ( + (0, []), + (1, []), + (2, [2]), + (3, [3]), + (4, [2, 2]), + (6, [2, 3]), + (360, [2, 2, 2, 3, 3, 5]), + (128_884_753_939, [128_884_753_939]), + (999953 * 999983, [999953, 999983]), + (909_909_090_909, [3, 3, 7, 13, 13, 751, 113797]), + ): + with self.subTest(n=n): + actual = list(mi.factor(n)) + self.assertEqual(actual, expected) + + def test_cross_check(self): + prod = lambda x: reduce(mul, x, 1) + self.assertTrue(all(prod(mi.factor(n)) == n for n in range(1, 2000))) + self.assertTrue( + all(set(mi.factor(n)) <= set(mi.sieve(n + 1)) for n in range(2000)) + ) + self.assertTrue( + all( + list(mi.factor(n)) == sorted(mi.factor(n)) for n in range(2000) + ) + )