ez-a-sync 0.22.14__py3-none-any.whl → 0.22.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ez-a-sync might be problematic. Click here for more details.
- a_sync/ENVIRONMENT_VARIABLES.py +37 -5
- a_sync/__init__.py +53 -12
- a_sync/_smart.py +231 -28
- a_sync/_typing.py +112 -15
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +248 -38
- a_sync/a_sync/_flags.py +78 -9
- a_sync/a_sync/_helpers.py +46 -13
- a_sync/a_sync/_kwargs.py +33 -8
- a_sync/a_sync/_meta.py +149 -28
- a_sync/a_sync/abstract.py +150 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +85 -14
- a_sync/a_sync/decorator.py +441 -139
- a_sync/a_sync/function.py +709 -147
- a_sync/a_sync/method.py +437 -110
- a_sync/a_sync/modifiers/__init__.py +85 -5
- a_sync/a_sync/modifiers/cache/__init__.py +116 -17
- a_sync/a_sync/modifiers/cache/memory.py +130 -20
- a_sync/a_sync/modifiers/limiter.py +101 -22
- a_sync/a_sync/modifiers/manager.py +142 -16
- a_sync/a_sync/modifiers/semaphores.py +121 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +44 -19
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +140 -1
- a_sync/asyncio/as_completed.py +213 -79
- a_sync/asyncio/create_task.py +70 -20
- a_sync/asyncio/gather.py +125 -58
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +248 -26
- a_sync/executor.py +164 -69
- a_sync/future.py +1227 -168
- a_sync/iter.py +173 -56
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +72 -18
- a_sync/primitives/_loggable.py +41 -10
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +107 -38
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +262 -63
- a_sync/primitives/locks/semaphore.py +138 -89
- a_sync/primitives/queue.py +601 -60
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +313 -112
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +170 -50
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +250 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +22 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +264 -19
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +31 -13
- tests/test_future.py +45 -8
- tests/test_gather.py +8 -2
- tests/test_helpers.py +2 -0
- tests/test_iter.py +55 -13
- tests/test_limiter.py +5 -3
- tests/test_meta.py +23 -9
- tests/test_modified.py +4 -1
- tests/test_semaphore.py +15 -8
- tests/test_singleton.py +28 -11
- tests/test_task.py +162 -36
- ez_a_sync-0.22.14.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
a_sync/ENVIRONMENT_VARIABLES.py
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
|
-
|
|
2
1
|
from typed_envs import EnvVarFactory
|
|
3
2
|
|
|
4
3
|
envs = EnvVarFactory("EZASYNC")
|
|
5
4
|
|
|
6
5
|
# We have some envs here to help you debug your custom class implementations
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
DEBUG_CLASS_NAME = envs.create_env("DEBUG_CLASS_NAME", str, default="", verbose=False)
|
|
8
|
+
"""str: The name of the class to debug.
|
|
9
|
+
|
|
10
|
+
If you're only interested in debugging a specific class, set this to the class name.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
To debug a class named `MyClass`, set the environment variable:
|
|
14
|
+
|
|
15
|
+
.. code-block:: bash
|
|
16
|
+
|
|
17
|
+
export EZASYNC_DEBUG_CLASS_NAME=MyClass
|
|
18
|
+
|
|
19
|
+
See Also:
|
|
20
|
+
:func:`DEBUG_MODE` for enabling debug mode on all classes.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
DEBUG_MODE = envs.create_env(
|
|
24
|
+
"DEBUG_MODE", bool, default=bool(DEBUG_CLASS_NAME), verbose=False
|
|
25
|
+
)
|
|
26
|
+
"""bool: Enables debug mode on all classes.
|
|
27
|
+
|
|
28
|
+
Set this environment variable to `True` to enable debug mode on all classes.
|
|
29
|
+
If `DEBUG_CLASS_NAME` is set to a truthy value other than an empty string,
|
|
30
|
+
`DEBUG_MODE` will default to `True`.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
To enable debug mode globally, set the environment variable:
|
|
34
|
+
|
|
35
|
+
.. code-block:: bash
|
|
36
|
+
|
|
37
|
+
export EZASYNC_DEBUG_MODE=True
|
|
38
|
+
|
|
39
|
+
If you have set `DEBUG_CLASS_NAME` to a specific class, `DEBUG_MODE` will
|
|
40
|
+
automatically be `True` unless `DEBUG_CLASS_NAME` is an empty string.
|
|
10
41
|
|
|
11
|
-
|
|
12
|
-
|
|
42
|
+
See Also:
|
|
43
|
+
:func:`DEBUG_CLASS_NAME` for debugging a specific class.
|
|
44
|
+
"""
|
a_sync/__init__.py
CHANGED
|
@@ -1,8 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module initializes the a_sync library by importing and organizing various components, utilities, and classes.
|
|
3
|
+
It provides a convenient and unified interface for asynchronous programming with a focus on flexibility and efficiency.
|
|
4
|
+
|
|
5
|
+
The `a_sync` library offers decorators and base classes to facilitate writing both synchronous and asynchronous code.
|
|
6
|
+
It includes the `@a_sync()` decorator and the `ASyncGenericBase` class, which allow for creating functions and classes
|
|
7
|
+
that can operate in both synchronous and asynchronous contexts. Additionally, it provides enhanced asyncio primitives,
|
|
8
|
+
such as queues and locks, with extra functionality.
|
|
9
|
+
|
|
10
|
+
Modules and components included:
|
|
11
|
+
- :mod:`~aliases`, :mod:`~exceptions`, :mod:`~iter`, :mod:`~task`: Core modules of the library.
|
|
12
|
+
- :class:`~ASyncGenericBase`, :class:`~ASyncGenericSingleton`, :func:`~a_sync`: Base classes and decorators for dual-context execution.
|
|
13
|
+
- :class:`~ASyncCachedPropertyDescriptor`, :class:`~ASyncPropertyDescriptor`, `cached_property`, `property`: Property descriptors for async properties.
|
|
14
|
+
- :func:`~as_completed`, :func:`~create_task`, :func:`~gather`: Enhanced asyncio functions.
|
|
15
|
+
- Executors: :class:`~AsyncThreadPoolExecutor`, :class:`~AsyncProcessPoolExecutor`, :class:`~PruningThreadPoolExecutor` for async execution.
|
|
16
|
+
- Iterators: :class:`~ASyncFilter`, :class:`~ASyncSorter`, :class:`~ASyncIterable`, :class:`~ASyncIterator` for async iteration.
|
|
17
|
+
- Utilities: :func:`~all`, :func:`~any`, :func:`~as_yielded`, :func:`~exhaust_iterator`, :func:`~exhaust_iterators` for async utilities.
|
|
18
|
+
- :func:`~apply_semaphore`: Function to apply semaphores to coroutines.
|
|
19
|
+
|
|
20
|
+
Alias for backward compatibility:
|
|
21
|
+
- :class:`~ASyncBase` is an alias for :class:`~ASyncGenericBase`, which will be removed eventually, probably in version 0.1.0.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
Using the `@a_sync` decorator:
|
|
25
|
+
>>> from a_sync import a_sync
|
|
26
|
+
>>> @a_sync
|
|
27
|
+
... async def my_function():
|
|
28
|
+
... return "Hello, World!"
|
|
29
|
+
>>> result = await my_function()
|
|
30
|
+
>>> print(result)
|
|
31
|
+
|
|
32
|
+
Using `ASyncGenericBase` for dual-context classes:
|
|
33
|
+
>>> from a_sync import ASyncGenericBase
|
|
34
|
+
>>> class MyClass(ASyncGenericBase):
|
|
35
|
+
... async def my_method(self):
|
|
36
|
+
... return "Hello from MyClass"
|
|
37
|
+
>>> obj = MyClass()
|
|
38
|
+
>>> result = await obj.my_method()
|
|
39
|
+
>>> print(result)
|
|
40
|
+
|
|
41
|
+
See Also:
|
|
42
|
+
- :mod:`a_sync.a_sync`: Contains the core classes and decorators.
|
|
43
|
+
- :mod:`a_sync.asyncio`: Provides enhanced asyncio functions.
|
|
44
|
+
- :mod:`a_sync.primitives`: Includes modified versions of standard asyncio primitives.
|
|
45
|
+
"""
|
|
1
46
|
|
|
2
47
|
from a_sync import aliases, exceptions, iter, task
|
|
3
48
|
from a_sync.a_sync import ASyncGenericBase, ASyncGenericSingleton, a_sync
|
|
4
49
|
from a_sync.a_sync.modifiers.semaphores import apply_semaphore
|
|
5
|
-
from a_sync.a_sync.property import
|
|
50
|
+
from a_sync.a_sync.property import (
|
|
51
|
+
ASyncCachedPropertyDescriptor,
|
|
52
|
+
ASyncPropertyDescriptor,
|
|
53
|
+
cached_property,
|
|
54
|
+
property,
|
|
55
|
+
)
|
|
6
56
|
from a_sync.asyncio import as_completed, create_task, gather
|
|
7
57
|
from a_sync.executor import *
|
|
8
58
|
from a_sync.executor import AsyncThreadPoolExecutor as ThreadPoolExecutor
|
|
@@ -29,16 +79,13 @@ __all__ = [
|
|
|
29
79
|
"exceptions",
|
|
30
80
|
"iter",
|
|
31
81
|
"task",
|
|
32
|
-
|
|
33
82
|
# builtins
|
|
34
83
|
"sorted",
|
|
35
84
|
"filter",
|
|
36
|
-
|
|
37
85
|
# asyncio
|
|
38
86
|
"create_task",
|
|
39
|
-
"gather",
|
|
87
|
+
"gather",
|
|
40
88
|
"as_completed",
|
|
41
|
-
|
|
42
89
|
# functions
|
|
43
90
|
"a_sync",
|
|
44
91
|
"all",
|
|
@@ -47,33 +94,27 @@ __all__ = [
|
|
|
47
94
|
"exhaust_iterator",
|
|
48
95
|
"exhaust_iterators",
|
|
49
96
|
"map",
|
|
50
|
-
|
|
51
97
|
# classes
|
|
52
98
|
"ASyncIterable",
|
|
53
99
|
"ASyncIterator",
|
|
54
100
|
"ASyncGenericSingleton",
|
|
55
|
-
"TaskMapping",
|
|
56
|
-
|
|
101
|
+
"TaskMapping",
|
|
57
102
|
# property
|
|
58
103
|
"cached_property",
|
|
59
104
|
"property",
|
|
60
105
|
"ASyncPropertyDescriptor",
|
|
61
106
|
"ASyncCachedPropertyDescriptor",
|
|
62
|
-
|
|
63
107
|
# semaphores
|
|
64
108
|
"Semaphore",
|
|
65
109
|
"PrioritySemaphore",
|
|
66
110
|
"ThreadsafeSemaphore",
|
|
67
|
-
|
|
68
111
|
# queues
|
|
69
112
|
"Queue",
|
|
70
113
|
"ProcessingQueue",
|
|
71
114
|
"SmartProcessingQueue",
|
|
72
|
-
|
|
73
115
|
# locks
|
|
74
116
|
"CounterLock",
|
|
75
117
|
"Event",
|
|
76
|
-
|
|
77
118
|
# executors
|
|
78
119
|
"AsyncThreadPoolExecutor",
|
|
79
120
|
"PruningThreadPoolExecutor",
|
a_sync/_smart.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines smart future and task utilities for the a_sync library.
|
|
3
|
+
These utilities provide enhanced functionality for managing asynchronous tasks and futures,
|
|
4
|
+
including a custom task factory for creating :class:`~SmartTask` instances and a shielding mechanism
|
|
5
|
+
to protect tasks from cancellation.
|
|
6
|
+
"""
|
|
1
7
|
|
|
2
8
|
import asyncio
|
|
3
9
|
import logging
|
|
@@ -17,11 +23,43 @@ _Key = Tuple[_Args, _Kwargs]
|
|
|
17
23
|
|
|
18
24
|
logger = logging.getLogger(__name__)
|
|
19
25
|
|
|
26
|
+
|
|
20
27
|
class _SmartFutureMixin(Generic[T]):
|
|
28
|
+
"""
|
|
29
|
+
Mixin class that provides common functionality for smart futures and tasks.
|
|
30
|
+
|
|
31
|
+
This mixin provides methods for managing waiters and integrating with a smart processing queue.
|
|
32
|
+
"""
|
|
33
|
+
|
|
21
34
|
_queue: Optional["SmartProcessingQueue[Any, Any, T]"] = None
|
|
22
35
|
_key: _Key
|
|
23
36
|
_waiters: "weakref.WeakSet[SmartTask[T]]"
|
|
37
|
+
|
|
24
38
|
def __await__(self: Union["SmartFuture", "SmartTask"]) -> Generator[Any, None, T]:
|
|
39
|
+
"""
|
|
40
|
+
Await the smart future or task, handling waiters and logging.
|
|
41
|
+
|
|
42
|
+
Yields:
|
|
43
|
+
The result of the future or task.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
RuntimeError: If await wasn't used with future.
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
Awaiting a SmartFuture:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
future = SmartFuture()
|
|
53
|
+
result = await future
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Awaiting a SmartTask:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
task = SmartTask(coro=my_coroutine())
|
|
60
|
+
result = await task
|
|
61
|
+
```
|
|
62
|
+
"""
|
|
25
63
|
if self.done():
|
|
26
64
|
return self.result() # May raise too.
|
|
27
65
|
self._asyncio_future_blocking = True
|
|
@@ -32,33 +70,86 @@ class _SmartFutureMixin(Generic[T]):
|
|
|
32
70
|
if not self.done():
|
|
33
71
|
raise RuntimeError("await wasn't used with future")
|
|
34
72
|
return self.result() # May raise too.
|
|
73
|
+
|
|
35
74
|
@property
|
|
36
75
|
def num_waiters(self: Union["SmartFuture", "SmartTask"]) -> int:
|
|
37
76
|
# NOTE: we check .done() because the callback may not have ran yet and its very lightweight
|
|
77
|
+
"""
|
|
78
|
+
Get the number of waiters currently awaiting the future or task.
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
```python
|
|
82
|
+
future = SmartFuture()
|
|
83
|
+
print(future.num_waiters)
|
|
84
|
+
```
|
|
85
|
+
"""
|
|
38
86
|
if self.done():
|
|
39
87
|
# if there are any waiters left, there won't be once the event loop runs once
|
|
40
88
|
return 0
|
|
41
|
-
return sum(getattr(waiter,
|
|
42
|
-
|
|
43
|
-
|
|
89
|
+
return sum(getattr(waiter, "num_waiters", 1) or 1 for waiter in self._waiters)
|
|
90
|
+
|
|
91
|
+
def _waiter_done_cleanup_callback(
|
|
92
|
+
self: Union["SmartFuture", "SmartTask"], waiter: "SmartTask"
|
|
93
|
+
) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Callback to clean up waiters when a waiter task is done.
|
|
96
|
+
|
|
97
|
+
Removes the waiter from _waiters, and _queue._futs if applicable
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
waiter: The waiter task to clean up.
|
|
101
|
+
"""
|
|
44
102
|
if not self.done():
|
|
45
103
|
self._waiters.remove(waiter)
|
|
104
|
+
|
|
46
105
|
def _self_done_cleanup_callback(self: Union["SmartFuture", "SmartTask"]) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Callback to clean up waiters and remove the future from the queue when done.
|
|
108
|
+
"""
|
|
47
109
|
self._waiters.clear()
|
|
48
110
|
if queue := self._queue:
|
|
49
111
|
queue._futs.pop(self._key)
|
|
50
112
|
|
|
51
113
|
|
|
52
114
|
class SmartFuture(_SmartFutureMixin[T], asyncio.Future):
|
|
115
|
+
"""
|
|
116
|
+
A smart future that tracks waiters and integrates with a smart processing queue.
|
|
117
|
+
|
|
118
|
+
Inherits from both :class:`_SmartFutureMixin` and :class:`asyncio.Future`, providing additional functionality
|
|
119
|
+
for tracking waiters and integrating with a smart processing queue.
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
Creating and awaiting a SmartFuture:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
future = SmartFuture()
|
|
126
|
+
await future
|
|
127
|
+
```
|
|
128
|
+
"""
|
|
129
|
+
|
|
53
130
|
_queue = None
|
|
54
131
|
_key = None
|
|
132
|
+
|
|
55
133
|
def __init__(
|
|
56
|
-
self,
|
|
57
|
-
*,
|
|
58
|
-
queue: Optional["SmartProcessingQueue[Any, Any, T]"],
|
|
59
|
-
key: Optional[_Key] = None,
|
|
134
|
+
self,
|
|
135
|
+
*,
|
|
136
|
+
queue: Optional["SmartProcessingQueue[Any, Any, T]"],
|
|
137
|
+
key: Optional[_Key] = None,
|
|
60
138
|
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
61
139
|
) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Initialize the SmartFuture with an optional queue and key.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
queue: Optional; a smart processing queue.
|
|
145
|
+
key: Optional; a key identifying the future.
|
|
146
|
+
loop: Optional; the event loop.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
```python
|
|
150
|
+
future = SmartFuture(queue=my_queue, key=my_key)
|
|
151
|
+
```
|
|
152
|
+
"""
|
|
62
153
|
super().__init__(loop=loop)
|
|
63
154
|
if queue:
|
|
64
155
|
self._queue = weakref.proxy(queue)
|
|
@@ -66,63 +157,150 @@ class SmartFuture(_SmartFutureMixin[T], asyncio.Future):
|
|
|
66
157
|
self._key = key
|
|
67
158
|
self._waiters = weakref.WeakSet()
|
|
68
159
|
self.add_done_callback(SmartFuture._self_done_cleanup_callback)
|
|
160
|
+
|
|
69
161
|
def __repr__(self):
|
|
70
162
|
return f"<{type(self).__name__} key={self._key} waiters={self.num_waiters} {self._state}>"
|
|
163
|
+
|
|
71
164
|
def __lt__(self, other: "SmartFuture[T]") -> bool:
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
165
|
+
"""
|
|
166
|
+
Compare the number of waiters to determine priority in a heap.
|
|
167
|
+
Lower values indicate higher priority, so more waiters means 'less than'.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
other: Another SmartFuture to compare with.
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
```python
|
|
174
|
+
future1 = SmartFuture()
|
|
175
|
+
future2 = SmartFuture()
|
|
176
|
+
print(future1 < future2)
|
|
177
|
+
```
|
|
178
|
+
"""
|
|
77
179
|
return self.num_waiters > other.num_waiters
|
|
78
180
|
|
|
181
|
+
|
|
79
182
|
def create_future(
|
|
80
183
|
*,
|
|
81
|
-
queue: Optional["SmartProcessingQueue"] = None,
|
|
82
|
-
key: Optional[_Key] = None,
|
|
184
|
+
queue: Optional["SmartProcessingQueue"] = None,
|
|
185
|
+
key: Optional[_Key] = None,
|
|
83
186
|
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
84
187
|
) -> SmartFuture[V]:
|
|
188
|
+
"""
|
|
189
|
+
Create a :class:`~SmartFuture` instance.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
queue: Optional; a smart processing queue.
|
|
193
|
+
key: Optional; a key identifying the future.
|
|
194
|
+
loop: Optional; the event loop.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
A SmartFuture instance.
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
Creating a SmartFuture using the factory function:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
future = create_future(queue=my_queue, key=my_key)
|
|
204
|
+
```
|
|
205
|
+
"""
|
|
85
206
|
return SmartFuture(queue=queue, key=key, loop=loop or asyncio.get_event_loop())
|
|
86
207
|
|
|
208
|
+
|
|
87
209
|
class SmartTask(_SmartFutureMixin[T], asyncio.Task):
|
|
210
|
+
"""
|
|
211
|
+
A smart task that tracks waiters and integrates with a smart processing queue.
|
|
212
|
+
|
|
213
|
+
Inherits from both :class:`_SmartFutureMixin` and :class:`asyncio.Task`, providing additional functionality
|
|
214
|
+
for tracking waiters and integrating with a smart processing queue.
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
Creating and awaiting a SmartTask:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
task = SmartTask(coro=my_coroutine())
|
|
221
|
+
await task
|
|
222
|
+
```
|
|
223
|
+
"""
|
|
224
|
+
|
|
88
225
|
def __init__(
|
|
89
|
-
self,
|
|
90
|
-
coro: Awaitable[T],
|
|
91
|
-
*,
|
|
92
|
-
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
226
|
+
self,
|
|
227
|
+
coro: Awaitable[T],
|
|
228
|
+
*,
|
|
229
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
93
230
|
name: Optional[str] = None,
|
|
94
231
|
) -> None:
|
|
232
|
+
"""
|
|
233
|
+
Initialize the SmartTask with a coroutine and optional event loop.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
coro: The coroutine to run in the task.
|
|
237
|
+
loop: Optional; the event loop.
|
|
238
|
+
name: Optional; the name of the task.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
```python
|
|
242
|
+
task = SmartTask(coro=my_coroutine(), name="my_task")
|
|
243
|
+
```
|
|
244
|
+
"""
|
|
95
245
|
super().__init__(coro, loop=loop, name=name)
|
|
96
246
|
self._waiters: Set["asyncio.Task[T]"] = set()
|
|
97
247
|
self.add_done_callback(SmartTask._self_done_cleanup_callback)
|
|
98
248
|
|
|
99
|
-
|
|
249
|
+
|
|
250
|
+
def smart_task_factory(
|
|
251
|
+
loop: asyncio.AbstractEventLoop, coro: Awaitable[T]
|
|
252
|
+
) -> SmartTask[T]:
|
|
100
253
|
"""
|
|
101
254
|
Task factory function that an event loop calls to create new tasks.
|
|
102
|
-
|
|
255
|
+
|
|
103
256
|
This factory function utilizes ez-a-sync's custom :class:`~SmartTask` implementation.
|
|
104
|
-
|
|
257
|
+
|
|
105
258
|
Args:
|
|
106
259
|
loop: The event loop.
|
|
107
260
|
coro: The coroutine to run in the task.
|
|
108
|
-
|
|
261
|
+
|
|
109
262
|
Returns:
|
|
110
263
|
A SmartTask instance running the provided coroutine.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
Using the smart task factory to create a SmartTask:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
loop = asyncio.get_event_loop()
|
|
270
|
+
task = smart_task_factory(loop, my_coroutine())
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
See Also:
|
|
274
|
+
- :func:`set_smart_task_factory`
|
|
111
275
|
"""
|
|
112
276
|
return SmartTask(coro, loop=loop)
|
|
113
277
|
|
|
278
|
+
|
|
114
279
|
def set_smart_task_factory(loop: asyncio.AbstractEventLoop = None) -> None:
|
|
115
280
|
"""
|
|
116
281
|
Set the event loop's task factory to :func:`~smart_task_factory` so all tasks will be SmartTask instances.
|
|
117
|
-
|
|
282
|
+
|
|
118
283
|
Args:
|
|
119
284
|
loop: Optional; the event loop. If None, the current event loop is used.
|
|
285
|
+
|
|
286
|
+
Example:
|
|
287
|
+
Setting the smart task factory for the current event loop:
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
set_smart_task_factory()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
See Also:
|
|
294
|
+
- :func:`smart_task_factory`
|
|
120
295
|
"""
|
|
121
296
|
if loop is None:
|
|
122
297
|
loop = a_sync.asyncio.get_event_loop()
|
|
123
298
|
loop.set_task_factory(smart_task_factory)
|
|
124
299
|
|
|
125
|
-
|
|
300
|
+
|
|
301
|
+
def shield(
|
|
302
|
+
arg: Awaitable[T], *, loop: Optional[asyncio.AbstractEventLoop] = None
|
|
303
|
+
) -> SmartFuture[T]:
|
|
126
304
|
"""
|
|
127
305
|
Wait for a future, shielding it from cancellation.
|
|
128
306
|
|
|
@@ -148,11 +326,28 @@ def shield(arg: Awaitable[T], *, loop: Optional[asyncio.AbstractEventLoop] = Non
|
|
|
148
326
|
res = await shield(something())
|
|
149
327
|
except CancelledError:
|
|
150
328
|
res = None
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
arg: The awaitable to shield from cancellation.
|
|
332
|
+
loop: Optional; the event loop. Deprecated since Python 3.8.
|
|
333
|
+
|
|
334
|
+
Example:
|
|
335
|
+
Using shield to protect a coroutine from cancellation:
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
result = await shield(my_coroutine())
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
See Also:
|
|
342
|
+
- :func:`asyncio.shield`
|
|
151
343
|
"""
|
|
152
344
|
if loop is not None:
|
|
153
|
-
warnings.warn(
|
|
154
|
-
|
|
155
|
-
|
|
345
|
+
warnings.warn(
|
|
346
|
+
"The loop argument is deprecated since Python 3.8, "
|
|
347
|
+
"and scheduled for removal in Python 3.10.",
|
|
348
|
+
DeprecationWarning,
|
|
349
|
+
stacklevel=2,
|
|
350
|
+
)
|
|
156
351
|
inner = asyncio.ensure_future(arg, loop=loop)
|
|
157
352
|
if inner.done():
|
|
158
353
|
# Shortcut.
|
|
@@ -162,6 +357,7 @@ def shield(arg: Awaitable[T], *, loop: Optional[asyncio.AbstractEventLoop] = Non
|
|
|
162
357
|
# special handling to connect SmartFutures to SmartTasks if enabled
|
|
163
358
|
if (waiters := getattr(inner, "_waiters", None)) is not None:
|
|
164
359
|
waiters.add(outer)
|
|
360
|
+
|
|
165
361
|
def _inner_done_callback(inner):
|
|
166
362
|
if outer.cancelled():
|
|
167
363
|
if not inner.cancelled():
|
|
@@ -187,4 +383,11 @@ def shield(arg: Awaitable[T], *, loop: Optional[asyncio.AbstractEventLoop] = Non
|
|
|
187
383
|
return outer
|
|
188
384
|
|
|
189
385
|
|
|
190
|
-
__all__ = [
|
|
386
|
+
__all__ = [
|
|
387
|
+
"create_future",
|
|
388
|
+
"shield",
|
|
389
|
+
"SmartFuture",
|
|
390
|
+
"SmartTask",
|
|
391
|
+
"smart_task_factory",
|
|
392
|
+
"set_smart_task_factory",
|
|
393
|
+
]
|