Skip to content

Commit

Permalink
Merge branch 'default' into fix-interceptor-abstract
Browse files Browse the repository at this point in the history
  • Loading branch information
graingert authored Dec 15, 2024
2 parents d4def61 + a883528 commit be41b0a
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 183 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,33 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
allow-prereleases: true
cache: pip
cache-dependency-path: pyproject.toml
python-version: "3.x"

- name: Install
run: pip install .

- uses: jakebailey/pyright-action@v2
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", pypy-3.10]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: pip install .
- name: Test
run: python demo.py
build:
runs-on: ubuntu-latest
timeout-minutes: 30
Expand All @@ -32,6 +53,9 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
allow-prereleases: true
cache: pip
cache-dependency-path: pyproject.toml
python-version: "3.x"

- name: build
Expand Down Expand Up @@ -72,6 +96,7 @@ jobs:
needs:
- build
- typecheck
- test

runs-on: ubuntu-latest

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ run(main())
```

# changelog
## 0.2.0
### What's Changed
* add readme and changelog by @graingert in https://github.com/graingert/taskgroup/pull/21
* Add link to PyPI by @graingert in https://github.com/graingert/taskgroup/pull/22
* changes from 3.12.8, add a smoke test, make uncancel installation simpler by @graingert in https://github.com/graingert/taskgroup/pull/23


**Full Changelog**: https://github.com/graingert/taskgroup/compare/0.1.1...0.2.0

## 0.1.1
### What's Changed
Expand Down
2 changes: 1 addition & 1 deletion taskgroup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
backport of asyncio.TaskGroup, asyncio.Runner and asyncio.timeout
"""

__version__ = "0.1.1"
__version__ = "0.2.0"

__all__ = ["run", "Runner", "TaskGroup", "Timeout", "timeout", "timeout_at"]

Expand Down
35 changes: 21 additions & 14 deletions taskgroup/runners.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# backported from cpython 3.12 bceb197947bbaebb11e01195bdce4f240fdf9332
# Copyright © 2001-2022 Python Software Foundation; All Rights Reserved
# modified to support working on 3.10, custom task_factory installed to
# backported from cpython 3.12.8 2dc476bcb9142cd25d7e1d52392b73a3dcdf1756
# Copyright © 2001 Python Software Foundation; All Rights Reserved
# modified to support working on 3.9, custom task_factory installed to
# support uncancel and contexts
from __future__ import annotations

__all__ = ("Runner", "run")

import sys

import collections.abc
import contextvars
import enum
import functools
import signal
import threading
from asyncio import AbstractEventLoop, coroutines, events, exceptions, tasks
from asyncio import AbstractEventLoop, coroutines, events, exceptions, tasks, constants
from typing import Any, TypeVar, final

from typing_extensions import Self
Expand Down Expand Up @@ -89,7 +91,12 @@ def close(self) -> None:
try:
_cancel_all_tasks(loop)
loop.run_until_complete(loop.shutdown_asyncgens())
loop.run_until_complete(loop.shutdown_default_executor())
if sys.version_info >= (3, 12):
loop.run_until_complete(
loop.shutdown_default_executor(constants.THREAD_JOIN_TIMEOUT) # type: ignore
)
else:
loop.run_until_complete(loop.shutdown_default_executor())
finally:
if self._set_event_loop:
events.set_event_loop(None)
Expand Down Expand Up @@ -143,8 +150,6 @@ def run(

self._interrupt_count = 0
try:
if self._set_event_loop:
events.set_event_loop(self._loop)
return self._loop.run_until_complete(task)
except exceptions.CancelledError:
if self._interrupt_count > 0:
Expand Down Expand Up @@ -180,24 +185,22 @@ def _lazy_init(self) -> None:
self._state = _State.INITIALIZED

def _on_sigint(self, signum, frame, main_task):
assert self._loop is not None
self._interrupt_count += 1
if self._interrupt_count == 1 and not main_task.done():
main_task.cancel()
# wakeup loop if it is blocked by select() with long timeout
assert self._loop is not None
self._loop.call_soon_threadsafe(lambda: None)
return
raise KeyboardInterrupt()


def run(
main: collections.abc.Coroutine[Any, Any, _T], *, debug: bool | None = None
) -> _T:
def run(main, *, debug=None, loop_factory=None):
"""Execute the coroutine and return the result.
This function runs the passed coroutine, taking care of
managing the asyncio event loop and finalizing asynchronous
generators.
managing the asyncio event loop, finalizing asynchronous
generators and closing the default executor.
This function cannot be called when another asyncio event loop is
running in the same thread.
Expand All @@ -208,6 +211,10 @@ def run(
It should be used as a main entry point for asyncio programs, and should
ideally only be called once.
The executor is given a timeout duration of 5 minutes to shutdown.
If the executor hasn't finished within that duration, a warning is
emitted and the executor is closed.
Example:
async def main():
Expand All @@ -220,7 +227,7 @@ async def main():
# fail fast with short traceback
raise RuntimeError("asyncio.run() cannot be called from a running event loop")

with Runner(debug=debug) as runner:
with Runner(debug=debug, loop_factory=loop_factory) as runner:
return runner.run(main)


Expand Down
Loading

0 comments on commit be41b0a

Please sign in to comment.