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.

Files changed (73) hide show
  1. a_sync/ENVIRONMENT_VARIABLES.py +37 -5
  2. a_sync/__init__.py +53 -12
  3. a_sync/_smart.py +231 -28
  4. a_sync/_typing.py +112 -15
  5. a_sync/a_sync/__init__.py +35 -10
  6. a_sync/a_sync/_descriptor.py +248 -38
  7. a_sync/a_sync/_flags.py +78 -9
  8. a_sync/a_sync/_helpers.py +46 -13
  9. a_sync/a_sync/_kwargs.py +33 -8
  10. a_sync/a_sync/_meta.py +149 -28
  11. a_sync/a_sync/abstract.py +150 -28
  12. a_sync/a_sync/base.py +34 -16
  13. a_sync/a_sync/config.py +85 -14
  14. a_sync/a_sync/decorator.py +441 -139
  15. a_sync/a_sync/function.py +709 -147
  16. a_sync/a_sync/method.py +437 -110
  17. a_sync/a_sync/modifiers/__init__.py +85 -5
  18. a_sync/a_sync/modifiers/cache/__init__.py +116 -17
  19. a_sync/a_sync/modifiers/cache/memory.py +130 -20
  20. a_sync/a_sync/modifiers/limiter.py +101 -22
  21. a_sync/a_sync/modifiers/manager.py +142 -16
  22. a_sync/a_sync/modifiers/semaphores.py +121 -15
  23. a_sync/a_sync/property.py +383 -82
  24. a_sync/a_sync/singleton.py +44 -19
  25. a_sync/aliases.py +0 -1
  26. a_sync/asyncio/__init__.py +140 -1
  27. a_sync/asyncio/as_completed.py +213 -79
  28. a_sync/asyncio/create_task.py +70 -20
  29. a_sync/asyncio/gather.py +125 -58
  30. a_sync/asyncio/utils.py +3 -3
  31. a_sync/exceptions.py +248 -26
  32. a_sync/executor.py +164 -69
  33. a_sync/future.py +1227 -168
  34. a_sync/iter.py +173 -56
  35. a_sync/primitives/__init__.py +14 -2
  36. a_sync/primitives/_debug.py +72 -18
  37. a_sync/primitives/_loggable.py +41 -10
  38. a_sync/primitives/locks/__init__.py +5 -2
  39. a_sync/primitives/locks/counter.py +107 -38
  40. a_sync/primitives/locks/event.py +21 -7
  41. a_sync/primitives/locks/prio_semaphore.py +262 -63
  42. a_sync/primitives/locks/semaphore.py +138 -89
  43. a_sync/primitives/queue.py +601 -60
  44. a_sync/sphinx/__init__.py +0 -1
  45. a_sync/sphinx/ext.py +160 -50
  46. a_sync/task.py +313 -112
  47. a_sync/utils/__init__.py +12 -6
  48. a_sync/utils/iterators.py +170 -50
  49. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
  50. ez_a_sync-0.22.16.dist-info/RECORD +74 -0
  51. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
  52. tests/conftest.py +1 -2
  53. tests/executor.py +250 -9
  54. tests/fixtures.py +61 -32
  55. tests/test_abstract.py +22 -4
  56. tests/test_as_completed.py +54 -21
  57. tests/test_base.py +264 -19
  58. tests/test_cache.py +31 -15
  59. tests/test_decorator.py +54 -28
  60. tests/test_executor.py +31 -13
  61. tests/test_future.py +45 -8
  62. tests/test_gather.py +8 -2
  63. tests/test_helpers.py +2 -0
  64. tests/test_iter.py +55 -13
  65. tests/test_limiter.py +5 -3
  66. tests/test_meta.py +23 -9
  67. tests/test_modified.py +4 -1
  68. tests/test_semaphore.py +15 -8
  69. tests/test_singleton.py +28 -11
  70. tests/test_task.py +162 -36
  71. ez_a_sync-0.22.14.dist-info/RECORD +0 -74
  72. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
  73. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
@@ -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
- # If you're only interested in debugging a specific class, set this to the class name
9
- DEBUG_CLASS_NAME = envs.create_env("DEBUG_CLASS_NAME", str, default='', verbose=False)
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
- # Set this to enable debug mode on all classes
12
- DEBUG_MODE = envs.create_env("DEBUG_MODE", bool, default=DEBUG_CLASS_NAME, verbose=False)
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 ASyncCachedPropertyDescriptor, ASyncPropertyDescriptor, cached_property, property
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, 'num_waiters', 1) or 1 for waiter in self._waiters)
42
- def _waiter_done_cleanup_callback(self: Union["SmartFuture", "SmartTask"], waiter: "SmartTask") -> None:
43
- "Removes the waiter from _waiters, and _queue._futs if applicable"
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
- """heap considers lower values as higher priority so a future with more waiters will be 'less than' a future with less waiters."""
73
- #other = other_ref()
74
- #if other is None:
75
- # # garbage collected refs should always process first so they can be popped from the queue
76
- # return False
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
- def smart_task_factory(loop: asyncio.AbstractEventLoop, coro: Awaitable[T]) -> SmartTask[T]:
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
- def shield(arg: Awaitable[T], *, loop: Optional[asyncio.AbstractEventLoop] = None) -> SmartFuture[T]:
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("The loop argument is deprecated since Python 3.8, "
154
- "and scheduled for removal in Python 3.10.",
155
- DeprecationWarning, stacklevel=2)
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__ = ["create_future", "shield", "SmartFuture", "SmartTask", "smart_task_factory", "set_smart_task_factory"]
386
+ __all__ = [
387
+ "create_future",
388
+ "shield",
389
+ "SmartFuture",
390
+ "SmartTask",
391
+ "smart_task_factory",
392
+ "set_smart_task_factory",
393
+ ]