Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-aioitertools for openSUSE:Factory checked in at 2022-10-17 14:58:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-aioitertools (Old) and /work/SRC/openSUSE:Factory/.python-aioitertools.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-aioitertools" Mon Oct 17 14:58:41 2022 rev:5 rq:1012087 version:0.11.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-aioitertools/python-aioitertools.changes 2022-01-07 12:47:46.339903415 +0100 +++ /work/SRC/openSUSE:Factory/.python-aioitertools.new.2275/python-aioitertools.changes 2022-10-17 14:58:44.298212419 +0200 @@ -1,0 +2,28 @@ +Mon Oct 17 12:04:35 UTC 2022 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to 0.11.0: + - New: `before_and_after` from `more_itertools` (#111) + - Removed: `loop` parameter is now removed from asyncio functions (#136) + - Docs: fixed docstring for `builtins.max` (#107) + - Docs: can now be built without installing package (#135) + - Python: Added support for Python 3.11 (#133) + - Python: Deprecated support for Python 3.6, to be removed in 0.12.0 +- from version 0.10.0: + - New `as_generated()` function for consuming multiple iterables (#99) +- from version 0.10.0b1: + - New `as_generated()` function for consuming multiple iterables (#99) +- from version 0.9.0: + - Python 3.10 support (#84, #97) + - Fixed: `as_completed()` will now cancel pending tasks when the timeout + threshold has been reached. + - Fixed: `zip()` will now gather all tasks correctly when exceptions are + raised, resulting in no pending tasks being unawaited. + - DEPRECATED: `loop` parameters to asyncio functions are ignored, and will + be removed entirely in v0.11.0. This is for 3.10 compatibility, as well + as to follow common practice and guidance around asyncio. +- Drop patches for issues fixed upstream + - aioitertools-remove-loop.patch +- Switch build system to pyproject.toml +- Update BuildRequires from pyproject.toml + +------------------------------------------------------------------- Old: ---- aioitertools-0.8.0.tar.gz aioitertools-remove-loop.patch New: ---- aioitertools-0.11.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-aioitertools.spec ++++++ --- /var/tmp/diff_new_pack.iJoTsz/_old 2022-10-17 14:58:44.746213280 +0200 +++ /var/tmp/diff_new_pack.iJoTsz/_new 2022-10-17 14:58:44.750213288 +0200 @@ -19,22 +19,21 @@ %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 Name: python-aioitertools -Version: 0.8.0 +Version: 0.11.0 Release: 0 Summary: Itertools and builtins for AsyncIO and mixed iterables License: MIT Group: Development/Languages/Python URL: https://aioitertools.omnilib.dev Source: https://files.pythonhosted.org/packages/source/a/aioitertools/aioitertools-%{version}.tar.gz -# PATCH-FIX-UPSTREAM aioitertools-remove-loop.patch -- gh#omnilib/aioitertools#84 -Patch0: aioitertools-remove-loop.patch BuildRequires: %{python_module asyncio} -BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module typing_extensions if %python-base < 3.8} +BuildRequires: %{python_module flit-core > 2} +BuildRequires: %{python_module pip} +BuildRequires: %{python_module typing_extensions if %python-base < 3.10} BuildRequires: fdupes BuildRequires: python-rpm-macros -%if 0%{?python_version_nodots} < 38 -Requires: python-typing_extensions >= 3.7 +%if 0%{?python_version_nodots} < 310 +Requires: python-typing_extensions >= 4.0 %endif BuildArch: noarch %python_subpackages @@ -46,10 +45,10 @@ %autosetup -p1 -n aioitertools-%{version} %build -%python_build +%pyproject_wheel %install -%python_install +%pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitelib} %check ++++++ aioitertools-0.8.0.tar.gz -> aioitertools-0.11.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/.pyup.yml new/aioitertools-0.11.0/.pyup.yml --- old/aioitertools-0.8.0/.pyup.yml 2020-11-18 19:50:17.881251800 +0100 +++ new/aioitertools-0.11.0/.pyup.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -assignees: jreese -label_prs: update -schedule: "every two weeks on saturday" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/CHANGELOG.md new/aioitertools-0.11.0/CHANGELOG.md --- old/aioitertools-0.8.0/CHANGELOG.md 2021-08-05 04:24:00.792124300 +0200 +++ new/aioitertools-0.11.0/CHANGELOG.md 2022-09-18 03:54:51.032733000 +0200 @@ -1,6 +1,79 @@ aioitertools ============ +[![Generated by attribution][attribution-badge]][attribution-url] + + +v0.11.0 +------- + +Feature release + +- New: `before_and_after` from `more_itertools` (#111) +- Removed: `loop` parameter is now removed from asyncio functions (#136) +- Docs: fixed docstring for `builtins.max` (#107) +- Docs: can now be built without installing package (#135) +- Python: Added support for Python 3.11 (#133) +- Python: Deprecated support for Python 3.6, to be removed in 0.12.0 + +```text +$ git shortlog -s v0.10.0...v0.11.0 + 10 Amethyst Reese + 2 Daniel Miranda + 6 John Reese + 1 Minsung.Kim + 12 dependabot[bot] +``` + + +v0.10.0 +------- + +Feature release + +- New `as_generated()` function for consuming multiple iterables (#99) + +```text +$ git shortlog -s v0.10.0b1...v0.10.0 + 1 John Reese +``` + + +v0.10.0b1 +--------- + +Beta release + +- New `as_generated()` function for consuming multiple iterables (#99) + +```text +$ git shortlog -s v0.9.0...v0.10.0b1 + 3 John Reese +``` + + +v0.9.0 +------ + +Feature release + +- Python 3.10 support (#84, #97) +- Fixed: `as_completed()` will now cancel pending tasks when the timeout + threshold has been reached. +- Fixed: `zip()` will now gather all tasks correctly when exceptions are + raised, resulting in no pending tasks being unawaited. +- DEPRECATED: `loop` parameters to asyncio functions are ignored, and will + be removed entirely in v0.11.0. This is for 3.10 compatibility, as well + as to follow common practice and guidance around asyncio. + +```text +$ git shortlog -s v0.8.0...v0.9.0 + 17 John Reese + 20 dependabot[bot] + 3 pyup.io bot +``` + + v0.8.0 ------ @@ -13,7 +86,7 @@ - Removed dependency on typing_extensions for Python 3.8 and newer (#49) - Improved documentation and formatting -``` +```text $ git shortlog -s v0.7.1...v0.8.0 1 Bryan Forbes 2 Jason Fried @@ -34,7 +107,7 @@ * Fix groupby() not working with empty iterables (#39) * Tested on Python 3.9 -``` +```text $ git shortlog -s v0.7.0...v0.7.1 8 John Reese 1 Roger Aiudi @@ -49,7 +122,7 @@ - Add `min()` and `max()` to builtins -``` +```text $ git shortlog -s v0.6.1...v0.7.0 7 John Reese ``` @@ -63,7 +136,7 @@ - Corrected description field for PyPI - Switched from setuptools to flit for build/publish -``` +```text $ git shortlog -s v0.6.0...v0.6.1 1 Dima Tisnek 6 John Reese @@ -77,7 +150,7 @@ - First pieces of more_itertools (#18) -``` +```text $ git shortlog -s v0.5.1...v0.6.0 3 John Reese 1 Zsolt Dollenstein @@ -92,7 +165,7 @@ - Include changelog, code of conduct, and contributers guide in sdist - Include wheels when building release distributions -``` +```text $ git shortlog -s v0.5.0...v0.5.1 4 John Reese ``` @@ -113,7 +186,7 @@ - Switch to Github Actions for CI - Testing on Python 3.8 -``` +```text $ git shortlog -s v0.4.0...v0.5.0 1 Alexey Simuskov 23 John Reese @@ -129,7 +202,7 @@ - Provisional module for friendly versions of the asyncio library. Access this via `aioitertools.asyncio`. -``` +```text $ git shortlog -s v0.3.2...v0.4.0 4 John Reese ``` @@ -142,7 +215,7 @@ - chain.from_iterable now accepts async generators to provide iterables (#8) -``` +```text $ git shortlog -s v0.3.1...v0.3.2 1 A Connecticut Princess 2 John Reese @@ -156,7 +229,7 @@ - Fixes `islice` consuming extra items (#7) -``` +```text $ git shortlog -s v0.3.0...v0.3.1 2 John Reese 3 Vladimir Solomatin @@ -172,7 +245,7 @@ - Added PEP 561 compliance and py.typed (#1) - Support for functions that return awaitables (#5) -``` +```text $ git shortlog -s v0.2.0...v0.3.0 6 Bryan Forbes 12 John Reese @@ -186,7 +259,7 @@ - Support all of itertools -``` +```text $ git shortlog -s v0.1.0...v0.2.0 8 John Reese ``` @@ -200,8 +273,11 @@ - Shadow major builtins for iterables - Unit tests for all builtins -``` +```text $ git shortlog -s v0.1.0 2 John Reese ``` +[attribution-badge]: + https://img.shields.io/badge/generated%20by-attribution-informational +[attribution-url]: https://attribution.omnilib.dev diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/CODE_OF_CONDUCT.md new/aioitertools-0.11.0/CODE_OF_CONDUCT.md --- old/aioitertools-0.8.0/CODE_OF_CONDUCT.md 2020-11-18 19:50:17.881542200 +0100 +++ new/aioitertools-0.11.0/CODE_OF_CONDUCT.md 2022-08-24 07:02:12.885702400 +0200 @@ -34,7 +34,7 @@ ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at j...@noswap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cont...@omnilib.dev. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/LICENSE new/aioitertools-0.11.0/LICENSE --- old/aioitertools-0.8.0/LICENSE 2020-11-18 19:50:17.881711700 +0100 +++ new/aioitertools-0.11.0/LICENSE 2022-08-24 07:02:12.895832000 +0200 @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 John Reese +Copyright (c) 2022 Amethyst Reese Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/PKG-INFO new/aioitertools-0.11.0/PKG-INFO --- old/aioitertools-0.8.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/aioitertools-0.11.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 2.1 Name: aioitertools -Version: 0.8.0 +Version: 0.11.0 Summary: itertools and builtins for AsyncIO and mixed iterables Home-page: https://aioitertools.omnilib.dev -Author: John Reese -Author-email: j...@noswap.com +Author: Amethyst Reese +Author-email: a...@noswap.com Requires-Python: >=3.6 Description-Content-Type: text/markdown Classifier: Development Status :: 4 - Beta @@ -12,7 +12,7 @@ Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Software Development :: Libraries -Requires-Dist: typing_extensions>=3.7; python_version < '3.8' +Requires-Dist: typing_extensions>=4.0; python_version < '3.10' Project-URL: Documentation, https://aioitertools.omnilib.dev/en/latest/ Project-URL: Github, https://github.com/omnilib/aioitertools @@ -21,11 +21,10 @@ Implementation of itertools, builtins, and more for AsyncIO and mixed-type iterables. -[![build status](https://github.com/omnilib/aioitertools/workflows/Build/badge.svg)](https://github.com/omnilib/aioitertools/actions) -[![code coverage](https://img.shields.io/codecov/c/gh/omnilib/aioitertools)](https://codecov.io/gh/omnilib/aioitertools) +[![documentation](https://readthedocs.org/projects/aioitertools/badge/?version=latest)](https://aioitertools.omnilib.dev) [![version](https://img.shields.io/pypi/v/aioitertools.svg)](https://pypi.org/project/aioitertools) +[![changelog](https://img.shields.io/badge/change-log-blue)](https://aioitertools.omnilib.dev/en/latest/changelog.html) [![license](https://img.shields.io/pypi/l/aioitertools.svg)](https://github.com/omnilib/aioitertools/blob/master/LICENSE) -[![code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Install @@ -97,7 +96,7 @@ License ------- -aioitertools is copyright [John Reese](https://jreese.sh), and licensed under +aioitertools is copyright [Amethyst Reese](https://noswap.com), and licensed under the MIT license. I am providing code in this repository to you under an open source license. This is my personal repository; the license you receive to my code is from me and not from my employer. See the `LICENSE` file for details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/README.md new/aioitertools-0.11.0/README.md --- old/aioitertools-0.8.0/README.md 2020-11-18 19:50:17.881892200 +0100 +++ new/aioitertools-0.11.0/README.md 2022-08-24 07:02:12.898446600 +0200 @@ -3,11 +3,10 @@ Implementation of itertools, builtins, and more for AsyncIO and mixed-type iterables. -[![build status](https://github.com/omnilib/aioitertools/workflows/Build/badge.svg)](https://github.com/omnilib/aioitertools/actions) -[![code coverage](https://img.shields.io/codecov/c/gh/omnilib/aioitertools)](https://codecov.io/gh/omnilib/aioitertools) +[![documentation](https://readthedocs.org/projects/aioitertools/badge/?version=latest)](https://aioitertools.omnilib.dev) [![version](https://img.shields.io/pypi/v/aioitertools.svg)](https://pypi.org/project/aioitertools) +[![changelog](https://img.shields.io/badge/change-log-blue)](https://aioitertools.omnilib.dev/en/latest/changelog.html) [![license](https://img.shields.io/pypi/l/aioitertools.svg)](https://github.com/omnilib/aioitertools/blob/master/LICENSE) -[![code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Install @@ -79,7 +78,7 @@ License ------- -aioitertools is copyright [John Reese](https://jreese.sh), and licensed under +aioitertools is copyright [Amethyst Reese](https://noswap.com), and licensed under the MIT license. I am providing code in this repository to you under an open source license. This is my personal repository; the license you receive to my code is from me and not from my employer. See the `LICENSE` file for details. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/__init__.py new/aioitertools-0.11.0/aioitertools/__init__.py --- old/aioitertools-0.8.0/aioitertools/__init__.py 2021-08-05 04:22:56.171010700 +0200 +++ new/aioitertools-0.11.0/aioitertools/__init__.py 2022-09-17 05:03:55.802892700 +0200 @@ -1,11 +1,11 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license """ itertools and builtins for AsyncIO and mixed iterables """ -__author__ = "John Reese" +__author__ = "Amethyst Reese" from . import asyncio from .__version__ import __version__ from .builtins import ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/__version__.py new/aioitertools-0.11.0/aioitertools/__version__.py --- old/aioitertools-0.8.0/aioitertools/__version__.py 2021-08-05 04:24:00.800830100 +0200 +++ new/aioitertools-0.11.0/aioitertools/__version__.py 2022-09-18 03:54:51.037689700 +0200 @@ -1 +1,7 @@ -__version__ = "0.8.0" +""" +This file is automatically generated by attribution. + +Do not edit manually. Get more info at https://attribution.omnilib.dev +""" + +__version__ = "0.11.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/asyncio.py new/aioitertools-0.11.0/aioitertools/asyncio.py --- old/aioitertools-0.8.0/aioitertools/asyncio.py 2021-08-05 04:22:56.166504100 +0200 +++ new/aioitertools-0.11.0/aioitertools/asyncio.py 2022-09-18 03:25:02.098674500 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license """ @@ -9,17 +9,28 @@ import asyncio import time -from typing import Any, Awaitable, Dict, Iterable, List, Optional, Set, Tuple, cast +from typing import ( + Any, + AsyncGenerator, + AsyncIterable, + Awaitable, + cast, + Dict, + Iterable, + List, + Optional, + Set, + Tuple, +) -from .builtins import maybe_await, iter as aiter -from .types import AsyncIterator, MaybeAwaitable, AnyIterable, T +from .builtins import iter as aiter, maybe_await +from .types import AnyIterable, AsyncIterator, MaybeAwaitable, T async def as_completed( aws: Iterable[Awaitable[T]], *, - loop: Optional[asyncio.AbstractEventLoop] = None, - timeout: Optional[float] = None + timeout: Optional[float] = None, ) -> AsyncIterator[T]: """ Run awaitables in `aws` concurrently, and yield results as they complete. @@ -27,6 +38,9 @@ Unlike `asyncio.as_completed`, this yields actual results, and does not require awaiting each item in the iterable. + Cancels all remaining awaitables if a timeout is given and the timeout threshold + is reached. + Example:: async for value in as_completed(futures): @@ -34,7 +48,7 @@ """ done: Set[Awaitable[T]] = set() - pending: Set[Awaitable[T]] = set(aws) + pending: Set[Awaitable[T]] = {asyncio.ensure_future(a) for a in aws} remaining: Optional[float] = None if timeout and timeout > 0: @@ -46,6 +60,11 @@ if timeout: remaining = threshold - time.time() if remaining <= 0: + for fut in pending: + if isinstance(fut, asyncio.Future): + fut.cancel() + else: # pragma: no cover + pass raise asyncio.TimeoutError() # asyncio.Future inherits from typing.Awaitable @@ -59,7 +78,6 @@ Tuple[Set[Awaitable[T]], Set[Awaitable[T]]], await asyncio.wait( pending, - loop=loop, timeout=remaining, return_when=asyncio.FIRST_COMPLETED, ), @@ -69,11 +87,91 @@ yield await item +async def as_generated( + iterables: Iterable[AsyncIterable[T]], + *, + return_exceptions: bool = False, +) -> AsyncIterable[T]: + """ + Yield results from one or more async iterables, in the order they are produced. + + Like :func:`as_completed`, but for async iterators or generators instead of futures. + Creates a separate task to drain each iterable, and a single queue for results. + + If ``return_exceptions`` is ``False``, then any exception will be raised, and + pending iterables and tasks will be cancelled, and async generators will be closed. + If ``return_exceptions`` is ``True``, any exceptions will be yielded as results, + and execution will continue until all iterables have been fully consumed. + + Example:: + + async def generator(x): + for i in range(x): + yield i + + gen1 = generator(10) + gen2 = generator(12) + + async for value in as_generated([gen1, gen2]): + ... # intermixed values yielded from gen1 and gen2 + """ + + exc_queue: asyncio.Queue[Exception] = asyncio.Queue() + queue: asyncio.Queue[T] = asyncio.Queue() + + async def tailer(iter: AsyncIterable[T]) -> None: + try: + async for item in iter: + await queue.put(item) + except asyncio.CancelledError: + if isinstance(iter, AsyncGenerator): # pragma:nocover + await iter.aclose() + raise + except Exception as e: + await exc_queue.put(e) + + tasks = [asyncio.ensure_future(tailer(iter)) for iter in iterables] + pending = set(tasks) + + try: + while pending: + try: + exc = exc_queue.get_nowait() + if return_exceptions: + yield exc # type: ignore + else: + raise exc + except asyncio.QueueEmpty: + pass + + try: + value = queue.get_nowait() + yield value + except asyncio.QueueEmpty: + for task in list(pending): + if task.done(): + pending.remove(task) + await asyncio.sleep(0.001) + + except (asyncio.CancelledError, GeneratorExit): + pass + + finally: + for task in tasks: + if not task.done(): + task.cancel() + + for task in tasks: + try: + await task + except asyncio.CancelledError: + pass + + async def gather( *args: Awaitable[T], - loop: Optional[asyncio.AbstractEventLoop] = None, return_exceptions: bool = False, - limit: int = -1 + limit: int = -1, ) -> List[Any]: """ Like asyncio.gather but with a limit on concurrency. @@ -124,7 +222,7 @@ if pending: try: done, pending = await asyncio.wait( - pending, loop=loop, return_when=asyncio.FIRST_COMPLETED + pending, return_when=asyncio.FIRST_COMPLETED ) for x in done: if return_exceptions and x.exception(): @@ -136,7 +234,7 @@ for x in pending: x.cancel() # we insure that all tasks are cancelled before we raise - await asyncio.gather(*pending, loop=loop, return_exceptions=True) + await asyncio.gather(*pending, return_exceptions=True) raise if not pending and next_arg == len(args): @@ -151,7 +249,6 @@ async def gather_iter( itr: AnyIterable[MaybeAwaitable[T]], - loop: Optional[asyncio.AbstractEventLoop] = None, return_exceptions: bool = False, limit: int = -1, ) -> List[T]: @@ -162,7 +259,6 @@ """ return await gather( *[maybe_await(i) async for i in aiter(itr)], - loop=loop, return_exceptions=return_exceptions, limit=limit, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/builtins.py new/aioitertools-0.11.0/aioitertools/builtins.py --- old/aioitertools-0.8.0/aioitertools/builtins.py 2021-08-05 04:22:57.200909100 +0200 +++ new/aioitertools-0.11.0/aioitertools/builtins.py 2022-09-17 05:03:55.830464600 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license """ @@ -19,30 +19,30 @@ AsyncIterable, AsyncIterator, Callable, + cast, Iterable, List, Optional, + overload, Set, Tuple, Union, - cast, - overload, ) from . import asyncio as ait_asyncio -from .helpers import Orderable, maybe_await +from .helpers import maybe_await, Orderable from .types import ( - T1, - T2, - T3, - T4, - T5, AnyIterable, AnyIterator, AnyStop, + MaybeAwaitable, R, T, - MaybeAwaitable, + T1, + T2, + T3, + T4, + T5, ) @@ -113,12 +113,12 @@ @overload -async def next(itr: AnyIterator[T]) -> T: +async def next(itr: AnyIterator[T]) -> T: # pragma: no cover ... @overload -async def next(itr: AnyIterator[T1], default: T2) -> Union[T1, T2]: +async def next(itr: AnyIterator[T1], default: T2) -> Union[T1, T2]: # pragma: no cover ... @@ -229,7 +229,7 @@ Example:: - await min(range(5)) + await max(range(5)) -> 4 """ @@ -415,8 +415,9 @@ its: List[AsyncIterator[Any]] = [iter(itr) for itr in itrs] while True: - try: - values = await asyncio.gather(*[it.__anext__() for it in its]) - yield values - except AnyStop: + values = await asyncio.gather( + *[it.__anext__() for it in its], return_exceptions=True + ) + if builtins.any(isinstance(v, AnyStop) for v in values): break + yield values diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/helpers.py new/aioitertools-0.11.0/aioitertools/helpers.py --- old/aioitertools-0.8.0/aioitertools/helpers.py 2021-08-05 04:22:56.191383400 +0200 +++ new/aioitertools-0.11.0/aioitertools/helpers.py 2022-09-18 03:26:23.196569700 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import inspect @@ -7,9 +7,9 @@ from .types import T -if sys.version_info < (3, 8): +if sys.version_info < (3, 8): # pragma: no cover from typing_extensions import Protocol -else: +else: # pragma: no cover from typing import Protocol diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/itertools.py new/aioitertools-0.11.0/aioitertools/itertools.py --- old/aioitertools-0.8.0/aioitertools/itertools.py 2021-08-05 04:22:56.255954300 +0200 +++ new/aioitertools-0.11.0/aioitertools/itertools.py 2022-09-17 05:03:55.893525800 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license """ @@ -17,7 +17,7 @@ import builtins import itertools import operator -from typing import Any, AsyncIterator, List, Optional, Tuple, overload +from typing import Any, AsyncIterator, List, Optional, overload, Tuple from .builtins import enumerate, iter, list, next, zip from .helpers import maybe_await diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/more_itertools.py new/aioitertools-0.11.0/aioitertools/more_itertools.py --- old/aioitertools-0.8.0/aioitertools/more_itertools.py 2021-08-05 04:22:56.186720100 +0200 +++ new/aioitertools-0.11.0/aioitertools/more_itertools.py 2022-09-17 05:03:55.816682800 +0200 @@ -1,11 +1,15 @@ -# Copyright 2020 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license -from typing import AsyncIterable, List, TypeVar +import asyncio +from typing import AsyncIterable, List, Tuple, TypeVar + +from aioitertools.helpers import maybe_await from .builtins import iter from .itertools import islice -from .types import AnyIterable +from .types import AnyIterable, Predicate + T = TypeVar("T") @@ -44,3 +48,47 @@ while chunk != []: yield chunk chunk = await take(n, it) + + +async def before_and_after( + predicate: Predicate[T], iterable: AnyIterable[T] +) -> Tuple[AsyncIterable[T], AsyncIterable[T]]: + """ + A variant of :func:`aioitertools.takewhile` that allows complete access to the + remainder of the iterator. + + >>> it = iter('ABCdEfGhI') + >>> all_upper, remainder = await before_and_after(str.isupper, it) + >>> ''.join([char async for char in all_upper]) + 'ABC' + >>> ''.join([char async for char in remainder]) + 'dEfGhI' + + Note that the first iterator must be fully consumed before the second + iterator can generate valid results. + """ + + it = iter(iterable) + + transition = asyncio.get_event_loop().create_future() + + async def true_iterator(): + async for elem in it: + if await maybe_await(predicate(elem)): + yield elem + else: + transition.set_result(elem) + return + + transition.set_exception(StopAsyncIteration) + + async def remainder_iterator(): + try: + yield (await transition) + except StopAsyncIteration: + return + + async for elm in it: + yield elm + + return true_iterator(), remainder_iterator() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/__init__.py new/aioitertools-0.11.0/aioitertools/tests/__init__.py --- old/aioitertools-0.8.0/aioitertools/tests/__init__.py 2021-08-05 04:22:56.368359600 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/__init__.py 2022-09-17 05:03:55.753780600 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license from .asyncio import AsyncioTest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/__main__.py new/aioitertools-0.11.0/aioitertools/tests/__main__.py --- old/aioitertools-0.8.0/aioitertools/tests/__main__.py 2021-08-05 04:22:56.393205000 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/__main__.py 2022-09-17 05:03:55.813780500 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import unittest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/asyncio.py new/aioitertools-0.11.0/aioitertools/tests/asyncio.py --- old/aioitertools-0.8.0/aioitertools/tests/asyncio.py 2021-08-05 04:22:56.366293200 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/asyncio.py 2022-09-17 05:03:55.838480200 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import asyncio @@ -51,6 +51,106 @@ self.assertEqual(results, 1) @async_test + async def test_as_generated(self): + async def gen(): + for i in range(10): + yield i + await asyncio.sleep(0) + + gens = [gen(), gen(), gen()] + expected = list(range(10)) * 3 + results = [] + async for value in aio.as_generated(gens): + results.append(value) + self.assertEqual(30, len(results)) + self.assertListEqual(sorted(expected), sorted(results)) + + @async_test + async def test_as_generated_exception(self): + async def gen1(): + for i in range(3): + yield i + await asyncio.sleep(0) + raise Exception("fake") + + async def gen2(): + for i in range(10): + yield i + await asyncio.sleep(0) + + gens = [gen1(), gen2()] + results = [] + with self.assertRaisesRegex(Exception, "fake"): + async for value in aio.as_generated(gens): + results.append(value) + self.assertNotIn(10, results) + + @async_test + async def test_as_generated_return_exception(self): + async def gen1(): + for i in range(3): + yield i + await asyncio.sleep(0) + raise Exception("fake") + + async def gen2(): + for i in range(10): + yield i + await asyncio.sleep(0) + + gens = [gen1(), gen2()] + expected = list(range(3)) + list(range(10)) + errors = [] + results = [] + async for value in aio.as_generated(gens, return_exceptions=True): + if isinstance(value, Exception): + errors.append(value) + else: + results.append(value) + self.assertListEqual(sorted(expected), sorted(results)) + self.assertEqual(1, len(errors)) + self.assertIsInstance(errors[0], Exception) + + @async_test + async def test_as_generated_task_cancelled(self): + async def gen(max: int = 10): + for i in range(5): + if i > max: + raise asyncio.CancelledError + yield i + await asyncio.sleep(0) + + gens = [gen(2), gen()] + expected = list(range(3)) + list(range(5)) + results = [] + async for value in aio.as_generated(gens): + results.append(value) + self.assertListEqual(sorted(expected), sorted(results)) + + @async_test + async def test_as_generated_cancelled(self): + async def gen(): + for i in range(5): + yield i + await asyncio.sleep(0.1) + + expected = [0, 0, 1, 1] + results = [] + + async def foo(): + gens = [gen(), gen()] + async for value in aio.as_generated(gens): + results.append(value) + return results + + task = asyncio.ensure_future(foo()) + await asyncio.sleep(0.15) + task.cancel() + await task + + self.assertListEqual(sorted(expected), sorted(results)) + + @async_test async def test_gather_input_types(self): async def fn(arg): await asyncio.sleep(0.001) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/builtins.py new/aioitertools-0.11.0/aioitertools/tests/builtins.py --- old/aioitertools-0.8.0/aioitertools/tests/builtins.py 2021-08-05 04:22:56.331863200 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/builtins.py 2022-09-17 05:03:55.871657400 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import asyncio @@ -357,3 +357,12 @@ self.assertEqual(a, slist[idx]) self.assertEqual(b, srange[idx]) idx += 1 + + @async_test + async def test_zip_shortest(self): + short = ["a", "b", "c"] + long = [0, 1, 2, 3, 5] + + result = await ait.list(ait.zip(short, long)) + expected = [["a", 0], ["b", 1], ["c", 2]] + self.assertListEqual(expected, result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/helpers.py new/aioitertools-0.11.0/aioitertools/tests/helpers.py --- old/aioitertools-0.8.0/aioitertools/tests/helpers.py 2021-08-05 04:22:56.391575600 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/helpers.py 2022-09-18 03:25:54.553944600 +0200 @@ -1,17 +1,23 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import asyncio import functools -from unittest import TestCase +import sys +from unittest import skipIf, TestCase from aioitertools.helpers import maybe_await def async_test(fn): def wrapped(*args, **kwargs): - loop = asyncio.get_event_loop() - return loop.run_until_complete(fn(*args, **kwargs)) + try: + loop = asyncio.new_event_loop() + loop.set_debug(False) + result = loop.run_until_complete(fn(*args, **kwargs)) + return result + finally: + loop.close() return wrapped @@ -32,6 +38,7 @@ self.assertEqual(await maybe_await(forty_two()), 42) + @skipIf(sys.version_info >= (3, 11), "@asyncio.coroutine removed") @async_test async def test_maybe_await_coroutine(self): @asyncio.coroutine diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/itertools.py new/aioitertools-0.11.0/aioitertools/tests/itertools.py --- old/aioitertools-0.8.0/aioitertools/tests/itertools.py 2021-08-05 04:22:56.567801200 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/itertools.py 2022-09-17 05:03:55.971668500 +0200 @@ -1,4 +1,4 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license import asyncio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/tests/more_itertools.py new/aioitertools-0.11.0/aioitertools/tests/more_itertools.py --- old/aioitertools-0.8.0/aioitertools/tests/more_itertools.py 2021-08-05 04:22:56.383290300 +0200 +++ new/aioitertools-0.11.0/aioitertools/tests/more_itertools.py 2022-09-17 05:03:55.831813300 +0200 @@ -1,4 +1,4 @@ -# Copyright 2020 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license from typing import AsyncIterable @@ -56,3 +56,41 @@ @async_test async def test_chunked_empty(self) -> None: self.assertEqual([], [chunk async for chunk in mit.chunked(_empty(), 2)]) + + @async_test + async def test_before_and_after_split(self) -> None: + it = _gen() + before, after = await mit.before_and_after(lambda i: i <= 2, it) + self.assertEqual([elm async for elm in before], [0, 1, 2]) + self.assertEqual([elm async for elm in after], [3, 4]) + + @async_test + async def test_before_and_after_before_only(self) -> None: + it = _gen() + before, after = await mit.before_and_after(lambda i: True, it) + self.assertEqual([elm async for elm in before], [0, 1, 2, 3, 4]) + self.assertEqual([elm async for elm in after], []) + + @async_test + async def test_before_and_after_after_only(self) -> None: + it = _gen() + before, after = await mit.before_and_after(lambda i: False, it) + self.assertEqual([elm async for elm in before], []) + self.assertEqual([elm async for elm in after], [0, 1, 2, 3, 4]) + + @async_test + async def test_before_and_after_async_predicate(self) -> None: + async def predicate(elm: int) -> bool: + return elm <= 2 + + it = _gen() + before, after = await mit.before_and_after(predicate, it) + self.assertEqual([elm async for elm in before], [0, 1, 2]) + self.assertEqual([elm async for elm in after], [3, 4]) + + @async_test + async def test_before_and_after_empty(self) -> None: + it = _empty() + before, after = await mit.before_and_after(lambda i: True, it) + self.assertEqual([elm async for elm in before], []) + self.assertEqual([elm async for elm in after], []) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/aioitertools/types.py new/aioitertools-0.11.0/aioitertools/types.py --- old/aioitertools-0.8.0/aioitertools/types.py 2021-08-05 04:22:56.181237700 +0200 +++ new/aioitertools-0.11.0/aioitertools/types.py 2022-09-17 05:03:55.823509200 +0200 @@ -1,6 +1,8 @@ -# Copyright 2018 John Reese +# Copyright 2022 Amethyst Reese # Licensed under the MIT license +import sys + from typing import ( AsyncIterable, AsyncIterator, @@ -12,6 +14,13 @@ Union, ) +if sys.version_info < (3, 10): # pragma: no cover + from typing_extensions import ParamSpec +else: # pragma: no cover + from typing import ParamSpec + + +P = ParamSpec("P") R = TypeVar("R") T = TypeVar("T") T1 = TypeVar("T1") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/docs/conf.py new/aioitertools-0.11.0/docs/conf.py --- old/aioitertools-0.8.0/docs/conf.py 2021-08-05 03:54:56.994592200 +0200 +++ new/aioitertools-0.11.0/docs/conf.py 2022-09-17 07:14:13.915692600 +0200 @@ -4,24 +4,18 @@ # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - # -- Project information ----------------------------------------------------- import datetime +import pathlib +import sys + +root = pathlib.Path(__file__).parent.parent +sys.path.insert(0, root.as_posix()) project = "aioitertools" -copyright = f"{datetime.date.today().year}, John Reese" -author = "John Reese" +copyright = f"{datetime.date.today().year}, Amethyst Reese" +author = "Amethyst Reese" # -- General configuration --------------------------------------------------- @@ -30,9 +24,9 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - "m2r", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", + "sphinx_mdinclude", ] autodoc_default_options = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/makefile new/aioitertools-0.11.0/makefile --- old/aioitertools-0.8.0/makefile 2021-05-16 23:04:10.655338300 +0200 +++ new/aioitertools-0.11.0/makefile 2022-08-24 06:25:50.816132500 +0200 @@ -35,7 +35,7 @@ source .venv/bin/activate && sphinx-build -b html docs html clean: - rm -rf build dist html README MANIFEST *.egg-info + rm -rf .mypy_cache build dist html README MANIFEST *.egg-info distclean: clean - rm -rf .venv \ No newline at end of file + rm -rf .venv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/pyproject.toml new/aioitertools-0.11.0/pyproject.toml --- old/aioitertools-0.8.0/pyproject.toml 2021-03-07 00:15:44.518752300 +0100 +++ new/aioitertools-0.11.0/pyproject.toml 2022-09-17 23:22:09.122407700 +0200 @@ -4,11 +4,11 @@ [tool.flit.metadata] module = "aioitertools" -author = "John Reese" -author-email = "j...@noswap.com" +author = "Amethyst Reese" +author-email = "a...@noswap.com" description-file = "README.md" home-page = "https://aioitertools.omnilib.dev" -requires = ["typing_extensions>=3.7; python_version < '3.8'"] +requires = ["typing_extensions>=4.0; python_version < '3.10'"] requires-python = ">=3.6" classifiers = [ "Development Status :: 4 - Beta", @@ -31,6 +31,7 @@ name = "aioitertools" package = "aioitertools" version_file = true +ignored_authors = ["dependabot[bot]"] [tool.coverage.run] branch = true @@ -38,7 +39,7 @@ omit = ["aioitertools/tests/*"] [tool.coverage.report] -fail_under = 70 +fail_under = 97 precision = 1 show_missing = true skip_covered = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/requirements-dev.txt new/aioitertools-0.11.0/requirements-dev.txt --- old/aioitertools-0.8.0/requirements-dev.txt 2021-08-05 03:56:49.667648300 +0200 +++ new/aioitertools-0.11.0/requirements-dev.txt 2022-09-17 06:40:16.958790500 +0200 @@ -1,10 +1,10 @@ attribution==1.5.2 -black==21.7b0 -codecov==2.1.11 -coverage[toml]==5.5 -flake8==3.9.2 -flit==3.2.0 -mypy==0.910 -sphinx==4.1.1 -usort==0.6.2 -git+https://github.com/miyakogi/m2r@66f4a5a500cdd9fc59085106bff082c9cadafaf3 +black==22.8.0 +coverage[toml]==6.2; python_version < "3.10" +coverage==6.4.4; python_version >= "3.10" +flake8==5.0.4 +flit==3.7.1 +mypy==0.971 +sphinx==4.3.2 +usort==1.0.5 +sphinx-mdinclude==0.5.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/requirements.txt new/aioitertools-0.11.0/requirements.txt --- old/aioitertools-0.8.0/requirements.txt 2021-03-07 00:15:44.519299500 +0100 +++ new/aioitertools-0.11.0/requirements.txt 2022-08-24 06:25:50.816625800 +0200 @@ -1 +1 @@ -typing_extensions>=3.7;python_version<"3.8" +typing_extensions>=4.0;python_version<"3.10" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aioitertools-0.8.0/setup.py new/aioitertools-0.11.0/setup.py --- old/aioitertools-0.8.0/setup.py 1970-01-01 01:00:00.000000000 +0100 +++ new/aioitertools-0.11.0/setup.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# setup.py generated by flit for tools that don't yet use PEP 517 - -from distutils.core import setup - -packages = \ -['aioitertools', 'aioitertools.tests'] - -package_data = \ -{'': ['*']} - -extras_require = \ -{":python_version < '3.8'": ['typing_extensions>=3.7']} - -setup(name='aioitertools', - version='0.8.0', - description='itertools and builtins for AsyncIO and mixed iterables', - author='John Reese', - author_email='j...@noswap.com', - url='https://aioitertools.omnilib.dev', - packages=packages, - package_data=package_data, - extras_require=extras_require, - python_requires='>=3.6', - )