Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
abec35d
chore: auto-update documentation a_sync/async_property/cached.pyi
Jun 24, 2025
ff25e47
chore: auto-update documentation a_sync/a_sync/flags.pyi
Jun 24, 2025
ea30c15
chore: auto-update documentation a_sync/sphinx/ext.py
Jun 24, 2025
183c238
chore: auto-update documentation a_sync/primitives/locks/semaphore.pyx
Jun 24, 2025
4439ae7
chore: auto-update documentation tests/primitives/test_semaphore.py
Jun 24, 2025
fdeaeaf
chore: auto-update documentation a_sync/_smart.pyi
Jun 24, 2025
7dc1c28
chore: auto-update documentation a_sync/primitives/locks/prio_semapho…
Jun 26, 2025
bbf90f5
chore: auto-update documentation a_sync/_smart.pyx
Jun 26, 2025
2933769
chore: auto-update documentation a_sync/a_sync/_helpers.pyi
Jun 26, 2025
b19889b
chore: auto-update documentation a_sync/primitives/locks/semaphore.pyx
Jun 26, 2025
1cdaae7
chore: auto-update documentation a_sync/future.py
Jun 26, 2025
946d2a0
chore: auto-update documentation tests/asyncio/test_as_completed.py
Jun 26, 2025
6ecebc6
chore: auto-update documentation a_sync/asyncio/gather.pyi
Jun 26, 2025
d730966
chore: auto-update documentation a_sync/asyncio/gather.pyx
Jun 26, 2025
9bf1837
chore: auto-update documentation tests/a_sync/test_limiter.py
Jun 26, 2025
1a9f9a9
chore: auto-update documentation a_sync/primitives/_debug.pyx
Jun 26, 2025
92f9526
chore: auto-update documentation a_sync/primitives/queue.py
Jun 26, 2025
0d62eaa
chore: auto-update documentation a_sync/primitives/locks/event.pyx
Jun 26, 2025
a6b5a82
chore: auto-update documentation a_sync/primitives/locks/event.pyi
Jun 26, 2025
a29d687
chore: auto-update documentation a_sync/primitives/locks/prio_semapho…
Jun 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 152 additions & 90 deletions a_sync/_smart.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,63 @@ _Key = Tuple[_Args, _Kwargs]

def shield(arg: Awaitable[_T]) -> Union[SmartFuture[_T], "Future[_T]"]:
"""
Wait for a future, shielding it from cancellation.
Wait for an awaitable, shielding it from cancellation.

The statement
This function awaits the given awaitable, similarly to using the `await`
operator directly. The key difference is that if the coroutine awaiting the
result is cancelled, the underlying task running the awaitable is not cancelled.
Instead, cancellation propagates only to the awaiting coroutine while the awaited
operation continues to run.

res = await shield(something())
Note:
If the awaited coroutine is cancelled by external means, its cancellation will
still affect the shield operation.

is exactly equivalent to the statement

res = await something()

*except* that if the coroutine containing it is cancelled, the
task running in something() is not cancelled. From the POV of
something(), the cancellation did not happen. But its caller is
still cancelled, so the yield-from expression still raises
CancelledError. Note: If something() is cancelled by other means
this will still cancel shield().

If you want to completely ignore cancellation (not recommended)
you can combine shield() with a try/except clause, as follows:
Example:
Basic usage:
::
result = await shield(my_coroutine())

try:
res = await shield(something())
except CancelledError:
res = None
Ignoring cancellation (not recommended):
::
try:
result = await shield(my_coroutine())
except CancelledError:
result = None

Args:
arg: The awaitable to shield from cancellation.
loop: Optional; the event loop. Deprecated since Python 3.8.
arg: The awaitable to shield from cancellation.

Returns:
A :class:`SmartFuture` or :class:`asyncio.Future` instance.

Example:
Using shield to protect a coroutine from cancellation:

```python
result = await shield(my_coroutine())
```
A :class:`SmartFuture` or :class:`asyncio.Future` instance.

See Also:
- :func:`asyncio.shield`
- :func:`asyncio.shield`
"""

class _SmartFutureMixin(Generic[_T]):
"""
Mixin class that provides common functionality for smart futures and tasks.

This mixin provides methods for managing waiters and integrating with a smart processing queue.
It uses weak references to manage resources efficiently.
This mixin provides methods for managing waiters and integrating with a smart
processing queue. It uses weak references to manage resources efficiently.

Example:
Creating a SmartFuture and awaiting it:
Creating and awaiting a smart future:

```python
future = SmartFuture()
result = await future
```
::
future = SmartFuture()
result = await future

Creating a SmartTask and awaiting it:
Creating and awaiting a smart task:

```python
task = SmartTask(coro=my_coroutine())
result = await task
```
::
task = SmartTask(coro=my_coroutine())
result = await task

See Also:
- :class:`SmartFuture`
- :class:`SmartTask`
- :class:`SmartFuture`
- :class:`SmartTask`
"""

_queue: Optional["SmartProcessingQueue[Any, Any, _T]"] = None
Expand All @@ -95,59 +84,57 @@ class SmartFuture(_SmartFutureMixin[_T], Future):
for tracking waiters and integrating with a smart processing queue.

Example:
Creating and awaiting a SmartFuture:
Creating and awaiting a SmartFuture:

