Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-duet for openSUSE:Factory checked in at 2023-04-24 22:31:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-duet (Old) and /work/SRC/openSUSE:Factory/.python-duet.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-duet" Mon Apr 24 22:31:12 2023 rev:4 rq:1082327 version:0.2.8 Changes: -------- --- /work/SRC/openSUSE:Factory/python-duet/python-duet.changes 2022-10-14 15:42:20.535898659 +0200 +++ /work/SRC/openSUSE:Factory/.python-duet.new.1533/python-duet.changes 2023-04-24 22:31:24.183578335 +0200 @@ -1,0 +2,10 @@ +Sun Apr 23 20:44:55 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 0.2.8: + * Test duet against python 3.11 + * Loosen version requirement on typing_extensions + * Make Limiter work with cancellation + * Add support for overloaded sync callbacks + * Fix timeout clipping in Scheduler.tick + +------------------------------------------------------------------- Old: ---- duet-0.2.7.tar.gz New: ---- duet-0.2.8.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-duet.spec ++++++ --- /var/tmp/diff_new_pack.0pxgCb/_old 2023-04-24 22:31:24.759581759 +0200 +++ /var/tmp/diff_new_pack.0pxgCb/_new 2023-04-24 22:31:24.763581783 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-duet # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 Name: python-duet -Version: 0.2.7 +Version: 0.2.8 Release: 0 Summary: A simple future-based async library for python License: Apache-2.0 ++++++ duet-0.2.7.tar.gz -> duet-0.2.8.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/.github/workflows/ci.yml new/duet-0.2.8/.github/workflows/ci.yml --- old/duet-0.2.7/.github/workflows/ci.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/.github/workflows/ci.yml 2023-03-03 00:41:07.000000000 +0100 @@ -0,0 +1,112 @@ +name: Continuous Integration + +on: [pull_request] + +jobs: + format: + name: Format check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-python@v3 + with: + python-version: '3.7' + architecture: 'x64' + - name: Install black + run: pip install -r dev/requirements.txt + - name: Format + run: isort duet --check && black duet --check + mypy: + name: Type check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.7' + architecture: 'x64' + - name: Install mypy + run: pip install -r dev/requirements.txt + - name: Type check + run: mypy duet + env: + PYTHONPATH: '.' + lint: + name: Lint check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.7' + architecture: 'x64' + - name: Install pylint + run: pip install -r dev/requirements.txt + - name: Lint + run: pylint duet + import: + name: Import check + strategy: + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Install requirements + run: pip install -r requirements.txt + - name: Import duet + run: python -c "import duet" + test-linux: + name: Pytest Linux + strategy: + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Install requirements + run: pip install -r requirements.txt -r dev/requirements.txt + - name: Pytest check + run: pytest duet + test-windows: + name: Pytest Windows + strategy: + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Install requirements + run: pip install -r requirements.txt -r dev/requirements.txt + - name: Pytest Windows + run: pytest duet + test-macos: + name: Pytest MacOS + strategy: + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Install requirements + run: pip install -r requirements.txt -r dev/requirements.txt + - name: Pytest check + run: pytest duet diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/.gitignore new/duet-0.2.8/.gitignore --- old/duet-0.2.7/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/.gitignore 2021-10-20 20:54:16.000000000 +0200 @@ -0,0 +1,133 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDEs +*.iml +.idea + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/.mypy.ini new/duet-0.2.8/.mypy.ini --- old/duet-0.2.7/.mypy.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/.mypy.ini 2023-02-27 21:10:50.000000000 +0100 @@ -0,0 +1,21 @@ +[mypy] +strict_optional = true +plugins = duet.typing +show_error_codes = true +warn_unused_ignores = true + +[mypy-__main__] +follow_imports = silent +ignore_missing_imports = true + +[mypy-grpc] +follow_imports = silent +ignore_missing_imports = true + +[mypy-pytest] +follow_imports = silent +ignore_missing_imports = true + +[mypy-aiocontext] +follow_imports = silent +ignore_missing_imports = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/.pylintrc new/duet-0.2.8/.pylintrc --- old/duet-0.2.7/.pylintrc 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/.pylintrc 2021-02-10 22:37:09.000000000 +0100 @@ -0,0 +1,40 @@ +[config] +disable=all +max-line-length=100 +enable= + anomalous-backslash-in-string, + bad-option-value, + bad-reversed-sequence, + bad-super-call, + continue-in-finally, + dangerous-default-value, + duplicate-argument-name, + expression-not-assigned, + f-string-without-interpolation, + function-redefined, + init-is-generator, + line-too-long, + mixed-indentation, + nonexistent-operator, + not-in-loop, + no-value-for-parameter + pointless-statement, + pointless-string-statement, + redefined-builtin, + relative-import, + return-arg-in-generator, + return-in-init, + return-outside-function, + singleton-comparison, + syntax-error, + undefined-variable, + unnecessary-pass, + unreachable, + unrecognized-inline-option, + unused-import, + unused-variable, + unused-wildcard-import, + wildcard-import, + wrong-import-order, + wrong-import-position, + yield-outside-function diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/AUTHORS new/duet-0.2.8/AUTHORS --- old/duet-0.2.7/AUTHORS 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/AUTHORS 2021-02-06 16:18:23.000000000 +0100 @@ -0,0 +1,7 @@ +# This is the list of Duet's significant contributors. +# +# This does not necessarily list everyone who has contributed code, +# especially since many employees of one corporation may be contributing. +# To see the full list of contributors, see the revision history in +# source control. +Google LLC diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/CONTRIBUTING.md new/duet-0.2.8/CONTRIBUTING.md --- old/duet-0.2.7/CONTRIBUTING.md 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/CONTRIBUTING.md 2021-02-05 23:48:38.000000000 +0100 @@ -0,0 +1,29 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement (CLA). You (or your employer) retain the copyright to your +contribution; this simply gives us permission to use and redistribute your +contributions as part of the project. Head over to +<https://cla.developers.google.com/> to see your current agreements on file or +to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code Reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/PKG-INFO new/duet-0.2.8/PKG-INFO --- old/duet-0.2.7/PKG-INFO 2022-06-18 03:52:56.991586000 +0200 +++ new/duet-0.2.8/PKG-INFO 2023-04-12 21:44:36.036382400 +0200 @@ -1,52 +1,49 @@ Metadata-Version: 2.1 Name: duet -Version: 0.2.7 +Version: 0.2.8 Summary: A simple future-based async library for python. Home-page: http://github.com/google/duet Author: The Duet Authors Author-email: maf...@google.com License: Apache 2 +Description: # duet + + A simple future-based async library for python + + Duet takes inspiration from the amazing [trio](https://trio.readthedocs.io/en/stable/) + library and the [structured concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/) + approach to async programming that it uses. + However, duet differs from trio in two major ways: + + - Instead of a full-blown implementation of asynchronous IO, duet relies on the + `Future` interface for parallelism, and provides a way to run async/await + coroutines around those `Future`s. This is useful if you are using an API that + returns futures, such as RPC libraries like gRPC. The standard `Future` + interface does not implement `__await__` directly, so `Future` instances must + be wrapped in `duet.AwaitableFuture`. + + - duet is re-entrant. At the top level, you run async code by calling + `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet + been fully refactored to be asynchronous, but itself calls `duet.run(bar)`. + Most async libraries, including `trio` and `asyncio`, will raise an exception + if you try to "re-enter" the event loop in this way, but duet allows it. We + have found that this can simplify the process of refactoring code to be + asynchronous because you don't have to completely separate the sync and async + parts of your codebase all at once. + + ## Installation + + Install from pypi: + + ``` + pip install duet + ``` + + ## Note + + duet is not an official Google project. + Platform: UNKNOWN Requires-Python: >=3.7.0 Description-Content-Type: text/markdown Provides-Extra: dev_env -License-File: LICENSE - -# duet - -A simple future-based async library for python - -Duet takes inspiration from the amazing [trio](https://trio.readthedocs.io/en/stable/) -library and the [structured concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/) -approach to async programming that it uses. -However, duet differs from trio in two major ways: - -- Instead of a full-blown implementation of asynchronous IO, duet relies on the - `Future` interface for parallelism, and provides a way to run async/await - coroutines around those `Future`s. This is useful if you are using an API that - returns futures, such as RPC libraries like gRPC. The standard `Future` - interface does not implement `__await__` directly, so `Future` instances must - be wrapped in `duet.AwaitableFuture`. - -- duet is re-entrant. At the top level, you run async code by calling - `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet - been fully refactored to be asynchronous, but itself calls `duet.run(bar)`. - Most async libraries, including `trio` and `asyncio`, will raise an exception - if you try to "re-enter" the event loop in this way, but duet allows it. We - have found that this can simplify the process of refactoring code to be - asynchronous because you don't have to completely separate the sync and async - parts of your codebase all at once. - -## Installation - -Install from pypi: - -``` -pip install duet -``` - -## Note - -duet is not an official Google project. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/dev/build new/duet-0.2.8/dev/build --- old/duet-0.2.7/dev/build 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/dev/build 2021-09-30 10:10:16.000000000 +0200 @@ -0,0 +1,31 @@ +#!/usr/bin/env sh + +if [ -d "dist" ]; then + rm -rf dist/ +fi + +for arg in "$@"; do + case $arg in + --pre) + SRC_VERSION_LINE=$(cat "duet/_version.py" | tail -n 1) + SRC_VERSION=$(echo $SRC_VERSION_LINE | cut -d'"' -f 2) + if [[ ${SRC_VERSION} != *"dev" ]]; then + echo "Version doesn't end in dev: ${SRC_VERSION_LINE}" >&2 + exit 1 + fi + export DUET_PRE_RELEASE_VERSION="${SRC_VERSION}$(date "+%Y%m%d%H%M%S")" + echo "pre-release version: ${DUET_PRE_RELEASE_VERSION}" + ;; + + --upload) + export UPLOAD="yes" + ;; + esac +done + +python setup.py sdist bdist_wheel + +if [ -n "${UPLOAD}" ]; then + echo "uploading..." + twine upload dist/* +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/dev/check new/duet-0.2.8/dev/check --- old/duet-0.2.7/dev/check 1970-01-01 01:00:00.000000000 +0100 +++ new/duet-0.2.8/dev/check 2021-02-10 22:37:09.000000000 +0100 @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +black duet/ +isort duet/ +mypy duet/ +pylint duet/ +pytest duet/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet/_version.py new/duet-0.2.8/duet/_version.py --- old/duet-0.2.7/duet/_version.py 2022-06-18 03:35:26.000000000 +0200 +++ new/duet-0.2.8/duet/_version.py 2023-04-12 21:43:44.000000000 +0200 @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.2.7" +__version__ = "0.2.8" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet/api.py new/duet-0.2.8/duet/api.py --- old/duet-0.2.7/duet/api.py 2022-06-18 03:23:31.000000000 +0200 +++ new/duet-0.2.8/duet/api.py 2023-04-12 21:17:44.000000000 +0200 @@ -51,9 +51,25 @@ Returns: The final result of the async function. """ - with impl.Scheduler() as scheduler: + scheduler = impl.Scheduler() + scheduler.init_signals() + try: task = scheduler.spawn(func(*args, **kwds)) - return task.result + try: + while scheduler.active_tasks: + scheduler.tick() + except BaseException as exc: + for task in scheduler.active_tasks: + task.interrupt(None, exc) + while scheduler.active_tasks: + try: + scheduler.tick() + except BaseException: + pass + raise + return task.result + finally: + scheduler.cleanup_signals() def sync(f: Callable[..., Awaitable[T]]) -> Callable[..., T]: @@ -205,8 +221,11 @@ async for i, arg in aenumerate(iterable): slot = await limiter.acquire() gen_scope.spawn(task, i, arg, slot) - except Exception as e: + except BaseException as e: collector.error(e) + if isinstance(e, GeneratorExit): + # Raise to avoid "coroutine ignored GeneratorExit" errors. + raise else: collector.done() @@ -322,7 +341,7 @@ try: yield scope await finish_tasks() - except (impl.Interrupt, Exception) as exc: + except BaseException as exc: # Interrupt remaining tasks. for task in tasks: if not task.done: @@ -418,13 +437,15 @@ def _release(self): self._count -= 1 - if self._waiters: + # Release the first waiter that has not yet been cancelled. + while self._waiters: f = self._waiters.popleft() - f.try_set_result(None) + if f.try_set_result(None): + break if self._available_waiters: for f in self._available_waiters: f.try_set_result(None) - self._available_waiters = [] + self._available_waiters.clear() async def available(self) -> None: """Wait until this limiter is available (i.e. not full to capacity). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet/api_test.py new/duet-0.2.8/duet/api_test.py --- old/duet-0.2.7/duet/api_test.py 2022-06-18 03:22:49.000000000 +0200 +++ new/duet-0.2.8/duet/api_test.py 2023-02-27 21:10:50.000000000 +0100 @@ -14,11 +14,12 @@ import abc import concurrent.futures +import contextlib import inspect import sys import time import traceback -from typing import List +from typing import List, Tuple import pytest @@ -338,12 +339,58 @@ # Ensure that all spawned tasks completed in the right order. assert completed == list(range(4)) + @duet.sync + async def test_cancel(self) -> None: + limiter = duet.Limiter(1) + + async def func( + ready: duet.AwaitableFuture[duet.Scope], done: duet.AwaitableFuture[Tuple[bool, bool]] + ) -> None: + """Acquired and release the lock, and record what happened.""" + async with duet.new_scope(timeout=1) as scope: + ready.set_result(scope) + acquired = False + cancelled = False + try: + async with limiter: + acquired = True + except duet.CancelledError: + cancelled = True + done.set_result((acquired, cancelled)) + + async with contextlib.AsyncExitStack() as exit_stack: + scope = await exit_stack.enter_async_context(duet.new_scope()) + + # first acquire the lock + await exit_stack.enter_async_context(limiter) + + # now spawn two coroutines that will attempt to acquire the lock + ready1 = duet.AwaitableFuture[duet.Scope]() + done1 = duet.AwaitableFuture[Tuple[bool, bool]]() + scope.spawn(func, ready1, done1) + scope1 = await ready1 + + ready2 = duet.AwaitableFuture[duet.Scope]() + done2 = duet.AwaitableFuture[Tuple[bool, bool]]() + scope.spawn(func, ready2, done2) + _scope2 = await ready2 + + # cancel the first waiting coroutine + scope1.cancel() + + # ensure that first coroutine was cancelled and second coroutine got the lock. + async with duet.new_scope(timeout=0.1): + acquired1, cancelled1 = await done1 + acquired2, cancelled2 = await done2 + assert cancelled1 and not acquired1 + assert acquired2 and not cancelled2 + @duet.sync async def test_sleep(): start = time.time() await duet.sleep(0.5) - assert abs((time.time() - start) - 0.5) < 0.2 + assert abs((time.time() - start) - 0.5) < 0.3 @duet.sync @@ -352,7 +399,7 @@ with pytest.raises(TimeoutError): async with duet.timeout_scope(0.5): await duet.sleep(10) - assert abs((time.time() - start) - 0.5) < 0.2 + assert abs((time.time() - start) - 0.5) < 0.3 @duet.sync @@ -360,7 +407,7 @@ start = time.time() for _ in range(5): await duet.sleep(0.1) - assert abs((time.time() - start) - 0.5) < 0.2 + assert abs((time.time() - start) - 0.5) < 0.3 @duet.sync @@ -370,7 +417,7 @@ async with duet.timeout_scope(0.5): for _ in range(5): await duet.sleep(0.2) - assert abs((time.time() - start) - 0.5) < 0.2 + assert abs((time.time() - start) - 0.5) < 0.3 class TestScope: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet/impl.py new/duet-0.2.8/duet/impl.py --- old/duet-0.2.7/duet/impl.py 2022-06-18 03:23:31.000000000 +0200 +++ new/duet-0.2.8/duet/impl.py 2023-04-12 21:17:44.000000000 +0200 @@ -131,7 +131,7 @@ self._result = e.value self._state = TaskState.SUCCEEDED return - except (Interrupt, Exception) as error: + except BaseException as error: self._error = error self._state = TaskState.FAILED if self.main_task: @@ -392,7 +392,7 @@ break try: ready_tasks = self._ready_tasks.get_all( - min(0, max(timeout, threading.TIMEOUT_MAX)) + max(0, min(timeout, threading.TIMEOUT_MAX)) ) break except TimeoutError: @@ -441,35 +441,13 @@ frame = frame.f_back return False - def __enter__(self): + def init_signals(self): if ( threading.current_thread() == threading.main_thread() and signal.getsignal(signal.SIGINT) == signal.default_int_handler ): self._prev_signal = signal.signal(signal.SIGINT, self._interrupt) - return self - def __exit__(self, exc_type, exc, tb): - def finish_tasks(error=None): - if error: - for task in self.active_tasks: - task.interrupt(None, error) - while self.active_tasks: - try: - self.tick() - except Exception: - if not error: - raise - - try: - if exc: - finish_tasks(exc) - else: - try: - finish_tasks() - except Exception as exc: - finish_tasks(exc) - raise - finally: - if self._prev_signal: - signal.signal(signal.SIGINT, self._prev_signal) + def cleanup_signals(self): + if self._prev_signal: + signal.signal(signal.SIGINT, self._prev_signal) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet/typing.py new/duet-0.2.8/duet/typing.py --- old/duet-0.2.7/duet/typing.py 2022-05-04 09:11:02.000000000 +0200 +++ new/duet-0.2.8/duet/typing.py 2023-02-27 21:10:50.000000000 +0100 @@ -18,10 +18,10 @@ https://mypy.readthedocs.io/en/stable/extending_mypy.html#extending-mypy-using-plugins """ -from typing import Callable, Optional +from typing import Callable, List, Optional from mypy.plugin import FunctionContext, Plugin -from mypy.types import CallableType, get_proper_type, Instance, Type +from mypy.types import CallableType, get_proper_type, Instance, Overloaded, Type def duet_sync_callback(ctx: FunctionContext) -> Type: @@ -37,10 +37,27 @@ functions wrapped by duet.sync. """ func_type = get_proper_type(ctx.arg_types[0][0]) - if not isinstance(func_type, CallableType): + if not isinstance(func_type, (CallableType, Overloaded)): ctx.api.msg.fail(f"expected Callable[..., Awaitable[T]], got {func_type}", ctx.context) return ctx.default_return_type + if isinstance(func_type, CallableType): + return modify_callable(func_type, ctx) + + # func_type is overloaded + overloaded_callables: List[CallableType] = [] + for ft in func_type.items: + overload_type = modify_callable(ft, ctx) + if not isinstance(overload_type, CallableType): + ctx.api.msg.fail( + f"expected overloaded type to be callable, got {overload_type}", ctx.context + ) + return ctx.default_return_type + overloaded_callables.append(overload_type) + return Overloaded(overloaded_callables) + + +def modify_callable(func_type: CallableType, ctx: FunctionContext) -> Type: # Note that the return type of an async function is Coroutine[Any, Any, T], # which is a subtype of Awaitable[T]. See: # https://mypy.readthedocs.io/en/stable/more_types.html#typing-async-await diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet.egg-info/PKG-INFO new/duet-0.2.8/duet.egg-info/PKG-INFO --- old/duet-0.2.7/duet.egg-info/PKG-INFO 2022-06-18 03:52:56.000000000 +0200 +++ new/duet-0.2.8/duet.egg-info/PKG-INFO 2023-04-12 21:44:35.000000000 +0200 @@ -1,52 +1,49 @@ Metadata-Version: 2.1 Name: duet -Version: 0.2.7 +Version: 0.2.8 Summary: A simple future-based async library for python. Home-page: http://github.com/google/duet Author: The Duet Authors Author-email: maf...@google.com License: Apache 2 +Description: # duet + + A simple future-based async library for python + + Duet takes inspiration from the amazing [trio](https://trio.readthedocs.io/en/stable/) + library and the [structured concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/) + approach to async programming that it uses. + However, duet differs from trio in two major ways: + + - Instead of a full-blown implementation of asynchronous IO, duet relies on the + `Future` interface for parallelism, and provides a way to run async/await + coroutines around those `Future`s. This is useful if you are using an API that + returns futures, such as RPC libraries like gRPC. The standard `Future` + interface does not implement `__await__` directly, so `Future` instances must + be wrapped in `duet.AwaitableFuture`. + + - duet is re-entrant. At the top level, you run async code by calling + `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet + been fully refactored to be asynchronous, but itself calls `duet.run(bar)`. + Most async libraries, including `trio` and `asyncio`, will raise an exception + if you try to "re-enter" the event loop in this way, but duet allows it. We + have found that this can simplify the process of refactoring code to be + asynchronous because you don't have to completely separate the sync and async + parts of your codebase all at once. + + ## Installation + + Install from pypi: + + ``` + pip install duet + ``` + + ## Note + + duet is not an official Google project. + Platform: UNKNOWN Requires-Python: >=3.7.0 Description-Content-Type: text/markdown Provides-Extra: dev_env -License-File: LICENSE - -# duet - -A simple future-based async library for python - -Duet takes inspiration from the amazing [trio](https://trio.readthedocs.io/en/stable/) -library and the [structured concurrency](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/) -approach to async programming that it uses. -However, duet differs from trio in two major ways: - -- Instead of a full-blown implementation of asynchronous IO, duet relies on the - `Future` interface for parallelism, and provides a way to run async/await - coroutines around those `Future`s. This is useful if you are using an API that - returns futures, such as RPC libraries like gRPC. The standard `Future` - interface does not implement `__await__` directly, so `Future` instances must - be wrapped in `duet.AwaitableFuture`. - -- duet is re-entrant. At the top level, you run async code by calling - `duet.run(foo)`. Inside `foo` suppose you call a function that has not yet - been fully refactored to be asynchronous, but itself calls `duet.run(bar)`. - Most async libraries, including `trio` and `asyncio`, will raise an exception - if you try to "re-enter" the event loop in this way, but duet allows it. We - have found that this can simplify the process of refactoring code to be - asynchronous because you don't have to completely separate the sync and async - parts of your codebase all at once. - -## Installation - -Install from pypi: - -``` -pip install duet -``` - -## Note - -duet is not an official Google project. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet.egg-info/SOURCES.txt new/duet-0.2.8/duet.egg-info/SOURCES.txt --- old/duet-0.2.7/duet.egg-info/SOURCES.txt 2022-06-18 03:52:56.000000000 +0200 +++ new/duet-0.2.8/duet.egg-info/SOURCES.txt 2023-04-12 21:44:35.000000000 +0200 @@ -1,3 +1,8 @@ +.gitignore +.mypy.ini +.pylintrc +AUTHORS +CONTRIBUTING.md LICENSE MANIFEST.in README.md @@ -5,6 +10,9 @@ requirements.txt setup.cfg setup.py +.github/workflows/ci.yml +dev/build +dev/check dev/requirements.txt duet/__init__.py duet/_version.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/duet.egg-info/requires.txt new/duet-0.2.8/duet.egg-info/requires.txt --- old/duet-0.2.7/duet.egg-info/requires.txt 2022-06-18 03:52:56.000000000 +0200 +++ new/duet-0.2.8/duet.egg-info/requires.txt 2023-04-12 21:44:35.000000000 +0200 @@ -1,6 +1,6 @@ [:python_version <= "3.7"] -typing-extensions==3.10.0 +typing-extensions>=3.10.0 [dev_env] black==22.3.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/duet-0.2.7/requirements.txt new/duet-0.2.8/requirements.txt --- old/duet-0.2.7/requirements.txt 2022-06-18 03:22:49.000000000 +0200 +++ new/duet-0.2.8/requirements.txt 2023-04-12 21:35:44.000000000 +0200 @@ -1 +1 @@ -typing-extensions == 3.10.0; python_version <= '3.7' +typing-extensions >= 3.10.0; python_version <= '3.7'