Hello community,
here is the log from the commit of package python-more-itertools for
openSUSE:Factory checked in at 2020-06-05 19:59:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-more-itertools (Old)
and /work/SRC/openSUSE:Factory/.python-more-itertools.new.3606 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-more-itertools"
Fri Jun 5 19:59:36 2020 rev:10 rq:810900 version:8.3.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-more-itertools/python-more-itertools.changes
2020-03-08 22:21:40.179986101 +0100
+++
/work/SRC/openSUSE:Factory/.python-more-itertools.new.3606/python-more-itertools.changes
2020-06-05 19:59:38.319819232 +0200
@@ -1,0 +2,14 @@
+Tue Jun 2 16:30:25 UTC 2020 - Dirk Mueller <[email protected]>
+
+- update to 8.3.0:
+ * New itertools
+ * :func:`zip_equal` (thanks to frankier and alexmojaki)
+ * :func:`split_at`, :func:`split_before`, :func:`split_after`, and
:func:`split_when` all got a ``maxsplit`` paramter (thanks to jferard and
ilai-deutel)
+ * :func:`split_at` now accepts a ``keep_separator`` parameter (thanks to
jferard)
+ * :func:`distinct_permutations` can now generate ``r``-length permutations
(thanks to SergBobrovsky and ilai-deutel)
+ * The :func:`windowed` implementation was improved (thanks to
SergBobrovsky)
+ * The :func:`spy` implementation was improved (thanks to has2k1)
+ * Type stubs are now tested with ``stubtest`` (thankjs to ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python setup.py
test`` (thanks to jdufresne)
+
+-------------------------------------------------------------------
Old:
----
more-itertools-8.2.0.tar.gz
New:
----
more-itertools-8.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-more-itertools.spec ++++++
--- /var/tmp/diff_new_pack.6XiiOg/_old 2020-06-05 19:59:38.891821211 +0200
+++ /var/tmp/diff_new_pack.6XiiOg/_new 2020-06-05 19:59:38.891821211 +0200
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-more-itertools
-Version: 8.2.0
+Version: 8.3.0
Release: 0
Summary: More routines for operating on iterables, beyond itertools
License: MIT
++++++ more-itertools-8.2.0.tar.gz -> more-itertools-8.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/PKG-INFO
new/more-itertools-8.3.0/PKG-INFO
--- old/more-itertools-8.2.0/PKG-INFO 2020-01-29 13:52:26.000000000 +0100
+++ new/more-itertools-8.3.0/PKG-INFO 2020-05-16 03:25:21.078277000 +0200
@@ -1,8 +1,8 @@
Metadata-Version: 1.2
Name: more-itertools
-Version: 8.2.0
+Version: 8.3.0
Summary: More routines for operating on iterables, beyond itertools
-Home-page: https://github.com/erikrose/more-itertools
+Home-page: https://github.com/more-itertools/more-itertools
Author: Erik Rose
Author-email: [email protected]
License: MIT
@@ -10,16 +10,17 @@
More Itertools
==============
- .. image::
https://coveralls.io/repos/github/erikrose/more-itertools/badge.svg?branch=master
- :target:
https://coveralls.io/github/erikrose/more-itertools?branch=master
+ .. image::
https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+ .. image::
https://coveralls.io/repos/github/more-itertools/more-itertools/badge.svg?branch=master
+ :target:
https://coveralls.io/github/more-itertools/more-itertools?branch=master
Python's ``itertools`` library is a gem - you can compose elegant
solutions
for a variety of problems with the functions it provides. In
``more-itertools``
we collect additional building blocks, recipes, and routines for
working with
Python iterables.
- ----
-
+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grouping | `chunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_,
|
| | `ichunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_,
|
@@ -60,6 +61,7 @@
| | `interleave
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_,
|
| | `interleave_longest
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_,
|
| | `zip_offset
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_,
|
+ | | `zip_equal
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_,
|
| | `dotproduct
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_,
|
| | `flatten
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_,
|
| | `roundrobin
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_,
|
@@ -165,13 +167,23 @@
- For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/latest/api.html>`_.
+ For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+ Links elsewhere
+ ===============
+
+ Blog posts about ``more-itertools``:
+
+ * `Yo, I heard you like decorators
<https://bbayles.com/index/decorator_factory>`_
+ * `Tour of Python Itertools <https://martinheinz.dev/blog/16>`_
+
Development
===========
``more-itertools`` is maintained by `@erikrose
<https://github.com/erikrose>`_
- and `@bbayles <https://github.com/bbayles>`_, with help from `many
others <https://github.com/erikrose/more-itertools/graphs/contributors>`_.
+ and `@bbayles <https://github.com/bbayles>`_, with help from `many
others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
If you have a problem or suggestion, please file a bug or pull request
in this
repository. Thanks for contributing!
@@ -182,6 +194,23 @@
:noindex:
+ 8.3.0
+ -----
+
+ * New itertools
+ * zip_equal (thanks to frankier and alexmojaki)
+
+ * Changes to existing itertools:
+ * split_at, split_before, split_after, and split_when all got a
``maxsplit`` paramter (thanks to jferard and ilai-deutel)
+ * split_at now accepts a ``keep_separator`` parameter (thanks to
jferard)
+ * distinct_permutations can now generate ``r``-length permutations
(thanks to SergBobrovsky and ilai-deutel)
+ * The windowed implementation was improved (thanks to
SergBobrovsky)
+ * The spy implementation was improved (thanks to has2k1)
+
+ * Other changes
+ * Type stubs are now tested with ``stubtest`` (thankjs to
ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python
setup.py test`` (thanks to jdufresne)
+
8.2.0
-----
@@ -235,7 +264,6 @@
* unique_everseen and groupby_transform were re-factored. (thanks
to SergBobrovsky)
* The implementation for difference was improved. (thanks to
Jabbey92)
-
* Other changes
* Python 3.4 has reached its end of life and is no longer
supported.
* Python 3.8 is officially supported. (thanks to jdufresne)
@@ -382,7 +410,7 @@
* New itertools:
* split_at (thanks to michael-celani)
* circular_shifts (thanks to hiqua)
- * make_decorator - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`_
+ * make_decorator - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`__
for a tour (thanks to pylang)
* always_reversible (thanks to michael-celani)
* nth_combination (from the `Python 3.7 docs
<https://docs.python.org/3.7/library/itertools.html#itertools-recipes>`_)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/README.rst
new/more-itertools-8.3.0/README.rst
--- old/more-itertools-8.2.0/README.rst 2020-01-11 19:59:09.000000000 +0100
+++ new/more-itertools-8.3.0/README.rst 2020-05-08 04:42:06.000000000 +0200
@@ -2,16 +2,17 @@
More Itertools
==============
-.. image::
https://coveralls.io/repos/github/erikrose/more-itertools/badge.svg?branch=master
- :target: https://coveralls.io/github/erikrose/more-itertools?branch=master
+.. image::
https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+.. image::
https://coveralls.io/repos/github/more-itertools/more-itertools/badge.svg?branch=master
+ :target:
https://coveralls.io/github/more-itertools/more-itertools?branch=master
Python's ``itertools`` library is a gem - you can compose elegant solutions
for a variety of problems with the functions it provides. In ``more-itertools``
we collect additional building blocks, recipes, and routines for working with
Python iterables.
-----
-
+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grouping | `chunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_,
|
| | `ichunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_,
|
@@ -52,6 +53,7 @@
| | `interleave
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_,
|
| | `interleave_longest
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_,
|
| | `zip_offset
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_,
|
+| | `zip_equal
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_,
|
| | `dotproduct
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_,
|
| | `flatten
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_,
|
| | `roundrobin
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_,
|
@@ -157,12 +159,22 @@
-For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/latest/api.html>`_.
+For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+Links elsewhere
+===============
+
+Blog posts about ``more-itertools``:
+
+* `Yo, I heard you like decorators
<https://bbayles.com/index/decorator_factory>`_
+* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`_
+
Development
===========
``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
-and `@bbayles <https://github.com/bbayles>`_, with help from `many others
<https://github.com/erikrose/more-itertools/graphs/contributors>`_.
+and `@bbayles <https://github.com/bbayles>`_, with help from `many others
<https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
If you have a problem or suggestion, please file a bug or pull request in this
repository. Thanks for contributing!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/docs/api.rst
new/more-itertools-8.3.0/docs/api.rst
--- old/more-itertools-8.2.0/docs/api.rst 2020-01-11 19:59:09.000000000
+0100
+++ new/more-itertools-8.3.0/docs/api.rst 2020-05-08 04:41:55.000000000
+0200
@@ -108,6 +108,7 @@
.. autofunction:: interleave
.. autofunction:: interleave_longest
.. autofunction:: zip_offset(*iterables, offsets, longest=False,
fillvalue=None)
+.. autofunction:: zip_equal
----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/docs/testing.rst
new/more-itertools-8.3.0/docs/testing.rst
--- old/more-itertools-8.2.0/docs/testing.rst 2018-11-12 02:37:51.000000000
+0100
+++ new/more-itertools-8.3.0/docs/testing.rst 2020-03-01 16:31:34.000000000
+0100
@@ -4,7 +4,7 @@
To run install dependencies and run tests, use this command::
- python setup.py test
+ python -m unittest
Multiple Python Versions
========================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/docs/versions.rst
new/more-itertools-8.3.0/docs/versions.rst
--- old/more-itertools-8.2.0/docs/versions.rst 2020-01-29 13:49:15.000000000
+0100
+++ new/more-itertools-8.3.0/docs/versions.rst 2020-05-16 03:24:56.000000000
+0200
@@ -5,6 +5,23 @@
.. automodule:: more_itertools
:noindex:
+8.3.0
+-----
+
+* New itertools
+ * :func:`zip_equal` (thanks to frankier and alexmojaki)
+
+* Changes to existing itertools:
+ * :func:`split_at`, :func:`split_before`, :func:`split_after`, and
:func:`split_when` all got a ``maxsplit`` paramter (thanks to jferard and
ilai-deutel)
+ * :func:`split_at` now accepts a ``keep_separator`` parameter (thanks to
jferard)
+ * :func:`distinct_permutations` can now generate ``r``-length permutations
(thanks to SergBobrovsky and ilai-deutel)
+ * The :func:`windowed` implementation was improved (thanks to
SergBobrovsky)
+ * The :func:`spy` implementation was improved (thanks to has2k1)
+
+* Other changes
+ * Type stubs are now tested with ``stubtest`` (thankjs to ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python setup.py
test`` (thanks to jdufresne)
+
8.2.0
-----
@@ -58,7 +75,6 @@
* :func:`unique_everseen` and :func:`groupby_transform` were re-factored.
(thanks to SergBobrovsky)
* The implementation for :func:`difference` was improved. (thanks to
Jabbey92)
-
* Other changes
* Python 3.4 has reached its end of life and is no longer supported.
* Python 3.8 is officially supported. (thanks to jdufresne)
@@ -205,7 +221,7 @@
* New itertools:
* :func:`split_at` (thanks to michael-celani)
* :func:`circular_shifts` (thanks to hiqua)
- * :func:`make_decorator` - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`_
+ * :func:`make_decorator` - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`__
for a tour (thanks to pylang)
* :func:`always_reversible` (thanks to michael-celani)
* :func:`nth_combination` (from the `Python 3.7 docs
<https://docs.python.org/3.7/library/itertools.html#itertools-recipes>`_)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/more_itertools/__init__.py
new/more-itertools-8.3.0/more_itertools/__init__.py
--- old/more-itertools-8.2.0/more_itertools/__init__.py 2020-01-29
13:51:50.000000000 +0100
+++ new/more-itertools-8.3.0/more_itertools/__init__.py 2020-05-16
03:19:57.000000000 +0200
@@ -1,4 +1,4 @@
from .more import * # noqa
from .recipes import * # noqa
-__version__ = '8.2.0'
+__version__ = '8.3.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/more_itertools/more.py
new/more-itertools-8.3.0/more_itertools/more.py
--- old/more-itertools-8.2.0/more_itertools/more.py 2020-01-29
13:49:15.000000000 +0100
+++ new/more-itertools-8.3.0/more_itertools/more.py 2020-05-16
03:19:57.000000000 +0200
@@ -93,6 +93,8 @@
'unzip',
'windowed',
'with_iter',
+ 'UnequalIterablesError',
+ 'zip_equal',
'zip_offset',
]
@@ -545,7 +547,7 @@
return first_value
-def distinct_permutations(iterable):
+def distinct_permutations(iterable, r=None):
"""Yield successive distinct permutations of the elements in *iterable*.
>>> sorted(distinct_permutations([1, 0, 1]))
@@ -561,30 +563,86 @@
items input, and each `x_i` is the count of a distinct item in the input
sequence.
+ If *r* is given, only the *r*-length permutations are yielded.
+
+ >>> sorted(distinct_permutations([1, 0, 1], r=3))
+ [(0, 1, 1), (1, 0, 1), (1, 1, 0)]
+
"""
+ # Algorithm: https://w.wiki/Qai
+ def _full(A):
+ while True:
+ # Yield the permutation we have
+ yield tuple(A)
- def make_new_permutations(pool, e):
- """Internal helper function.
- The output permutations are built up by adding element *e* to the
- current *permutations* at every possible position.
- The key idea is to keep repeated elements (reverse) ordered:
- if e1 == e2 and e1 is before e2 in the iterable, then all permutations
- with e1 before e2 are ignored.
+ # Find the largest index i such that A[i] < A[i + 1]
+ for i in range(size - 2, -1, -1):
+ if A[i] < A[i + 1]:
+ break
+ # If no such index exists, this permutation is the last one
+ else:
+ return
- """
- for perm in pool:
- for j in range(len(perm)):
- yield perm[:j] + (e,) + perm[j:]
- if perm[j] == e:
+ # Find the largest index j greater than j such that A[i] < A[j]
+ for j in range(size - 1, i, -1):
+ if A[i] < A[j]:
+ break
+
+ # Swap the value of A[i] with that of A[j], then reverse the
+ # sequence from A[i + 1] to form the new permutation
+ A[i], A[j] = A[j], A[i]
+ A[i + 1:] = A[:i - size:-1] # A[i + 1:][::-1]
+
+ # Algorithm: modified from the above
+ def _partial(A, r):
+ # Split A into the first r items and the last r items
+ head, tail = A[:r], A[r:]
+ right_head_indexes = range(r - 1, -1, -1)
+ left_tail_indexes = range(len(tail))
+
+ while True:
+ # Yield the permutation we have
+ yield tuple(head)
+
+ # Starting from the right, find the first index of the head with
+ # value smaller than the maximum value of the tail - call it i.
+ pivot = tail[-1]
+ for i in right_head_indexes:
+ if head[i] < pivot:
+ break
+ pivot = head[i]
+ else:
+ return
+
+ # Starting from the left, find the first value of the tail
+ # with a value greater than head[i] and swap.
+ for j in left_tail_indexes:
+ if tail[j] > head[i]:
+ head[i], tail[j] = tail[j], head[i]
break
+ # If we didn't find one, start from the right and find the first
+ # index of the head with a value greater than head[i] and swap.
else:
- yield perm + (e,)
+ for j in right_head_indexes:
+ if head[j] > head[i]:
+ head[i], head[j] = head[j], head[i]
+ break
+
+ # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)]
+ tail += head[:i - r:-1] # head[i + 1:][::-1]
+ i += 1
+ head[i:], tail[:] = tail[:r - i], tail[r - i:]
+
+ items = sorted(iterable)
+
+ size = len(items)
+ if r is None:
+ r = size
- permutations = [()]
- for e in iterable:
- permutations = make_new_permutations(permutations, e)
+ if 0 < r <= size:
+ return _full(items) if (r == size) else _partial(items, r)
- return (tuple(t) for t in permutations)
+ return iter(() if r else ((),))
def intersperse(e, iterable, n=1):
@@ -654,7 +712,7 @@
[(1, 2, 3), (2, 3, 4), (3, 4, 5)]
When the window is larger than the iterable, *fillvalue* is used in place
- of missing values::
+ of missing values:
>>> list(windowed([1, 2, 3], 4))
[(1, 2, 3, None)]
@@ -672,7 +730,6 @@
>>> padding = [None] * (n - 1)
>>> list(windowed(chain(padding, iterable), 3))
[(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)]
-
"""
if n < 0:
raise ValueError('n must be >= 0')
@@ -682,28 +739,19 @@
if step < 1:
raise ValueError('step must be >= 1')
- it = iter(seq)
- window = deque([], n)
- append = window.append
-
- # Initial deque fill
- for _ in range(n):
- append(next(it, fillvalue))
- yield tuple(window)
-
- # Appending new items to the right causes old items to fall off the left
- i = 0
- for item in it:
- append(item)
- i = (i + 1) % step
- if i % step == 0:
+ window = deque(maxlen=n)
+ i = n
+ for _ in map(window.append, seq):
+ i -= 1
+ if not i:
+ i = step
yield tuple(window)
- # If there are items from the iterable in the window, pad with the given
- # value and emit them.
- if (i % step) and (step - i < n):
- for _ in range(step - i):
- append(fillvalue)
+ size = len(window)
+ if size < n:
+ yield tuple(chain(window, repeat(fillvalue, n - size)))
+ elif 0 < i < min(step, n):
+ window += (fillvalue,) * i
yield tuple(window)
@@ -903,7 +951,7 @@
it = iter(iterable)
head = take(n, it)
- return head, chain(head, it)
+ return head.copy(), chain(head, it)
def interleave(*iterables):
@@ -1064,28 +1112,51 @@
return takewhile(len, (seq[i : i + n] for i in count(0, n)))
-def split_at(iterable, pred):
+def split_at(iterable, pred, maxsplit=-1, keep_separator=False):
"""Yield lists of items from *iterable*, where each list is delimited by
- an item where callable *pred* returns ``True``. The lists do not include
- the delimiting items.
+ an item where callable *pred* returns ``True``.
>>> list(split_at('abcdcba', lambda x: x == 'b'))
[['a'], ['c', 'd', 'c'], ['a']]
>>> list(split_at(range(10), lambda n: n % 2 == 1))
[[0], [2], [4], [6], [8], []]
+
+ At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
+ then there is no limit on the number of splits:
+
+ >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2))
+ [[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``.
+
+ >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True))
+ [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']]
+
"""
+ if maxsplit == 0:
+ yield list(iterable)
+ return
+
buf = []
- for item in iterable:
+ it = iter(iterable)
+ for item in it:
if pred(item):
yield buf
+ if keep_separator:
+ yield [item]
+ if maxsplit == 1:
+ yield list(it)
+ return
buf = []
+ maxsplit -= 1
else:
buf.append(item)
yield buf
-def split_before(iterable, pred):
+def split_before(iterable, pred, maxsplit=-1):
"""Yield lists of items from *iterable*, where each list ends just before
an item for which callable *pred* returns ``True``:
@@ -1095,17 +1166,31 @@
>>> list(split_before(range(10), lambda n: n % 3 == 0))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
+ At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
+ then there is no limit on the number of splits:
+
+ >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2))
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
"""
+ if maxsplit == 0:
+ yield list(iterable)
+ return
+
buf = []
- for item in iterable:
+ it = iter(iterable)
+ for item in it:
if pred(item) and buf:
yield buf
+ if maxsplit == 1:
+ yield [item] + list(it)
+ return
buf = []
+ maxsplit -= 1
buf.append(item)
yield buf
-def split_after(iterable, pred):
+def split_after(iterable, pred, maxsplit=-1):
"""Yield lists of items from *iterable*, where each list ends with an
item where callable *pred* returns ``True``:
@@ -1115,18 +1200,33 @@
>>> list(split_after(range(10), lambda n: n % 3 == 0))
[[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
+ At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
+ then there is no limit on the number of splits:
+
+ >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2))
+ [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]]
+
"""
+ if maxsplit == 0:
+ yield list(iterable)
+ return
+
buf = []
- for item in iterable:
+ it = iter(iterable)
+ for item in it:
buf.append(item)
if pred(item) and buf:
yield buf
+ if maxsplit == 1:
+ yield list(it)
+ return
buf = []
+ maxsplit -= 1
if buf:
yield buf
-def split_when(iterable, pred):
+def split_when(iterable, pred, maxsplit=-1):
"""Split *iterable* into pieces based on the output of *pred*.
*pred* should be a function that takes successive pairs of items and
returns ``True`` if the iterable should be split in between them.
@@ -1136,7 +1236,19 @@
>>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y))
[[1, 2, 3, 3], [2, 5], [2, 4], [2]]
+
+ At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
+ then there is no limit on the number of splits:
+
+ >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2],
+ ... lambda x, y: x > y, maxsplit=2))
+ [[1, 2, 3, 3], [2, 5], [2, 4, 2]]
+
"""
+ if maxsplit == 0:
+ yield list(iterable)
+ return
+
it = iter(iterable)
try:
cur_item = next(it)
@@ -1147,7 +1259,11 @@
for next_item in it:
if pred(cur_item, next_item):
yield buf
+ if maxsplit == 1:
+ yield [next_item] + list(it)
+ return
buf = []
+ maxsplit -= 1
buf.append(next_item)
cur_item = next_item
@@ -1314,6 +1430,62 @@
)
+class UnequalIterablesError(ValueError):
+ def __init__(self, details=None):
+ msg = 'Iterables have different lengths'
+ if details is not None:
+ msg += (
+ ': index 0 has length {}; index {} has length {}'
+ ).format(*details)
+
+ super().__init__(msg)
+
+
+def zip_equal(*iterables):
+ """``zip`` the input *iterables* together, but raise
+ ``UnequalIterablesError`` if they aren't all the same length.
+
+ >>> it_1 = range(3)
+ >>> it_2 = iter('abc')
+ >>> list(zip_equal(it_1, it_2))
+ [(0, 'a'), (1, 'b'), (2, 'c')]
+
+ >>> it_1 = range(3)
+ >>> it_2 = iter('abcd')
+ >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ more_itertools.more.UnequalIterablesError: Iterables have different
+ lengths
+
+ """
+ # Check whether the iterables are all the same size.
+ try:
+ first_size = len(iterables[0])
+ for i, it in enumerate(iterables[1:], 1):
+ size = len(it)
+ if size != first_size:
+ break
+ else:
+ # If we didn't break out, we can use the built-in zip.
+ return zip(*iterables)
+
+ # If we did break out, there was a mismatch.
+ raise UnequalIterablesError(details=(first_size, i, size))
+ # If any one of the iterables didn't have a length, start reading
+ # them until one runs out.
+ except TypeError:
+ return _zip_equal_generator(iterables)
+
+
+def _zip_equal_generator(iterables):
+ for combo in zip_longest(*iterables, fillvalue=_marker):
+ for val in combo:
+ if val is _marker:
+ raise UnequalIterablesError()
+ yield combo
+
+
def zip_offset(*iterables, offsets, longest=False, fillvalue=None):
"""``zip`` the input *iterables* together, but offset the `i`-th iterable
by the `i`-th item in *offsets*.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/more-itertools-8.2.0/more_itertools.egg-info/PKG-INFO
new/more-itertools-8.3.0/more_itertools.egg-info/PKG-INFO
--- old/more-itertools-8.2.0/more_itertools.egg-info/PKG-INFO 2020-01-29
13:52:26.000000000 +0100
+++ new/more-itertools-8.3.0/more_itertools.egg-info/PKG-INFO 2020-05-16
03:25:20.000000000 +0200
@@ -1,8 +1,8 @@
Metadata-Version: 1.2
Name: more-itertools
-Version: 8.2.0
+Version: 8.3.0
Summary: More routines for operating on iterables, beyond itertools
-Home-page: https://github.com/erikrose/more-itertools
+Home-page: https://github.com/more-itertools/more-itertools
Author: Erik Rose
Author-email: [email protected]
License: MIT
@@ -10,16 +10,17 @@
More Itertools
==============
- .. image::
https://coveralls.io/repos/github/erikrose/more-itertools/badge.svg?branch=master
- :target:
https://coveralls.io/github/erikrose/more-itertools?branch=master
+ .. image::
https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+ .. image::
https://coveralls.io/repos/github/more-itertools/more-itertools/badge.svg?branch=master
+ :target:
https://coveralls.io/github/more-itertools/more-itertools?branch=master
Python's ``itertools`` library is a gem - you can compose elegant
solutions
for a variety of problems with the functions it provides. In
``more-itertools``
we collect additional building blocks, recipes, and routines for
working with
Python iterables.
- ----
-
+------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grouping | `chunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_,
|
| | `ichunked
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_,
|
@@ -60,6 +61,7 @@
| | `interleave
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_,
|
| | `interleave_longest
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_,
|
| | `zip_offset
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_,
|
+ | | `zip_equal
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_,
|
| | `dotproduct
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_,
|
| | `flatten
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_,
|
| | `roundrobin
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_,
|
@@ -165,13 +167,23 @@
- For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/latest/api.html>`_.
+ For the full listing of functions, see the `API documentation
<https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+ Links elsewhere
+ ===============
+
+ Blog posts about ``more-itertools``:
+
+ * `Yo, I heard you like decorators
<https://bbayles.com/index/decorator_factory>`_
+ * `Tour of Python Itertools <https://martinheinz.dev/blog/16>`_
+
Development
===========
``more-itertools`` is maintained by `@erikrose
<https://github.com/erikrose>`_
- and `@bbayles <https://github.com/bbayles>`_, with help from `many
others <https://github.com/erikrose/more-itertools/graphs/contributors>`_.
+ and `@bbayles <https://github.com/bbayles>`_, with help from `many
others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
If you have a problem or suggestion, please file a bug or pull request
in this
repository. Thanks for contributing!
@@ -182,6 +194,23 @@
:noindex:
+ 8.3.0
+ -----
+
+ * New itertools
+ * zip_equal (thanks to frankier and alexmojaki)
+
+ * Changes to existing itertools:
+ * split_at, split_before, split_after, and split_when all got a
``maxsplit`` paramter (thanks to jferard and ilai-deutel)
+ * split_at now accepts a ``keep_separator`` parameter (thanks to
jferard)
+ * distinct_permutations can now generate ``r``-length permutations
(thanks to SergBobrovsky and ilai-deutel)
+ * The windowed implementation was improved (thanks to
SergBobrovsky)
+ * The spy implementation was improved (thanks to has2k1)
+
+ * Other changes
+ * Type stubs are now tested with ``stubtest`` (thankjs to
ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python
setup.py test`` (thanks to jdufresne)
+
8.2.0
-----
@@ -235,7 +264,6 @@
* unique_everseen and groupby_transform were re-factored. (thanks
to SergBobrovsky)
* The implementation for difference was improved. (thanks to
Jabbey92)
-
* Other changes
* Python 3.4 has reached its end of life and is no longer
supported.
* Python 3.8 is officially supported. (thanks to jdufresne)
@@ -382,7 +410,7 @@
* New itertools:
* split_at (thanks to michael-celani)
* circular_shifts (thanks to hiqua)
- * make_decorator - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`_
+ * make_decorator - see the blog post `Yo, I heard you like
decorators <https://sites.google.com/site/bbayles/index/decorator_factory>`__
for a tour (thanks to pylang)
* always_reversible (thanks to michael-celani)
* nth_combination (from the `Python 3.7 docs
<https://docs.python.org/3.7/library/itertools.html#itertools-recipes>`_)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/setup.cfg
new/more-itertools-8.3.0/setup.cfg
--- old/more-itertools-8.2.0/setup.cfg 2020-01-29 13:52:26.000000000 +0100
+++ new/more-itertools-8.3.0/setup.cfg 2020-05-16 03:25:21.078277000 +0200
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 8.2.0
+current_version = 8.3.0
commit = True
tag = False
files = more_itertools/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/setup.py
new/more-itertools-8.3.0/setup.py
--- old/more-itertools-8.2.0/setup.py 2020-01-11 19:59:09.000000000 +0100
+++ new/more-itertools-8.3.0/setup.py 2020-05-16 03:22:50.000000000 +0200
@@ -34,8 +34,7 @@
package_data={'more_itertools': ['py.typed', '*.pyi']},
include_package_data=True,
python_requires='>=3.5',
- test_suite='tests',
- url='https://github.com/erikrose/more-itertools',
+ url='https://github.com/more-itertools/more-itertools',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/more-itertools-8.2.0/tests/test_more.py
new/more-itertools-8.3.0/tests/test_more.py
--- old/more-itertools-8.2.0/tests/test_more.py 2020-01-29 13:49:15.000000000
+0100
+++ new/more-itertools-8.3.0/tests/test_more.py 2020-05-09 04:19:44.000000000
+0200
@@ -526,6 +526,30 @@
ref_output = sorted(set(permutations(iterable)))
self.assertEqual(test_output, ref_output)
+ def test_r(self):
+ for iterable, r in (
+ ('mississippi', 0),
+ ('mississippi', 1),
+ ('mississippi', 6),
+ ('mississippi', 7),
+ ('mississippi', 12),
+ ([0, 1, 1, 0], 0),
+ ([0, 1, 1, 0], 1),
+ ([0, 1, 1, 0], 2),
+ ([0, 1, 1, 0], 3),
+ ([0, 1, 1, 0], 4),
+ (['a'], 0),
+ (['a'], 1),
+ (['a'], 5),
+ ([], 0),
+ ([], 1),
+ ([], 4),
+ ):
+ with self.subTest(iterable=iterable, r=r):
+ expected = sorted(set(permutations(iterable, r)))
+ actual = sorted(mi.distinct_permutations(iter(iterable), r))
+ self.assertEqual(actual, expected)
+
class IlenTests(TestCase):
def test_ilen(self):
@@ -909,6 +933,15 @@
self.assertEqual(head, [])
self.assertEqual(list(new_iterable), ['a', 'b', 'c'])
+ def test_immutable(self):
+ original_iterable = iter('abcdefg')
+ head, new_iterable = mi.spy(original_iterable, 3)
+ head[0] = 'A'
+ self.assertEqual(head, ['A', 'b', 'c'])
+ self.assertEqual(
+ list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g']
+ )
+
class InterleaveTests(TestCase):
def test_even(self):
@@ -1098,19 +1131,60 @@
class SplitAtTests(TestCase):
- """Tests for ``split()``"""
+ def test_basic(self):
+ for iterable, separator in [
+ ('a,bb,ccc,dddd', ','),
+ (',a,bb,ccc,dddd', ','),
+ ('a,bb,ccc,dddd,', ','),
+ ('a,bb,ccc,,dddd', ','),
+ ('', ','),
+ (',', ','),
+ ('a,bb,ccc,dddd', ';'),
+ ]:
+ with self.subTest(iterable=iterable, separator=separator):
+ it = iter(iterable)
+ pred = lambda x: x == separator
+ actual = [''.join(x) for x in mi.split_at(it, pred)]
+ expected = iterable.split(separator)
+ self.assertEqual(actual, expected)
+
+ def test_maxsplit(self):
+ iterable = 'a,bb,ccc,dddd'
+ separator = ','
+ pred = lambda x: x == separator
+
+ for maxsplit in range(-1, 4):
+ with self.subTest(maxsplit=maxsplit):
+ it = iter(iterable)
+ result = mi.split_at(it, pred, maxsplit=maxsplit)
+ actual = [''.join(x) for x in result]
+ expected = iterable.split(separator, maxsplit)
+ self.assertEqual(actual, expected)
+
+ def test_keep_separator(self):
+ separator = ','
+ pred = lambda x: x == separator
+
+ for iterable, expected in [
+ ('a,bb,ccc', ['a', ',', 'bb', ',', 'ccc']),
+ (',a,bb,ccc', ['', ',', 'a', ',', 'bb', ',', 'ccc']),
+ ('a,bb,ccc,', ['a', ',', 'bb', ',', 'ccc', ',', '']),
+ ]:
+ with self.subTest(iterable=iterable):
+ it = iter(iterable)
+ result = mi.split_at(it, pred, keep_separator=True)
+ actual = [''.join(x) for x in result]
+ self.assertEqual(actual, expected)
- def comp_with_str_split(self, str_to_split, delim):
- pred = lambda c: c == delim
- actual = list(map(''.join, mi.split_at(str_to_split, pred)))
- expected = str_to_split.split(delim)
+ def test_combination(self):
+ iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ pred = lambda x: x % 3 == 0
+ actual = list(
+ mi.split_at(iterable, pred, maxsplit=2, keep_separator=True)
+ )
+ expected = [[1, 2], [3], [4, 5], [6], [7, 8, 9, 10]]
self.assertEqual(actual, expected)
- def test_seperators(self):
- test_strs = ['', 'abcba', 'aaabbbcccddd', 'e']
- for s, delim in product(test_strs, 'abcd'):
- self.comp_with_str_split(s, delim)
-
class SplitBeforeTest(TestCase):
"""Tests for ``split_before()``"""
@@ -1130,6 +1204,26 @@
expected = [['o', 'o', 'o']]
self.assertEqual(actual, expected)
+ def test_max_split(self):
+ for args, expected in [
+ (('a,b,c,d', lambda c: c == ',', -1),
+ [['a'], [',', 'b'], [',', 'c'], [',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 0),
+ [['a', ',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 1),
+ [['a'], [',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 2),
+ [['a'], [',', 'b'], [',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 10),
+ [['a'], [',', 'b'], [',', 'c'], [',', 'd']]),
+ (('a,b,c,d', lambda c: c == '@', 2),
+ [['a', ',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c != ',', 2),
+ [['a', ','], ['b', ','], ['c', ',', 'd']]),
+ ]:
+ actual = list(mi.split_before(*args))
+ self.assertEqual(actual, expected)
+
class SplitAfterTest(TestCase):
"""Tests for ``split_after()``"""
@@ -1149,6 +1243,26 @@
expected = [['o', 'o', 'o']]
self.assertEqual(actual, expected)
+ def test_max_split(self):
+ for args, expected in [
+ (('a,b,c,d', lambda c: c == ',', -1),
+ [['a', ','], ['b', ','], ['c', ','], ['d']]),
+ (('a,b,c,d', lambda c: c == ',', 0),
+ [['a', ',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 1),
+ [['a', ','], ['b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 2),
+ [['a', ','], ['b', ','], ['c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c == ',', 10),
+ [['a', ','], ['b', ','], ['c', ','], ['d']]),
+ (('a,b,c,d', lambda c: c == '@', 2),
+ [['a', ',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda c: c != ',', 2),
+ [['a'], [',', 'b'], [',', 'c', ',', 'd']]),
+ ]:
+ actual = list(mi.split_after(*args))
+ self.assertEqual(actual, expected)
+
class SplitWhenTests(TestCase):
"""Tests for ``split_when()``"""
@@ -1214,6 +1328,28 @@
expected = [['x']]
self.assertEqual(actual, expected)
+ def test_max_split(self):
+ for args, expected in [
+ (('a,b,c,d', lambda a, _: a == ',', -1),
+ [['a', ','], ['b', ','], ['c', ','], ['d']]),
+ (('a,b,c,d', lambda a, _: a == ',', 0),
+ [['a', ',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda _, b: b == ',', 1),
+ [['a'], [',', 'b', ',', 'c', ',', 'd']]),
+ (('a,b,c,d', lambda a, _: a == ',', 2),
+ [['a', ','], ['b', ','], ['c', ',', 'd']]),
+ (('0124376', lambda a, b: a > b, -1),
+ [['0', '1', '2', '4'], ['3', '7'], ['6']]),
+ (('0124376', lambda a, b: a > b, 0),
+ [['0', '1', '2', '4', '3', '7', '6']]),
+ (('0124376', lambda a, b: a > b, 1),
+ [['0', '1', '2', '4'], ['3', '7', '6']]),
+ (('0124376', lambda a, b: a > b, 2),
+ [['0', '1', '2', '4'], ['3', '7'], ['6']]),
+ ]:
+ actual = list(mi.split_when(*args))
+ self.assertEqual(actual, expected, str(args))
+
class SplitIntoTests(TestCase):
"""Tests for ``split_into()``"""
@@ -1528,6 +1664,55 @@
self.assertEqual(list(all_groups), expected)
+class ZipEqualTest(TestCase):
+ """Tests for ``zip_equal()``"""
+
+ def test_equal(self):
+ lists = [0, 1, 2], [2, 3, 4]
+
+ for iterables in [lists, map(iter, lists)]:
+ actual = list(mi.zip_equal(*iterables))
+ expected = [(0, 2), (1, 3), (2, 4)]
+ self.assertEqual(actual, expected)
+
+ def test_unequal_lists(self):
+ two_items = [0, 1]
+ three_items = [2, 3, 4]
+ four_items = [5, 6, 7, 8]
+
+ # the mismatch is at index 1
+ try:
+ list(mi.zip_equal(two_items, three_items, four_items))
+ except mi.UnequalIterablesError as e:
+ self.assertEqual(
+ e.args[0],
+ (
+ 'Iterables have different lengths: '
+ 'index 0 has length 2; index 1 has length 3'
+ )
+ )
+
+ # the mismatch is at index 2
+ try:
+ list(mi.zip_equal(two_items, two_items, four_items, four_items))
+ except mi.UnequalIterablesError as e:
+ self.assertEqual(
+ e.args[0],
+ (
+ 'Iterables have different lengths: '
+ 'index 0 has length 2; index 2 has length 4'
+ )
+ )
+
+ # One without length: delegate to _zip_equal_generator
+ try:
+ list(mi.zip_equal(two_items, iter(two_items), three_items))
+ except mi.UnequalIterablesError as e:
+ self.assertEqual(
+ e.args[0], 'Iterables have different lengths'
+ )
+
+
class ZipOffsetTest(TestCase):
"""Tests for ``zip_offset()``"""