```python
future = SmartFuture()
await future
```
::
future = SmartFuture()
await future

See Also:
- :class:`_SmartFutureMixin`
- :class:`asyncio.Future`
- :class:`_SmartFutureMixin`
- :class:`asyncio.Future`
"""

def __await__(self) -> Generator[Any, None, T]:
def __await__(self) -> Generator[Any, None, _T]:
"""
Await the SmartFuture, handling waiters and logging.

Yields:
The result of the future.
The result of the future.

Example:
```python
future = SmartFuture()
result = await future
```
::
future = SmartFuture()
result = await future
"""

def create_future(
*,
queue: Optional["SmartProcessingQueue"] = None,
key: Optional[_Key] = None,
loop: Optional[AbstractEventLoop] = None,
) -> SmartFuture[_T]:
"""
Create a :class:`~SmartFuture` instance.
Create a :class:`SmartFuture` instance.

Args:
queue: Optional; a smart processing queue.
key: Optional; a key identifying the future.
loop: Optional; the event loop.
queue: Optional; a smart processing queue.
key: Optional; a key identifying the future.
loop: Optional; the event loop.

Returns:
A SmartFuture instance.
A SmartFuture instance.

Example:
Creating a SmartFuture using the factory function:
Creating a SmartFuture using the factory function:

```python
future = create_future(queue=my_queue, key=my_key)
```
::
future = create_future(queue=my_queue, key=my_key)

See Also:
- :class:`SmartFuture`
- :class:`SmartFuture`
"""
return _SmartFuture(queue=queue, key=key, loop=loop or get_event_loop())

class SmartTask(_SmartFutureMixin[_T], Task):
"""
Expand All @@ -157,46 +144,121 @@ class SmartTask(_SmartFutureMixin[_T], Task):
for tracking waiters and integrating with a smart processing queue.

Example:
Creating and awaiting a SmartTask:
Creating and awaiting a SmartTask:

```python
task = SmartTask(coro=my_coroutine())
await task
```
::
task = SmartTask(coro=my_coroutine())
result = await task

See Also:
- :class:`_SmartFutureMixin`
- :class:`asyncio.Task`
- :class:`_SmartFutureMixin`
- :class:`asyncio.Task`
"""

def __await__(self) -> Generator[Any, None, T]:
def __await__(self) -> Generator[Any, None, _T]:
"""
Await the SmartTask, handling waiters and logging.

Yields:
The result of the task.
The result of the task.

Example:
```python
task = SmartTask(coro=my_coroutine())
result = await task
```
::
task = SmartTask(coro=my_coroutine())
result = await task
"""

def set_smart_task_factory(loop: AbstractEventLoop = None) -> None:
"""
Set the event loop's task factory to :func:`~smart_task_factory` so all tasks will be SmartTask instances.
Set the event loop's task factory to :func:`smart_task_factory` so all tasks will be SmartTask instances.

Args:
loop: Optional; the event loop. If None, the current event loop is used.
loop: Optional; the event loop. If None, the current event loop is used.

Example:
Setting the smart task factory for the current event loop:
Setting the smart task factory for the current event loop:

```python
set_smart_task_factory()
```
::
set_smart_task_factory()

See Also:
- :func:`smart_task_factory`
- :func:`smart_task_factory`
"""

cpdef object smart_task_factory(loop: AbstractEventLoop, coro: Awaitable[_T]):
"""
Task factory function that an event loop calls to create new tasks.

This factory function utilizes ez-a-sync's custom :class:`SmartTask` implementation.

Args:
loop: The event loop.
coro: The coroutine to run in the task.

Returns:
A SmartTask instance running the provided coroutine.

Example:
Using the smart task factory to create a SmartTask:

::
loop = asyncio.get_event_loop()
task = smart_task_factory(loop, my_coroutine())

See Also:
- :func:`set_smart_task_factory`
- :class:`SmartTask`
"""
return _SmartTask(coro, loop=loop)

cpdef object shield(arg: Awaitable[_T]) -> Union[SmartFuture[_T], "Future[_T]"]:
"""
Wait for an awaitable, shielding it from cancellation.

This function awaits the given awaitable, similar to using the `await`
operator directly. However, if the coroutine awaiting the result is cancelled,
the underlying task is not cancelled. Cancellation propagates only to the awaiting
coroutine while the awaited operation continues to run.

Note:
If the awaited coroutine is cancelled by external factors, its cancellation will
still propagate to the shielded operation.

Example:
Basic usage:
::
result = await shield(my_coroutine())

Ignoring cancellation (not recommended):
::
try:
result = await shield(my_coroutine())
except CancelledError:
result = None

Args:
arg: The awaitable to shield from cancellation.

Returns:
A :class:`SmartFuture` or :class:`asyncio.Future` instance.

See Also:
- :func:`asyncio.shield`
"""
inner = ensure_future(arg)
if _is_done(inner):
# Shortcut.
return inner

loop = _get_loop(inner)
outer = _SmartFuture(loop=loop)

# special handling to connect SmartFutures to SmartTasks if enabled
if (waiters := getattr(inner, "_waiters", None)) is not None:
waiters.add(outer)

_inner_done_callback, _outer_done_callback = _get_done_callbacks(inner, outer)

inner.add_done_callback(_inner_done_callback)
outer.add_done_callback(_outer_done_callback)
return outer
Loading
Loading