mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-20 05:13:21 -07:00
Bump cherrypy from 18.6.1 to 18.8.0 (#1796)
* Bump cherrypy from 18.6.1 to 18.8.0 Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.6.1 to 18.8.0. - [Release notes](https://github.com/cherrypy/cherrypy/releases) - [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst) - [Commits](https://github.com/cherrypy/cherrypy/compare/v18.6.1...v18.8.0) --- updated-dependencies: - dependency-name: cherrypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update cherrypy==18.8.0 Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
This commit is contained in:
parent
e79da07973
commit
76cc56a215
75 changed files with 19150 additions and 1339 deletions
|
@ -1,4 +1,6 @@
|
|||
"""More routines for operating on iterables, beyond itertools"""
|
||||
|
||||
from .more import * # noqa
|
||||
from .recipes import * # noqa
|
||||
|
||||
__version__ = '8.12.0'
|
||||
__version__ = '9.0.0'
|
||||
|
|
|
@ -2,9 +2,8 @@ import warnings
|
|||
|
||||
from collections import Counter, defaultdict, deque, abc
|
||||
from collections.abc import Sequence
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import partial, reduce, wraps
|
||||
from heapq import merge, heapify, heapreplace, heappop
|
||||
from heapq import heapify, heapreplace, heappop
|
||||
from itertools import (
|
||||
chain,
|
||||
compress,
|
||||
|
@ -27,12 +26,16 @@ from sys import hexversion, maxsize
|
|||
from time import monotonic
|
||||
|
||||
from .recipes import (
|
||||
_marker,
|
||||
_zip_equal,
|
||||
UnequalIterablesError,
|
||||
consume,
|
||||
flatten,
|
||||
pairwise,
|
||||
powerset,
|
||||
take,
|
||||
unique_everseen,
|
||||
all_equal,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
@ -49,9 +52,9 @@ __all__ = [
|
|||
'chunked_even',
|
||||
'circular_shifts',
|
||||
'collapse',
|
||||
'collate',
|
||||
'combination_index',
|
||||
'consecutive_groups',
|
||||
'constrained_batches',
|
||||
'consumer',
|
||||
'count_cycle',
|
||||
'countable',
|
||||
|
@ -67,6 +70,7 @@ __all__ = [
|
|||
'first',
|
||||
'groupby_transform',
|
||||
'ichunked',
|
||||
'iequals',
|
||||
'ilen',
|
||||
'interleave',
|
||||
'interleave_evenly',
|
||||
|
@ -77,6 +81,7 @@ __all__ = [
|
|||
'iterate',
|
||||
'last',
|
||||
'locate',
|
||||
'longest_common_prefix',
|
||||
'lstrip',
|
||||
'make_decorator',
|
||||
'map_except',
|
||||
|
@ -133,9 +138,6 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
_marker = object()
|
||||
|
||||
|
||||
def chunked(iterable, n, strict=False):
|
||||
"""Break *iterable* into lists of length *n*:
|
||||
|
||||
|
@ -410,44 +412,6 @@ class peekable:
|
|||
return self._cache[index]
|
||||
|
||||
|
||||
def collate(*iterables, **kwargs):
|
||||
"""Return a sorted merge of the items from each of several already-sorted
|
||||
*iterables*.
|
||||
|
||||
>>> list(collate('ACDZ', 'AZ', 'JKL'))
|
||||
['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z']
|
||||
|
||||
Works lazily, keeping only the next value from each iterable in memory. Use
|
||||
:func:`collate` to, for example, perform a n-way mergesort of items that
|
||||
don't fit in memory.
|
||||
|
||||
If a *key* function is specified, the iterables will be sorted according
|
||||
to its result:
|
||||
|
||||
>>> key = lambda s: int(s) # Sort by numeric value, not by string
|
||||
>>> list(collate(['1', '10'], ['2', '11'], key=key))
|
||||
['1', '2', '10', '11']
|
||||
|
||||
|
||||
If the *iterables* are sorted in descending order, set *reverse* to
|
||||
``True``:
|
||||
|
||||
>>> list(collate([5, 3, 1], [4, 2, 0], reverse=True))
|
||||
[5, 4, 3, 2, 1, 0]
|
||||
|
||||
If the elements of the passed-in iterables are out of order, you might get
|
||||
unexpected results.
|
||||
|
||||
On Python 3.5+, this function is an alias for :func:`heapq.merge`.
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
"collate is no longer part of more_itertools, use heapq.merge",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return merge(*iterables, **kwargs)
|
||||
|
||||
|
||||
def consumer(func):
|
||||
"""Decorator that automatically advances a PEP-342-style "reverse iterator"
|
||||
to its first yield point so you don't have to call ``next()`` on it
|
||||
|
@ -873,7 +837,9 @@ def windowed(seq, n, fillvalue=None, step=1):
|
|||
yield tuple(window)
|
||||
|
||||
size = len(window)
|
||||
if size < n:
|
||||
if size == 0:
|
||||
return
|
||||
elif size < n:
|
||||
yield tuple(chain(window, repeat(fillvalue, n - size)))
|
||||
elif 0 < i < min(step, n):
|
||||
window += (fillvalue,) * i
|
||||
|
@ -1646,45 +1612,6 @@ def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None):
|
|||
)
|
||||
|
||||
|
||||
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_generator(iterables):
|
||||
for combo in zip_longest(*iterables, fillvalue=_marker):
|
||||
for val in combo:
|
||||
if val is _marker:
|
||||
raise UnequalIterablesError()
|
||||
yield combo
|
||||
|
||||
|
||||
def _zip_equal(*iterables):
|
||||
# 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(*iterables):
|
||||
"""``zip`` the input *iterables* together, but raise
|
||||
``UnequalIterablesError`` if they aren't all the same length.
|
||||
|
@ -1826,7 +1753,7 @@ def unzip(iterable):
|
|||
of the zipped *iterable*.
|
||||
|
||||
The ``i``-th iterable contains the ``i``-th element from each element
|
||||
of the zipped iterable. The first element is used to to determine the
|
||||
of the zipped iterable. The first element is used to determine the
|
||||
length of the remaining elements.
|
||||
|
||||
>>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
|
||||
|
@ -2376,6 +2303,16 @@ def locate(iterable, pred=bool, window_size=None):
|
|||
return compress(count(), starmap(pred, it))
|
||||
|
||||
|
||||
def longest_common_prefix(iterables):
|
||||
"""Yield elements of the longest common prefix amongst given *iterables*.
|
||||
|
||||
>>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf']))
|
||||
'ab'
|
||||
|
||||
"""
|
||||
return (c[0] for c in takewhile(all_equal, zip(*iterables)))
|
||||
|
||||
|
||||
def lstrip(iterable, pred):
|
||||
"""Yield the items from *iterable*, but strip any from the beginning
|
||||
for which *pred* returns ``True``.
|
||||
|
@ -2684,7 +2621,7 @@ def difference(iterable, func=sub, *, initial=None):
|
|||
if initial is not None:
|
||||
first = []
|
||||
|
||||
return chain(first, starmap(func, zip(b, a)))
|
||||
return chain(first, map(func, b, a))
|
||||
|
||||
|
||||
class SequenceView(Sequence):
|
||||
|
@ -3327,6 +3264,27 @@ def only(iterable, default=None, too_long=None):
|
|||
return first_value
|
||||
|
||||
|
||||
class _IChunk:
|
||||
def __init__(self, iterable, n):
|
||||
self._it = islice(iterable, n)
|
||||
self._cache = deque()
|
||||
|
||||
def fill_cache(self):
|
||||
self._cache.extend(self._it)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
try:
|
||||
return next(self._it)
|
||||
except StopIteration:
|
||||
if self._cache:
|
||||
return self._cache.popleft()
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def ichunked(iterable, n):
|
||||
"""Break *iterable* into sub-iterables with *n* elements each.
|
||||
:func:`ichunked` is like :func:`chunked`, but it yields iterables
|
||||
|
@ -3348,20 +3306,39 @@ def ichunked(iterable, n):
|
|||
[8, 9, 10, 11]
|
||||
|
||||
"""
|
||||
source = iter(iterable)
|
||||
|
||||
source = peekable(iter(iterable))
|
||||
ichunk_marker = object()
|
||||
while True:
|
||||
# Check to see whether we're at the end of the source iterable
|
||||
item = next(source, _marker)
|
||||
if item is _marker:
|
||||
item = source.peek(ichunk_marker)
|
||||
if item is ichunk_marker:
|
||||
return
|
||||
|
||||
# Clone the source and yield an n-length slice
|
||||
source, it = tee(chain([item], source))
|
||||
yield islice(it, n)
|
||||
chunk = _IChunk(source, n)
|
||||
yield chunk
|
||||
|
||||
# Advance the source iterable
|
||||
consume(source, n)
|
||||
# Advance the source iterable and fill previous chunk's cache
|
||||
chunk.fill_cache()
|
||||
|
||||
|
||||
def iequals(*iterables):
|
||||
"""Return ``True`` if all given *iterables* are equal to each other,
|
||||
which means that they contain the same elements in the same order.
|
||||
|
||||
The function is useful for comparing iterables of different data types
|
||||
or iterables that do not support equality checks.
|
||||
|
||||
>>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc"))
|
||||
True
|
||||
|
||||
>>> iequals("abc", "acb")
|
||||
False
|
||||
|
||||
Not to be confused with :func:`all_equals`, which checks whether all
|
||||
elements of iterable are equal to each other.
|
||||
|
||||
"""
|
||||
return all(map(all_equal, zip_longest(*iterables, fillvalue=object())))
|
||||
|
||||
|
||||
def distinct_combinations(iterable, r):
|
||||
|
@ -3656,7 +3633,10 @@ class callback_iter:
|
|||
self._aborted = False
|
||||
self._future = None
|
||||
self._wait_seconds = wait_seconds
|
||||
self._executor = ThreadPoolExecutor(max_workers=1)
|
||||
# Lazily import concurrent.future
|
||||
self._executor = __import__(
|
||||
'concurrent.futures'
|
||||
).futures.ThreadPoolExecutor(max_workers=1)
|
||||
self._iterator = self._reader()
|
||||
|
||||
def __enter__(self):
|
||||
|
@ -3961,7 +3941,7 @@ def combination_index(element, iterable):
|
|||
|
||||
n, _ = last(pool, default=(n, None))
|
||||
|
||||
# Python versiosn below 3.8 don't have math.comb
|
||||
# Python versions below 3.8 don't have math.comb
|
||||
index = 1
|
||||
for i, j in enumerate(reversed(indexes), start=1):
|
||||
j = n - j
|
||||
|
@ -4114,7 +4094,7 @@ def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False):
|
|||
|
||||
If the *strict* keyword argument is ``True``, then
|
||||
``UnequalIterablesError`` will be raised if any of the iterables have
|
||||
different lengthss.
|
||||
different lengths.
|
||||
"""
|
||||
|
||||
def is_scalar(obj):
|
||||
|
@ -4315,3 +4295,53 @@ def minmax(iterable_or_value, *others, key=None, default=_marker):
|
|||
hi, hi_key = y, y_key
|
||||
|
||||
return lo, hi
|
||||
|
||||
|
||||
def constrained_batches(
|
||||
iterable, max_size, max_count=None, get_len=len, strict=True
|
||||
):
|
||||
"""Yield batches of items from *iterable* with a combined size limited by
|
||||
*max_size*.
|
||||
|
||||
>>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
|
||||
>>> list(constrained_batches(iterable, 10))
|
||||
[(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')]
|
||||
|
||||
If a *max_count* is supplied, the number of items per batch is also
|
||||
limited:
|
||||
|
||||
>>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
|
||||
>>> list(constrained_batches(iterable, 10, max_count = 2))
|
||||
[(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)]
|
||||
|
||||
If a *get_len* function is supplied, use that instead of :func:`len` to
|
||||
determine item size.
|
||||
|
||||
If *strict* is ``True``, raise ``ValueError`` if any single item is bigger
|
||||
than *max_size*. Otherwise, allow single items to exceed *max_size*.
|
||||
"""
|
||||
if max_size <= 0:
|
||||
raise ValueError('maximum size must be greater than zero')
|
||||
|
||||
batch = []
|
||||
batch_size = 0
|
||||
batch_count = 0
|
||||
for item in iterable:
|
||||
item_len = get_len(item)
|
||||
if strict and item_len > max_size:
|
||||
raise ValueError('item size exceeds maximum size')
|
||||
|
||||
reached_count = batch_count == max_count
|
||||
reached_size = item_len + batch_size > max_size
|
||||
if batch_count and (reached_size or reached_count):
|
||||
yield tuple(batch)
|
||||
batch.clear()
|
||||
batch_size = 0
|
||||
batch_count = 0
|
||||
|
||||
batch.append(item)
|
||||
batch_size += item_len
|
||||
batch_count += 1
|
||||
|
||||
if batch:
|
||||
yield tuple(batch)
|
||||
|
|
|
@ -72,7 +72,6 @@ class peekable(Generic[_T], Iterator[_T]):
|
|||
@overload
|
||||
def __getitem__(self, index: slice) -> List[_T]: ...
|
||||
|
||||
def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ...
|
||||
def consumer(func: _GenFn) -> _GenFn: ...
|
||||
def ilen(iterable: Iterable[object]) -> int: ...
|
||||
def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ...
|
||||
|
@ -179,7 +178,7 @@ def padded(
|
|||
iterable: Iterable[_T],
|
||||
*,
|
||||
n: Optional[int] = ...,
|
||||
next_multiple: bool = ...
|
||||
next_multiple: bool = ...,
|
||||
) -> Iterator[Optional[_T]]: ...
|
||||
@overload
|
||||
def padded(
|
||||
|
@ -225,7 +224,7 @@ def zip_equal(
|
|||
__iter1: Iterable[_T],
|
||||
__iter2: Iterable[_T],
|
||||
__iter3: Iterable[_T],
|
||||
*iterables: Iterable[_T]
|
||||
*iterables: Iterable[_T],
|
||||
) -> Iterator[Tuple[_T, ...]]: ...
|
||||
@overload
|
||||
def zip_offset(
|
||||
|
@ -233,7 +232,7 @@ def zip_offset(
|
|||
*,
|
||||
offsets: _SizedIterable[int],
|
||||
longest: bool = ...,
|
||||
fillvalue: None = None
|
||||
fillvalue: None = None,
|
||||
) -> Iterator[Tuple[Optional[_T1]]]: ...
|
||||
@overload
|
||||
def zip_offset(
|
||||
|
@ -242,7 +241,7 @@ def zip_offset(
|
|||
*,
|
||||
offsets: _SizedIterable[int],
|
||||
longest: bool = ...,
|
||||
fillvalue: None = None
|
||||
fillvalue: None = None,
|
||||
) -> Iterator[Tuple[Optional[_T1], Optional[_T2]]]: ...
|
||||
@overload
|
||||
def zip_offset(
|
||||
|
@ -252,7 +251,7 @@ def zip_offset(
|
|||
*iterables: Iterable[_T],
|
||||
offsets: _SizedIterable[int],
|
||||
longest: bool = ...,
|
||||
fillvalue: None = None
|
||||
fillvalue: None = None,
|
||||
) -> Iterator[Tuple[Optional[_T], ...]]: ...
|
||||
@overload
|
||||
def zip_offset(
|
||||
|
@ -420,7 +419,7 @@ def difference(
|
|||
iterable: Iterable[_T],
|
||||
func: Callable[[_T, _T], _U] = ...,
|
||||
*,
|
||||
initial: None = ...
|
||||
initial: None = ...,
|
||||
) -> Iterator[Union[_T, _U]]: ...
|
||||
@overload
|
||||
def difference(
|
||||
|
@ -529,12 +528,12 @@ def distinct_combinations(
|
|||
def filter_except(
|
||||
validator: Callable[[Any], object],
|
||||
iterable: Iterable[_T],
|
||||
*exceptions: Type[BaseException]
|
||||
*exceptions: Type[BaseException],
|
||||
) -> Iterator[_T]: ...
|
||||
def map_except(
|
||||
function: Callable[[Any], _U],
|
||||
iterable: Iterable[_T],
|
||||
*exceptions: Type[BaseException]
|
||||
*exceptions: Type[BaseException],
|
||||
) -> Iterator[_U]: ...
|
||||
def map_if(
|
||||
iterable: Iterable[Any],
|
||||
|
@ -610,7 +609,7 @@ def zip_broadcast(
|
|||
scalar_types: Union[
|
||||
type, Tuple[Union[type, Tuple[Any, ...]], ...], None
|
||||
] = ...,
|
||||
strict: bool = ...
|
||||
strict: bool = ...,
|
||||
) -> Iterable[Tuple[_T, ...]]: ...
|
||||
def unique_in_window(
|
||||
iterable: Iterable[_T], n: int, key: Optional[Callable[[_T], _U]] = ...
|
||||
|
@ -640,7 +639,7 @@ def minmax(
|
|||
iterable_or_value: Iterable[_SupportsLessThanT],
|
||||
*,
|
||||
key: None = None,
|
||||
default: _U
|
||||
default: _U,
|
||||
) -> Union[_U, Tuple[_SupportsLessThanT, _SupportsLessThanT]]: ...
|
||||
@overload
|
||||
def minmax(
|
||||
|
@ -653,12 +652,23 @@ def minmax(
|
|||
def minmax(
|
||||
iterable_or_value: _SupportsLessThanT,
|
||||
__other: _SupportsLessThanT,
|
||||
*others: _SupportsLessThanT
|
||||
*others: _SupportsLessThanT,
|
||||
) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ...
|
||||
@overload
|
||||
def minmax(
|
||||
iterable_or_value: _T,
|
||||
__other: _T,
|
||||
*others: _T,
|
||||
key: Callable[[_T], _SupportsLessThan]
|
||||
key: Callable[[_T], _SupportsLessThan],
|
||||
) -> Tuple[_T, _T]: ...
|
||||
def longest_common_prefix(
|
||||
iterables: Iterable[Iterable[_T]],
|
||||
) -> Iterator[_T]: ...
|
||||
def iequals(*iterables: Iterable[object]) -> bool: ...
|
||||
def constrained_batches(
|
||||
iterable: Iterable[object],
|
||||
max_size: int,
|
||||
max_count: Optional[int] = ...,
|
||||
get_len: Callable[[_T], object] = ...,
|
||||
strict: bool = ...,
|
||||
) -> Iterator[Tuple[_T]]: ...
|
||||
|
|
|
@ -7,11 +7,16 @@ Some backward-compatible usability improvements have been made.
|
|||
.. [1] http://docs.python.org/library/itertools.html#recipes
|
||||
|
||||
"""
|
||||
import warnings
|
||||
import math
|
||||
import operator
|
||||
|
||||
from collections import deque
|
||||
from collections.abc import Sized
|
||||
from functools import reduce
|
||||
from itertools import (
|
||||
chain,
|
||||
combinations,
|
||||
compress,
|
||||
count,
|
||||
cycle,
|
||||
groupby,
|
||||
|
@ -21,11 +26,11 @@ from itertools import (
|
|||
tee,
|
||||
zip_longest,
|
||||
)
|
||||
import operator
|
||||
from random import randrange, sample, choice
|
||||
|
||||
__all__ = [
|
||||
'all_equal',
|
||||
'batched',
|
||||
'before_and_after',
|
||||
'consume',
|
||||
'convolve',
|
||||
|
@ -41,6 +46,7 @@ __all__ = [
|
|||
'pad_none',
|
||||
'pairwise',
|
||||
'partition',
|
||||
'polynomial_from_roots',
|
||||
'powerset',
|
||||
'prepend',
|
||||
'quantify',
|
||||
|
@ -50,7 +56,9 @@ __all__ = [
|
|||
'random_product',
|
||||
'repeatfunc',
|
||||
'roundrobin',
|
||||
'sieve',
|
||||
'sliding_window',
|
||||
'subslices',
|
||||
'tabulate',
|
||||
'tail',
|
||||
'take',
|
||||
|
@ -59,6 +67,8 @@ __all__ = [
|
|||
'unique_justseen',
|
||||
]
|
||||
|
||||
_marker = object()
|
||||
|
||||
|
||||
def take(n, iterable):
|
||||
"""Return first *n* items of the iterable as a list.
|
||||
|
@ -102,7 +112,14 @@ def tail(n, iterable):
|
|||
['E', 'F', 'G']
|
||||
|
||||
"""
|
||||
return iter(deque(iterable, maxlen=n))
|
||||
# If the given iterable has a length, then we can use islice to get its
|
||||
# final elements. Note that if the iterable is not actually Iterable,
|
||||
# either islice or deque will throw a TypeError. This is why we don't
|
||||
# check if it is Iterable.
|
||||
if isinstance(iterable, Sized):
|
||||
yield from islice(iterable, max(0, len(iterable) - n), None)
|
||||
else:
|
||||
yield from iter(deque(iterable, maxlen=n))
|
||||
|
||||
|
||||
def consume(iterator, n=None):
|
||||
|
@ -284,20 +301,83 @@ else:
|
|||
pairwise.__doc__ = _pairwise.__doc__
|
||||
|
||||
|
||||
def grouper(iterable, n, fillvalue=None):
|
||||
"""Collect data into fixed-length chunks or blocks.
|
||||
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
|
||||
)
|
||||
|
||||
>>> list(grouper('ABCDEFG', 3, 'x'))
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
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_equal(*iterables):
|
||||
# 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 grouper(iterable, n, incomplete='fill', fillvalue=None):
|
||||
"""Group elements from *iterable* into fixed-length groups of length *n*.
|
||||
|
||||
>>> list(grouper('ABCDEF', 3))
|
||||
[('A', 'B', 'C'), ('D', 'E', 'F')]
|
||||
|
||||
The keyword arguments *incomplete* and *fillvalue* control what happens for
|
||||
iterables whose length is not a multiple of *n*.
|
||||
|
||||
When *incomplete* is `'fill'`, the last group will contain instances of
|
||||
*fillvalue*.
|
||||
|
||||
>>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x'))
|
||||
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')]
|
||||
|
||||
When *incomplete* is `'ignore'`, the last group will not be emitted.
|
||||
|
||||
>>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x'))
|
||||
[('A', 'B', 'C'), ('D', 'E', 'F')]
|
||||
|
||||
When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised.
|
||||
|
||||
>>> it = grouper('ABCDEFG', 3, incomplete='strict')
|
||||
>>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UnequalIterablesError
|
||||
|
||||
"""
|
||||
if isinstance(iterable, int):
|
||||
warnings.warn(
|
||||
"grouper expects iterable as first parameter", DeprecationWarning
|
||||
)
|
||||
n, iterable = iterable, n
|
||||
args = [iter(iterable)] * n
|
||||
return zip_longest(fillvalue=fillvalue, *args)
|
||||
if incomplete == 'fill':
|
||||
return zip_longest(*args, fillvalue=fillvalue)
|
||||
if incomplete == 'strict':
|
||||
return _zip_equal(*args)
|
||||
if incomplete == 'ignore':
|
||||
return zip(*args)
|
||||
else:
|
||||
raise ValueError('Expected fill, strict, or ignore')
|
||||
|
||||
|
||||
def roundrobin(*iterables):
|
||||
|
@ -658,11 +738,12 @@ def before_and_after(predicate, it):
|
|||
transition.append(elem)
|
||||
return
|
||||
|
||||
def remainder_iterator():
|
||||
yield from transition
|
||||
yield from it
|
||||
# Note: this is different from itertools recipes to allow nesting
|
||||
# before_and_after remainders into before_and_after again. See tests
|
||||
# for an example.
|
||||
remainder_iterator = chain(transition, it)
|
||||
|
||||
return true_iterator(), remainder_iterator()
|
||||
return true_iterator(), remainder_iterator
|
||||
|
||||
|
||||
def triplewise(iterable):
|
||||
|
@ -696,3 +777,65 @@ def sliding_window(iterable, n):
|
|||
for x in it:
|
||||
window.append(x)
|
||||
yield tuple(window)
|
||||
|
||||
|
||||
def subslices(iterable):
|
||||
"""Return all contiguous non-empty subslices of *iterable*.
|
||||
|
||||
>>> list(subslices('ABC'))
|
||||
[['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']]
|
||||
|
||||
This is similar to :func:`substrings`, but emits items in a different
|
||||
order.
|
||||
"""
|
||||
seq = list(iterable)
|
||||
slices = starmap(slice, combinations(range(len(seq) + 1), 2))
|
||||
return map(operator.getitem, repeat(seq), slices)
|
||||
|
||||
|
||||
def polynomial_from_roots(roots):
|
||||
"""Compute a polynomial's coefficients from its roots.
|
||||
|
||||
>>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3)
|
||||
>>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60
|
||||
[1, -4, -17, 60]
|
||||
"""
|
||||
# Use math.prod for Python 3.8+,
|
||||
prod = getattr(math, 'prod', lambda x: reduce(operator.mul, x, 1))
|
||||
roots = list(map(operator.neg, roots))
|
||||
return [
|
||||
sum(map(prod, combinations(roots, k))) for k in range(len(roots) + 1)
|
||||
]
|
||||
|
||||
|
||||
def sieve(n):
|
||||
"""Yield the primes less than n.
|
||||
|
||||
>>> list(sieve(30))
|
||||
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
|
||||
"""
|
||||
isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x)))
|
||||
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)
|
||||
|
||||
|
||||
def batched(iterable, n):
|
||||
"""Batch data into lists of length *n*. The last batch may be shorter.
|
||||
|
||||
>>> list(batched('ABCDEFG', 3))
|
||||
[['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
|
||||
|
||||
This recipe is from the ``itertools`` docs. This library also provides
|
||||
:func:`chunked`, which has a different implementation.
|
||||
"""
|
||||
it = iter(iterable)
|
||||
while True:
|
||||
batch = list(islice(it, n))
|
||||
if not batch:
|
||||
break
|
||||
yield batch
|
||||
|
|
|
@ -6,6 +6,7 @@ from typing import (
|
|||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
|
@ -39,21 +40,11 @@ def repeatfunc(
|
|||
func: Callable[..., _U], times: Optional[int] = ..., *args: Any
|
||||
) -> Iterator[_U]: ...
|
||||
def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ...
|
||||
@overload
|
||||
def grouper(
|
||||
iterable: Iterable[_T], n: int
|
||||
) -> Iterator[Tuple[Optional[_T], ...]]: ...
|
||||
@overload
|
||||
def grouper(
|
||||
iterable: Iterable[_T], n: int, fillvalue: _U
|
||||
) -> Iterator[Tuple[Union[_T, _U], ...]]: ...
|
||||
@overload
|
||||
def grouper( # Deprecated interface
|
||||
iterable: int, n: Iterable[_T]
|
||||
) -> Iterator[Tuple[Optional[_T], ...]]: ...
|
||||
@overload
|
||||
def grouper( # Deprecated interface
|
||||
iterable: int, n: Iterable[_T], fillvalue: _U
|
||||
iterable: Iterable[_T],
|
||||
n: int,
|
||||
incomplete: str = ...,
|
||||
fillvalue: _U = ...,
|
||||
) -> Iterator[Tuple[Union[_T, _U], ...]]: ...
|
||||
def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ...
|
||||
def partition(
|
||||
|
@ -110,3 +101,10 @@ 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]: ...
|
||||
def sieve(n: int) -> Iterator[int]: ...
|
||||
def batched(
|
||||
iterable: Iterable[_T],
|
||||
n: int,
|
||||
) -> Iterator[List[_T]]: ...
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue