ez-a-sync 0.33.4__cp313-cp313-musllinux_1_2_i686.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.
- a_sync/ENVIRONMENT_VARIABLES.py +42 -0
- a_sync/__init__.pxd +2 -0
- a_sync/__init__.py +145 -0
- a_sync/_smart.c +22830 -0
- a_sync/_smart.cpython-313-i386-linux-musl.so +0 -0
- a_sync/_smart.pxd +2 -0
- a_sync/_smart.pyi +202 -0
- a_sync/_smart.pyx +674 -0
- a_sync/_typing.py +258 -0
- a_sync/a_sync/__init__.py +60 -0
- a_sync/a_sync/_descriptor.c +20537 -0
- a_sync/a_sync/_descriptor.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_descriptor.pyi +33 -0
- a_sync/a_sync/_descriptor.pyx +422 -0
- a_sync/a_sync/_flags.c +6082 -0
- a_sync/a_sync/_flags.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_flags.pxd +3 -0
- a_sync/a_sync/_flags.pyx +92 -0
- a_sync/a_sync/_helpers.c +14529 -0
- a_sync/a_sync/_helpers.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_helpers.pxd +3 -0
- a_sync/a_sync/_helpers.pyi +10 -0
- a_sync/a_sync/_helpers.pyx +167 -0
- a_sync/a_sync/_kwargs.c +12202 -0
- a_sync/a_sync/_kwargs.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/_kwargs.pxd +2 -0
- a_sync/a_sync/_kwargs.pyx +64 -0
- a_sync/a_sync/_meta.py +210 -0
- a_sync/a_sync/abstract.c +12420 -0
- a_sync/a_sync/abstract.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/abstract.pyi +141 -0
- a_sync/a_sync/abstract.pyx +221 -0
- a_sync/a_sync/base.c +14940 -0
- a_sync/a_sync/base.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/base.pyi +60 -0
- a_sync/a_sync/base.pyx +271 -0
- a_sync/a_sync/config.py +168 -0
- a_sync/a_sync/decorator.py +651 -0
- a_sync/a_sync/flags.c +5272 -0
- a_sync/a_sync/flags.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/flags.pxd +72 -0
- a_sync/a_sync/flags.pyi +74 -0
- a_sync/a_sync/flags.pyx +72 -0
- a_sync/a_sync/function.c +37856 -0
- a_sync/a_sync/function.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/function.pxd +28 -0
- a_sync/a_sync/function.pyi +571 -0
- a_sync/a_sync/function.pyx +1381 -0
- a_sync/a_sync/method.c +29662 -0
- a_sync/a_sync/method.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/method.pxd +9 -0
- a_sync/a_sync/method.pyi +523 -0
- a_sync/a_sync/method.pyx +1023 -0
- a_sync/a_sync/modifiers/__init__.pxd +1 -0
- a_sync/a_sync/modifiers/__init__.py +101 -0
- a_sync/a_sync/modifiers/cache/__init__.py +160 -0
- a_sync/a_sync/modifiers/cache/memory.py +165 -0
- a_sync/a_sync/modifiers/limiter.py +132 -0
- a_sync/a_sync/modifiers/manager.c +16157 -0
- a_sync/a_sync/modifiers/manager.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/modifiers/manager.pxd +5 -0
- a_sync/a_sync/modifiers/manager.pyi +219 -0
- a_sync/a_sync/modifiers/manager.pyx +299 -0
- a_sync/a_sync/modifiers/semaphores.py +173 -0
- a_sync/a_sync/property.c +27268 -0
- a_sync/a_sync/property.cpython-313-i386-linux-musl.so +0 -0
- a_sync/a_sync/property.pyi +376 -0
- a_sync/a_sync/property.pyx +819 -0
- a_sync/a_sync/singleton.py +63 -0
- a_sync/aliases.py +3 -0
- a_sync/async_property/__init__.pxd +1 -0
- a_sync/async_property/__init__.py +1 -0
- a_sync/async_property/cached.c +20397 -0
- a_sync/async_property/cached.cpython-313-i386-linux-musl.so +0 -0
- a_sync/async_property/cached.pxd +10 -0
- a_sync/async_property/cached.pyi +45 -0
- a_sync/async_property/cached.pyx +178 -0
- a_sync/async_property/proxy.c +34662 -0
- a_sync/async_property/proxy.cpython-313-i386-linux-musl.so +0 -0
- a_sync/async_property/proxy.pxd +2 -0
- a_sync/async_property/proxy.pyi +124 -0
- a_sync/async_property/proxy.pyx +474 -0
- a_sync/asyncio/__init__.pxd +6 -0
- a_sync/asyncio/__init__.py +164 -0
- a_sync/asyncio/as_completed.c +18849 -0
- a_sync/asyncio/as_completed.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/as_completed.pxd +8 -0
- a_sync/asyncio/as_completed.pyi +109 -0
- a_sync/asyncio/as_completed.pyx +269 -0
- a_sync/asyncio/create_task.c +15912 -0
- a_sync/asyncio/create_task.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/create_task.pxd +2 -0
- a_sync/asyncio/create_task.pyi +51 -0
- a_sync/asyncio/create_task.pyx +271 -0
- a_sync/asyncio/gather.c +16687 -0
- a_sync/asyncio/gather.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/gather.pyi +107 -0
- a_sync/asyncio/gather.pyx +218 -0
- a_sync/asyncio/igather.c +13080 -0
- a_sync/asyncio/igather.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/igather.pxd +1 -0
- a_sync/asyncio/igather.pyi +8 -0
- a_sync/asyncio/igather.pyx +183 -0
- a_sync/asyncio/sleep.c +9601 -0
- a_sync/asyncio/sleep.cpython-313-i386-linux-musl.so +0 -0
- a_sync/asyncio/sleep.pyi +14 -0
- a_sync/asyncio/sleep.pyx +49 -0
- a_sync/debugging.c +15370 -0
- a_sync/debugging.cpython-313-i386-linux-musl.so +0 -0
- a_sync/debugging.pyi +76 -0
- a_sync/debugging.pyx +107 -0
- a_sync/exceptions.c +13320 -0
- a_sync/exceptions.cpython-313-i386-linux-musl.so +0 -0
- a_sync/exceptions.pyi +376 -0
- a_sync/exceptions.pyx +446 -0
- a_sync/executor.py +619 -0
- a_sync/functools.c +12746 -0
- a_sync/functools.cpython-313-i386-linux-musl.so +0 -0
- a_sync/functools.pxd +7 -0
- a_sync/functools.pyi +33 -0
- a_sync/functools.pyx +139 -0
- a_sync/future.py +1497 -0
- a_sync/iter.c +37279 -0
- a_sync/iter.cpython-313-i386-linux-musl.so +0 -0
- a_sync/iter.pxd +11 -0
- a_sync/iter.pyi +370 -0
- a_sync/iter.pyx +981 -0
- a_sync/primitives/__init__.pxd +1 -0
- a_sync/primitives/__init__.py +53 -0
- a_sync/primitives/_debug.c +15765 -0
- a_sync/primitives/_debug.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/_debug.pxd +12 -0
- a_sync/primitives/_debug.pyi +52 -0
- a_sync/primitives/_debug.pyx +223 -0
- a_sync/primitives/_loggable.c +11538 -0
- a_sync/primitives/_loggable.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/_loggable.pxd +4 -0
- a_sync/primitives/_loggable.pyi +66 -0
- a_sync/primitives/_loggable.pyx +102 -0
- a_sync/primitives/locks/__init__.pxd +8 -0
- a_sync/primitives/locks/__init__.py +17 -0
- a_sync/primitives/locks/counter.c +17938 -0
- a_sync/primitives/locks/counter.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/counter.pxd +12 -0
- a_sync/primitives/locks/counter.pyi +151 -0
- a_sync/primitives/locks/counter.pyx +267 -0
- a_sync/primitives/locks/event.c +17072 -0
- a_sync/primitives/locks/event.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/event.pxd +22 -0
- a_sync/primitives/locks/event.pyi +43 -0
- a_sync/primitives/locks/event.pyx +185 -0
- a_sync/primitives/locks/prio_semaphore.c +25635 -0
- a_sync/primitives/locks/prio_semaphore.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/prio_semaphore.pxd +25 -0
- a_sync/primitives/locks/prio_semaphore.pyi +217 -0
- a_sync/primitives/locks/prio_semaphore.pyx +597 -0
- a_sync/primitives/locks/semaphore.c +26553 -0
- a_sync/primitives/locks/semaphore.cpython-313-i386-linux-musl.so +0 -0
- a_sync/primitives/locks/semaphore.pxd +21 -0
- a_sync/primitives/locks/semaphore.pyi +197 -0
- a_sync/primitives/locks/semaphore.pyx +454 -0
- a_sync/primitives/queue.py +1026 -0
- a_sync/py.typed +0 -0
- a_sync/sphinx/__init__.py +3 -0
- a_sync/sphinx/ext.py +289 -0
- a_sync/task.py +934 -0
- a_sync/utils/__init__.py +105 -0
- a_sync/utils/iterators.py +297 -0
- a_sync/utils/repr.c +15866 -0
- a_sync/utils/repr.cpython-313-i386-linux-musl.so +0 -0
- a_sync/utils/repr.pyi +2 -0
- a_sync/utils/repr.pyx +73 -0
- ez_a_sync-0.33.4.dist-info/METADATA +368 -0
- ez_a_sync-0.33.4.dist-info/RECORD +177 -0
- ez_a_sync-0.33.4.dist-info/WHEEL +5 -0
- ez_a_sync-0.33.4.dist-info/licenses/LICENSE.txt +17 -0
- ez_a_sync-0.33.4.dist-info/top_level.txt +1 -0
a_sync/a_sync/method.pyx
ADDED
|
@@ -0,0 +1,1023 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides classes for implementing dual-functional sync/async methods in Python.
|
|
3
|
+
|
|
4
|
+
It includes descriptors and bound methods that can be used to create flexible
|
|
5
|
+
asynchronous interfaces, allowing methods to be called both synchronously and
|
|
6
|
+
asynchronously based on various conditions and configurations.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# mypy: disable-error-code=valid-type
|
|
10
|
+
# mypy: disable-error-code=misc
|
|
11
|
+
import asyncio
|
|
12
|
+
import inspect
|
|
13
|
+
import typing
|
|
14
|
+
from cpython.object cimport PyObject
|
|
15
|
+
from cpython.ref cimport Py_DECREF, Py_INCREF
|
|
16
|
+
from libc.stdint cimport uintptr_t
|
|
17
|
+
from logging import getLogger
|
|
18
|
+
|
|
19
|
+
import typing_extensions
|
|
20
|
+
|
|
21
|
+
from a_sync._typing import AnyFn, AnyIterable, I, MaybeCoro, ModifierKwargs, P, T
|
|
22
|
+
from a_sync.a_sync import _descriptor, function
|
|
23
|
+
from a_sync.a_sync._kwargs cimport get_flag_name, is_sync
|
|
24
|
+
from a_sync.a_sync._helpers cimport _await
|
|
25
|
+
from a_sync.a_sync.function cimport _ASyncFunction, _ModifiedMixin
|
|
26
|
+
from a_sync.functools cimport update_wrapper
|
|
27
|
+
|
|
28
|
+
cdef extern from "weakrefobject.h":
|
|
29
|
+
PyObject* PyWeakref_NewRef(PyObject*, PyObject*)
|
|
30
|
+
|
|
31
|
+
cdef extern from "pythoncapi_compat.h":
|
|
32
|
+
int PyWeakref_GetRef(PyObject*, PyObject**)
|
|
33
|
+
|
|
34
|
+
if typing.TYPE_CHECKING:
|
|
35
|
+
from a_sync import TaskMapping
|
|
36
|
+
from a_sync.a_sync.abstract import ASyncABC
|
|
37
|
+
|
|
38
|
+
else:
|
|
39
|
+
# Due to circ import issues we will populate these later
|
|
40
|
+
ASyncABC, TaskMapping = None, None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# cdef asyncio
|
|
44
|
+
cdef object get_event_loop = asyncio.get_event_loop
|
|
45
|
+
cdef object iscoroutinefunction = asyncio.iscoroutinefunction
|
|
46
|
+
cdef object cancel_handle = asyncio.TimerHandle.cancel
|
|
47
|
+
|
|
48
|
+
# cdef inspect
|
|
49
|
+
cdef object isawaitable = inspect.isawaitable
|
|
50
|
+
|
|
51
|
+
# cdef typing
|
|
52
|
+
cdef object Any = typing.Any
|
|
53
|
+
cdef object Coroutine = typing.Coroutine
|
|
54
|
+
cdef object Generic = typing.Generic
|
|
55
|
+
cdef object Literal = typing.Literal
|
|
56
|
+
cdef object Optional = typing.Optional
|
|
57
|
+
cdef object Type = typing.Type
|
|
58
|
+
cdef object Union = typing.Union
|
|
59
|
+
cdef object overload = typing.overload
|
|
60
|
+
|
|
61
|
+
# cdef typing_extensions
|
|
62
|
+
cdef object Concatenate = typing_extensions.Concatenate
|
|
63
|
+
cdef object Self = typing_extensions.Self
|
|
64
|
+
cdef object Unpack = typing_extensions.Unpack
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# cdef a_sync
|
|
68
|
+
cdef object ASyncDescriptor = _descriptor.ASyncDescriptor
|
|
69
|
+
cdef object ASyncFunctionAsyncDefault = function.ASyncFunctionAsyncDefault
|
|
70
|
+
cdef object ASyncFunctionSyncDefault = function.ASyncFunctionSyncDefault
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
cdef public double METHOD_CACHE_TTL = 3600
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
logger = getLogger(__name__)
|
|
77
|
+
|
|
78
|
+
cdef object _logger_is_enabled = logger.isEnabledFor
|
|
79
|
+
cdef object _logger_log = logger._log
|
|
80
|
+
cdef object DEBUG = 10
|
|
81
|
+
|
|
82
|
+
cdef inline void _logger_debug(str msg, tuple args):
|
|
83
|
+
if _logger_is_enabled(DEBUG):
|
|
84
|
+
_logger_log(DEBUG, msg, args)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
|
|
88
|
+
"""
|
|
89
|
+
A descriptor for managing methods that can be called both synchronously and asynchronously.
|
|
90
|
+
|
|
91
|
+
This class provides the core functionality for binding methods to instances and determining
|
|
92
|
+
the execution mode ("sync" or "async") based on various conditions, such as the instance type,
|
|
93
|
+
the method's default setting, or specific flags passed during the method call.
|
|
94
|
+
|
|
95
|
+
The descriptor is responsible for creating an appropriate bound method when accessed,
|
|
96
|
+
which is the actual object that can be called in both synchronous and asynchronous contexts.
|
|
97
|
+
It can create different types of bound methods (`ASyncBoundMethodSyncDefault`,
|
|
98
|
+
`ASyncBoundMethodAsyncDefault`, or `ASyncBoundMethod`) based on the default mode or instance type.
|
|
99
|
+
|
|
100
|
+
If the default mode is explicitly set to "sync" or "async", it creates `ASyncBoundMethodSyncDefault`
|
|
101
|
+
or `ASyncBoundMethodAsyncDefault` respectively. If neither is set, it defaults to creating an
|
|
102
|
+
`ASyncBoundMethod`. For instances of :class:`ASyncABC`, it checks the `__a_sync_instance_should_await__`
|
|
103
|
+
attribute to decide the type of bound method to create.
|
|
104
|
+
|
|
105
|
+
It also manages cache handles for bound methods and prevents setting or deleting the descriptor.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
>>> class MyClass:
|
|
109
|
+
... @ASyncMethodDescriptor
|
|
110
|
+
... async def my_method(self):
|
|
111
|
+
... return "Hello, World!"
|
|
112
|
+
...
|
|
113
|
+
>>> obj = MyClass()
|
|
114
|
+
>>> await obj.my_method()
|
|
115
|
+
'Hello, World!'
|
|
116
|
+
>>> obj.my_method(sync=True)
|
|
117
|
+
'Hello, World!'
|
|
118
|
+
|
|
119
|
+
See Also:
|
|
120
|
+
- :class:`ASyncBoundMethod`
|
|
121
|
+
- :class:`ASyncFunction`
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
__wrapped__: AnyFn[P, T]
|
|
125
|
+
"""The unbound function which will be bound to an instance when :meth:`__get__` is called."""
|
|
126
|
+
|
|
127
|
+
_initialized = False
|
|
128
|
+
|
|
129
|
+
async def __call__(self, instance: I, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
130
|
+
"""
|
|
131
|
+
Asynchronously call the method.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
instance: The instance the method is bound to.
|
|
135
|
+
*args: Positional arguments.
|
|
136
|
+
**kwargs: Keyword arguments.
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
>>> descriptor = ASyncMethodDescriptor(my_async_function)
|
|
140
|
+
>>> await descriptor(instance, arg1, arg2, kwarg1=value1)
|
|
141
|
+
"""
|
|
142
|
+
# NOTE: This is only used by TaskMapping atm # TODO: use it elsewhere
|
|
143
|
+
_logger_debug(
|
|
144
|
+
"awaiting %s for instance: %s args: %s kwargs: %s",
|
|
145
|
+
(self, instance, args, kwargs),
|
|
146
|
+
)
|
|
147
|
+
return await self.__get__(instance, None)(*args, **kwargs)
|
|
148
|
+
|
|
149
|
+
@overload
|
|
150
|
+
def __get__(self, instance: None, owner: Type[I]) -> Self: ...
|
|
151
|
+
@overload
|
|
152
|
+
def __get__(self, instance: I, owner: Type[I]) -> "ASyncBoundMethod[I, P, T]": ...
|
|
153
|
+
def __get__(
|
|
154
|
+
_ModifiedMixin self, instance: Optional[I], owner: Type[I]
|
|
155
|
+
) -> Union[Self, "ASyncBoundMethod[I, P, T]"]:
|
|
156
|
+
"""
|
|
157
|
+
Get the bound method or the descriptor itself.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
instance: The instance to bind the method to, or None.
|
|
161
|
+
owner: The owner class.
|
|
162
|
+
|
|
163
|
+
Examples:
|
|
164
|
+
>>> descriptor = ASyncMethodDescriptor(my_function)
|
|
165
|
+
>>> bound_method = descriptor.__get__(instance, MyClass)
|
|
166
|
+
"""
|
|
167
|
+
if instance is None:
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
cdef str field_name = self.field_name
|
|
171
|
+
cdef dict instance_dict = instance.__dict__
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
bound = instance_dict[field_name]
|
|
175
|
+
except KeyError:
|
|
176
|
+
if ASyncABC is None:
|
|
177
|
+
_import_ASyncABC()
|
|
178
|
+
|
|
179
|
+
default = _ModifiedMixin.get_default(self)
|
|
180
|
+
if default == "sync":
|
|
181
|
+
bound = ASyncBoundMethodSyncDefault(
|
|
182
|
+
instance, self.__wrapped__, self.__is_async_def__, **self.modifiers._modifiers
|
|
183
|
+
)
|
|
184
|
+
elif default == "async":
|
|
185
|
+
bound = ASyncBoundMethodAsyncDefault(
|
|
186
|
+
instance, self.__wrapped__, self.__is_async_def__, **self.modifiers._modifiers
|
|
187
|
+
)
|
|
188
|
+
elif isinstance(instance, ASyncABC):
|
|
189
|
+
try:
|
|
190
|
+
if instance.__a_sync_instance_should_await__:
|
|
191
|
+
bound = ASyncBoundMethodSyncDefault(
|
|
192
|
+
instance,
|
|
193
|
+
self.__wrapped__,
|
|
194
|
+
self.__is_async_def__,
|
|
195
|
+
**self.modifiers._modifiers,
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
bound = ASyncBoundMethodAsyncDefault(
|
|
199
|
+
instance,
|
|
200
|
+
self.__wrapped__,
|
|
201
|
+
self.__is_async_def__,
|
|
202
|
+
**self.modifiers._modifiers,
|
|
203
|
+
)
|
|
204
|
+
except AttributeError:
|
|
205
|
+
bound = ASyncBoundMethod(
|
|
206
|
+
instance,
|
|
207
|
+
self.__wrapped__,
|
|
208
|
+
self.__is_async_def__,
|
|
209
|
+
**self.modifiers._modifiers,
|
|
210
|
+
)
|
|
211
|
+
else:
|
|
212
|
+
bound = ASyncBoundMethod(
|
|
213
|
+
instance, self.__wrapped__, self.__is_async_def__, **self.modifiers._modifiers
|
|
214
|
+
)
|
|
215
|
+
instance_dict[field_name] = bound
|
|
216
|
+
_logger_debug("new bound method: %s", (bound,))
|
|
217
|
+
_update_cache_timer(field_name, instance, bound)
|
|
218
|
+
return bound
|
|
219
|
+
|
|
220
|
+
def __set__(self, instance, value):
|
|
221
|
+
"""
|
|
222
|
+
Prevent setting the descriptor.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
instance: The instance.
|
|
226
|
+
value: The value to set.
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
RuntimeError: Always raised to prevent setting.
|
|
230
|
+
|
|
231
|
+
Examples:
|
|
232
|
+
>>> descriptor = ASyncMethodDescriptor(my_function)
|
|
233
|
+
>>> descriptor.__set__(instance, value)
|
|
234
|
+
RuntimeError: cannot set field_name, descriptor is what you get. sorry.
|
|
235
|
+
"""
|
|
236
|
+
raise RuntimeError(
|
|
237
|
+
f"cannot set {self.field_name}, {self} is what you get. sorry."
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def __delete__(self, instance):
|
|
241
|
+
"""
|
|
242
|
+
Prevent deleting the descriptor.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
instance: The instance.
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
RuntimeError: Always raised to prevent deletion.
|
|
249
|
+
|
|
250
|
+
Examples:
|
|
251
|
+
>>> descriptor = ASyncMethodDescriptor(my_function)
|
|
252
|
+
>>> descriptor.__delete__(instance)
|
|
253
|
+
RuntimeError: cannot delete field_name, you're stuck with descriptor forever. sorry.
|
|
254
|
+
"""
|
|
255
|
+
raise RuntimeError(
|
|
256
|
+
f"cannot delete {self.field_name}, you're stuck with {self} forever. sorry."
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def __is_async_def__(self) -> bint:
|
|
261
|
+
"""
|
|
262
|
+
Check if the wrapped function is a coroutine function.
|
|
263
|
+
|
|
264
|
+
Examples:
|
|
265
|
+
>>> descriptor = ASyncMethodDescriptor(my_function)
|
|
266
|
+
>>> descriptor.__is_async_def__
|
|
267
|
+
True
|
|
268
|
+
"""
|
|
269
|
+
if not self._initialized:
|
|
270
|
+
self._is_async_def = iscoroutinefunction(self.__wrapped__)
|
|
271
|
+
self._initialized = True
|
|
272
|
+
return self._is_async_def
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
cdef void _update_cache_timer(str field_name, object instance, _ASyncBoundMethod bound):
|
|
276
|
+
"""
|
|
277
|
+
Update the TTL for the cache handle for the instance.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
instance: The instance to create a cache handle for.
|
|
281
|
+
bound: The bound method we are caching.
|
|
282
|
+
"""
|
|
283
|
+
# Handler for popping unused bound methods from bound method cache
|
|
284
|
+
cdef object handle, loop
|
|
285
|
+
if handle := bound._cache_handle:
|
|
286
|
+
# update the timer handle
|
|
287
|
+
handle._when = <double>handle._loop.time() + METHOD_CACHE_TTL
|
|
288
|
+
else:
|
|
289
|
+
# create and assign the timer handle
|
|
290
|
+
loop = get_event_loop()
|
|
291
|
+
# NOTE: use `instance.__dict__.pop` instead of `delattr` so we don't create a strong ref to `instance`
|
|
292
|
+
bound._cache_handle = loop.call_at(<double>loop.time() + METHOD_CACHE_TTL, instance.__dict__.pop, field_name)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class ASyncMethodDescriptorSyncDefault(ASyncMethodDescriptor[I, P, T]):
|
|
296
|
+
"""
|
|
297
|
+
A descriptor for :class:`ASyncBoundMethodSyncDefault` objects.
|
|
298
|
+
|
|
299
|
+
This class extends :class:`ASyncMethodDescriptor` to provide a synchronous
|
|
300
|
+
default behavior for method calls. It specifically creates `ASyncBoundMethodSyncDefault`
|
|
301
|
+
instances, which are specialized versions of `ASyncBoundMethod` with synchronous default behavior.
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
>>> class MyClass:
|
|
305
|
+
... @ASyncMethodDescriptorSyncDefault
|
|
306
|
+
... def my_method(self):
|
|
307
|
+
... return "Hello, World!"
|
|
308
|
+
...
|
|
309
|
+
>>> obj = MyClass()
|
|
310
|
+
>>> obj.my_method()
|
|
311
|
+
'Hello, World!'
|
|
312
|
+
>>> coro = obj.my_method(sync=False)
|
|
313
|
+
>>> coro
|
|
314
|
+
<coroutine object MyClass.my_method at 0x7fb4f5fb49c0>
|
|
315
|
+
>>> await coro
|
|
316
|
+
'Hello, World!'
|
|
317
|
+
|
|
318
|
+
See Also:
|
|
319
|
+
- :class:`ASyncBoundMethodSyncDefault`
|
|
320
|
+
- :class:`ASyncFunctionSyncDefault`
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
default = "sync"
|
|
324
|
+
"""The default mode for this bound method. Always set to "sync"."""
|
|
325
|
+
|
|
326
|
+
any: ASyncFunctionSyncDefault[Concatenate[AnyIterable[I], P], bool]
|
|
327
|
+
"""Synchronous default version of the :meth:`~ASyncMethodDescriptor.any` method."""
|
|
328
|
+
|
|
329
|
+
all: ASyncFunctionSyncDefault[Concatenate[AnyIterable[I], P], bool]
|
|
330
|
+
"""Synchronous default version of the :meth:`~ASyncMethodDescriptor.all` method."""
|
|
331
|
+
|
|
332
|
+
min: ASyncFunctionSyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
333
|
+
"""Synchronous default version of the :meth:`~ASyncMethodDescriptor.min` method."""
|
|
334
|
+
|
|
335
|
+
max: ASyncFunctionSyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
336
|
+
"""Synchronous default version of the :meth:`~ASyncMethodDescriptor.max` method."""
|
|
337
|
+
|
|
338
|
+
sum: ASyncFunctionSyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
339
|
+
"""Synchronous default version of the :meth:`~ASyncMethodDescriptor.sum` method."""
|
|
340
|
+
|
|
341
|
+
@overload
|
|
342
|
+
def __get__(
|
|
343
|
+
self, instance: None, owner: Type[I]
|
|
344
|
+
) -> "ASyncMethodDescriptorSyncDefault[I, P, T]": ...
|
|
345
|
+
@overload
|
|
346
|
+
def __get__(
|
|
347
|
+
self, instance: I, owner: Type[I]
|
|
348
|
+
) -> "ASyncBoundMethodSyncDefault[I, P, T]": ...
|
|
349
|
+
def __get__(
|
|
350
|
+
_ModifiedMixin self, instance: Optional[I], owner: Type[I]
|
|
351
|
+
) -> (
|
|
352
|
+
"Union[ASyncMethodDescriptorSyncDefault, ASyncBoundMethodSyncDefault[I, P, T]]"
|
|
353
|
+
):
|
|
354
|
+
"""
|
|
355
|
+
Get the bound method or the descriptor itself.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
instance: The instance to bind the method to, or None.
|
|
359
|
+
owner: The owner class.
|
|
360
|
+
|
|
361
|
+
Examples:
|
|
362
|
+
>>> descriptor = ASyncMethodDescriptorSyncDefault(my_function)
|
|
363
|
+
>>> bound_method = descriptor.__get__(instance, MyClass)
|
|
364
|
+
"""
|
|
365
|
+
if instance is None:
|
|
366
|
+
return self
|
|
367
|
+
|
|
368
|
+
cdef str field_name = self.field_name
|
|
369
|
+
cdef dict instance_dict = instance.__dict__
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
bound = instance_dict[field_name]
|
|
373
|
+
except KeyError:
|
|
374
|
+
bound = ASyncBoundMethodSyncDefault(
|
|
375
|
+
instance, self.__wrapped__, self.__is_async_def__, **self.modifiers._modifiers
|
|
376
|
+
)
|
|
377
|
+
instance_dict[field_name] = bound
|
|
378
|
+
_logger_debug("new bound method: %s", (bound,))
|
|
379
|
+
_update_cache_timer(field_name, instance, bound)
|
|
380
|
+
return bound
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class ASyncMethodDescriptorAsyncDefault(ASyncMethodDescriptor[I, P, T]):
|
|
384
|
+
"""
|
|
385
|
+
A descriptor for asynchronous methods with an asynchronous default.
|
|
386
|
+
|
|
387
|
+
This class extends :class:`ASyncMethodDescriptor` to provide an asynchronous default
|
|
388
|
+
behavior for method calls. It specifically creates `ASyncBoundMethodAsyncDefault`
|
|
389
|
+
instances, which are specialized versions of `ASyncBoundMethod` with asynchronous default behavior.
|
|
390
|
+
|
|
391
|
+
Examples:
|
|
392
|
+
>>> class MyClass:
|
|
393
|
+
... @ASyncMethodDescriptorAsyncDefault
|
|
394
|
+
... async def my_method(self):
|
|
395
|
+
... return "Hello, World!"
|
|
396
|
+
...
|
|
397
|
+
>>> obj = MyClass()
|
|
398
|
+
>>> coro = obj.my_method()
|
|
399
|
+
>>> coro
|
|
400
|
+
<coroutine object MyClass.my_method at 0x7fb4f5fb49c0>
|
|
401
|
+
>>> await coro
|
|
402
|
+
>>> obj.my_method(sync=True)
|
|
403
|
+
'Hello, World!'
|
|
404
|
+
|
|
405
|
+
See Also:
|
|
406
|
+
- :class:`ASyncBoundMethodAsyncDefault`
|
|
407
|
+
- :class:`ASyncFunctionAsyncDefault`
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
default = "async"
|
|
411
|
+
"""The default mode for this bound method. Always set to "async"."""
|
|
412
|
+
|
|
413
|
+
any: ASyncFunctionAsyncDefault[Concatenate[AnyIterable[I], P], bool]
|
|
414
|
+
"""Asynchronous default version of the :meth:`~ASyncMethodDescriptor.any` method."""
|
|
415
|
+
|
|
416
|
+
all: ASyncFunctionAsyncDefault[Concatenate[AnyIterable[I], P], bool]
|
|
417
|
+
"""Asynchronous default version of the :meth:`~ASyncMethodDescriptor.all` method."""
|
|
418
|
+
|
|
419
|
+
min: ASyncFunctionAsyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
420
|
+
"""Asynchronous default version of the :meth:`~ASyncMethodDescriptor.min` method."""
|
|
421
|
+
|
|
422
|
+
max: ASyncFunctionAsyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
423
|
+
"""Asynchronous default version of the :meth:`~ASyncMethodDescriptor.max` method."""
|
|
424
|
+
|
|
425
|
+
sum: ASyncFunctionAsyncDefault[Concatenate[AnyIterable[I], P], T]
|
|
426
|
+
"""Asynchronous default version of the :meth:`~ASyncMethodDescriptor.sum` method."""
|
|
427
|
+
|
|
428
|
+
@overload
|
|
429
|
+
def __get__(
|
|
430
|
+
self, instance: None, owner: Type[I]
|
|
431
|
+
) -> "ASyncMethodDescriptorAsyncDefault[I, P, T]": ...
|
|
432
|
+
@overload
|
|
433
|
+
def __get__(
|
|
434
|
+
self, instance: I, owner: Type[I]
|
|
435
|
+
) -> "ASyncBoundMethodAsyncDefault[I, P, T]": ...
|
|
436
|
+
def __get__(
|
|
437
|
+
_ModifiedMixin self, instance: Optional[I], owner: Type[I]
|
|
438
|
+
) -> "Union[ASyncMethodDescriptorAsyncDefault, ASyncBoundMethodAsyncDefault[I, P, T]]":
|
|
439
|
+
"""
|
|
440
|
+
Get the bound method or the descriptor itself.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
instance: The instance to bind the method to, or None.
|
|
444
|
+
owner: The owner class.
|
|
445
|
+
|
|
446
|
+
Examples:
|
|
447
|
+
>>> descriptor = ASyncMethodDescriptorAsyncDefault(my_function)
|
|
448
|
+
>>> bound_method = descriptor.__get__(instance, MyClass)
|
|
449
|
+
"""
|
|
450
|
+
if instance is None:
|
|
451
|
+
return self
|
|
452
|
+
|
|
453
|
+
cdef object bound
|
|
454
|
+
cdef str field_name = self.field_name
|
|
455
|
+
cdef dict instance_dict = instance.__dict__
|
|
456
|
+
|
|
457
|
+
try:
|
|
458
|
+
bound = instance_dict[field_name]
|
|
459
|
+
except KeyError:
|
|
460
|
+
bound = ASyncBoundMethodAsyncDefault(
|
|
461
|
+
instance, self.__wrapped__, self.__is_async_def__, **self.modifiers._modifiers
|
|
462
|
+
)
|
|
463
|
+
instance_dict[field_name] = bound
|
|
464
|
+
_logger_debug("new bound method: %s", (bound,))
|
|
465
|
+
_update_cache_timer(field_name, instance, bound)
|
|
466
|
+
return bound
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
cdef dict[object, bint] _is_a_sync_instance_cache = {}
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
cdef bint _is_a_sync_instance(object instance):
|
|
473
|
+
"""Checks if an instance is an ASync instance.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
instance: The instance to check.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
A boolean indicating if the instance is an ASync instance.
|
|
480
|
+
"""
|
|
481
|
+
cdef object instance_type = type(instance)
|
|
482
|
+
cdef object instance_type_uid = id(instance_type)
|
|
483
|
+
if instance_type_uid in _is_a_sync_instance_cache:
|
|
484
|
+
return _is_a_sync_instance_cache[instance_type_uid]
|
|
485
|
+
|
|
486
|
+
if ASyncABC is None:
|
|
487
|
+
_import_ASyncABC()
|
|
488
|
+
|
|
489
|
+
cdef bint is_a_sync = issubclass(instance_type, ASyncABC)
|
|
490
|
+
_is_a_sync_instance_cache[instance_type_uid] = is_a_sync
|
|
491
|
+
return is_a_sync
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
cdef bint _should_await(_ASyncBoundMethod self, dict kwargs):
|
|
495
|
+
"""
|
|
496
|
+
Determine if the method should be awaited.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
kwargs: Keyword arguments passed to the method.
|
|
500
|
+
|
|
501
|
+
Examples:
|
|
502
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
503
|
+
>>> should_await = _should_await(bound_method, kwargs)
|
|
504
|
+
"""
|
|
505
|
+
cdef str flag = get_flag_name(kwargs)
|
|
506
|
+
if flag:
|
|
507
|
+
return is_sync(flag, kwargs, pop_flag=True) # type: ignore [arg-type]
|
|
508
|
+
elif default := self.get_default():
|
|
509
|
+
return default == "sync"
|
|
510
|
+
elif _is_a_sync_instance(instance := self.__c_self__()):
|
|
511
|
+
instance: "ASyncABC"
|
|
512
|
+
return instance.__a_sync_should_await__(kwargs)
|
|
513
|
+
return self._is_async_def
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
cdef class _ASyncBoundMethod(_ASyncFunction):
|
|
517
|
+
"""
|
|
518
|
+
A bound method that can be called both synchronously and asynchronously.
|
|
519
|
+
|
|
520
|
+
This class represents a method bound to an instance, which can be called
|
|
521
|
+
either synchronously or asynchronously based on various conditions. It handles
|
|
522
|
+
caching of bound methods and includes logic for determining whether to await
|
|
523
|
+
the method call based on flags or default settings.
|
|
524
|
+
|
|
525
|
+
Examples:
|
|
526
|
+
>>> class MyClass:
|
|
527
|
+
... def __init__(self, value):
|
|
528
|
+
... self.value = value
|
|
529
|
+
...
|
|
530
|
+
... @ASyncMethodDescriptor
|
|
531
|
+
... async def my_method(self):
|
|
532
|
+
... return self.value
|
|
533
|
+
...
|
|
534
|
+
>>> obj = MyClass(42)
|
|
535
|
+
>>> await obj.my_method()
|
|
536
|
+
42
|
|
537
|
+
>>> obj.my_method(sync=True)
|
|
538
|
+
42
|
|
539
|
+
|
|
540
|
+
See Also:
|
|
541
|
+
- :class:`ASyncMethodDescriptor`
|
|
542
|
+
- :class:`ASyncFunction`
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
def __init__(
|
|
546
|
+
self,
|
|
547
|
+
instance: I,
|
|
548
|
+
unbound: AnyFn[Concatenate[I, P], T],
|
|
549
|
+
bint async_def,
|
|
550
|
+
dict modifiers,
|
|
551
|
+
) -> None:
|
|
552
|
+
"""
|
|
553
|
+
Initialize the bound method.
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
instance: The instance to bind the method to.
|
|
557
|
+
unbound: The unbound function.
|
|
558
|
+
async_def: Whether the original function is an async def.
|
|
559
|
+
**modifiers: Additional modifiers for the function.
|
|
560
|
+
|
|
561
|
+
Examples:
|
|
562
|
+
>>> class MyClass:
|
|
563
|
+
... def __init__(self, value):
|
|
564
|
+
... self.value = value
|
|
565
|
+
...
|
|
566
|
+
... @ASyncMethodDescriptor
|
|
567
|
+
... async def my_method(self):
|
|
568
|
+
... return self.value
|
|
569
|
+
...
|
|
570
|
+
>>> obj = MyClass(42)
|
|
571
|
+
>>> bound_method = ASyncBoundMethod(obj, MyClass.my_method, True)
|
|
572
|
+
"""
|
|
573
|
+
# NOTE: This will be created by the Descriptor
|
|
574
|
+
self._cache_handle = None
|
|
575
|
+
|
|
576
|
+
# First we bind the method to a weak reference
|
|
577
|
+
# - bind and create a strong reference to the cache handle method
|
|
578
|
+
weakref_callback = self.__cancel_cache_handle
|
|
579
|
+
# - we do this so the bound callback method does not get garbage collected until this ASyncBoundMethod dies
|
|
580
|
+
self.__cancel_cache_handle = weakref_callback
|
|
581
|
+
self.__weakself__ = <object>PyWeakref_NewRef(<PyObject*>instance, <PyObject*>weakref_callback)
|
|
582
|
+
|
|
583
|
+
# Then we unwrap the coro_fn and rewrap it so overriding flag kwargs are handled automagically.
|
|
584
|
+
if isinstance(unbound, _ASyncFunction):
|
|
585
|
+
(<dict>modifiers).update((<_ASyncFunction>unbound).modifiers._modifiers)
|
|
586
|
+
unbound = (<_ASyncFunction>unbound).__wrapped__
|
|
587
|
+
# NOTE: the wrapped function was validated when the descriptor was initialized
|
|
588
|
+
_ASyncFunction.__init__(self, unbound, _skip_validate=True, **<dict>modifiers)
|
|
589
|
+
self._is_async_def = async_def
|
|
590
|
+
"""True if `self.__wrapped__` is a coroutine function, False otherwise."""
|
|
591
|
+
update_wrapper(self, unbound)
|
|
592
|
+
|
|
593
|
+
def __repr__(self) -> str:
|
|
594
|
+
"""
|
|
595
|
+
Return a string representation of the bound method.
|
|
596
|
+
|
|
597
|
+
Examples:
|
|
598
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
599
|
+
>>> repr(bound_method)
|
|
600
|
+
'<ASyncBoundMethod for function module.ClassName.method_name bound to instance>'
|
|
601
|
+
"""
|
|
602
|
+
cdef object instance, instance_type
|
|
603
|
+
|
|
604
|
+
try:
|
|
605
|
+
instance = self.__c_self__()
|
|
606
|
+
instance_type = type(instance)
|
|
607
|
+
return "<{} for function {}.{}.{} bound to {}>".format(
|
|
608
|
+
self.__class__.__name__,
|
|
609
|
+
instance_type.__module__,
|
|
610
|
+
instance_type.__name__,
|
|
611
|
+
self.__name__,
|
|
612
|
+
instance,
|
|
613
|
+
)
|
|
614
|
+
except ReferenceError:
|
|
615
|
+
return "<{} for function COLLECTED.COLLECTED.{} bound to {}>".format(
|
|
616
|
+
self.__class__.__name__,
|
|
617
|
+
self.__name__,
|
|
618
|
+
self.__weakself__
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:
|
|
622
|
+
"""
|
|
623
|
+
Call the bound method.
|
|
624
|
+
|
|
625
|
+
This method handles both synchronous and asynchronous calls based on
|
|
626
|
+
the provided flags and the method's configuration.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
*args: Positional arguments.
|
|
630
|
+
**kwargs: Keyword arguments.
|
|
631
|
+
|
|
632
|
+
Examples:
|
|
633
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
634
|
+
>>> await bound_method(arg1, arg2, kwarg1=value1)
|
|
635
|
+
>>> bound_method(arg1, arg2, kwarg1=value1, sync=True)
|
|
636
|
+
"""
|
|
637
|
+
cdef object retval, coro
|
|
638
|
+
cdef bint debug_logs
|
|
639
|
+
if debug_logs := _logger_is_enabled(DEBUG):
|
|
640
|
+
_logger_log(DEBUG, "calling %s with args: %s kwargs: %s", (self, args, kwargs))
|
|
641
|
+
# This could either be a coroutine or a return value from an awaited coroutine,
|
|
642
|
+
# depending on if an overriding flag kwarg was passed into the function call.
|
|
643
|
+
retval = coro = _ASyncFunction.__call__(self, self.__c_self__(), *args, **kwargs)
|
|
644
|
+
if not isawaitable(retval):
|
|
645
|
+
# The coroutine was already awaited due to the use of an overriding flag kwarg.
|
|
646
|
+
# We can return the value.
|
|
647
|
+
pass
|
|
648
|
+
elif _should_await(self, kwargs):
|
|
649
|
+
# The awaitable was not awaited, so now we need to check the flag as defined on 'self' and await if appropriate.
|
|
650
|
+
if debug_logs:
|
|
651
|
+
_logger_log(
|
|
652
|
+
DEBUG, "awaiting %s for %s args: %s kwargs: %s", (coro, self, args, kwargs)
|
|
653
|
+
)
|
|
654
|
+
retval = _await(coro)
|
|
655
|
+
if debug_logs:
|
|
656
|
+
_logger_log(
|
|
657
|
+
DEBUG, "returning %s for %s args: %s kwargs: %s", (retval, self, args, kwargs)
|
|
658
|
+
)
|
|
659
|
+
return retval # type: ignore [call-overload, return-value]
|
|
660
|
+
|
|
661
|
+
@property
|
|
662
|
+
def __self__(self) -> object:
|
|
663
|
+
"""
|
|
664
|
+
Get the instance the method is bound to.
|
|
665
|
+
|
|
666
|
+
Raises:
|
|
667
|
+
ReferenceError: If the instance has been garbage collected.
|
|
668
|
+
|
|
669
|
+
Examples:
|
|
670
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
671
|
+
>>> bound_method.__self__
|
|
672
|
+
<MyClass instance>
|
|
673
|
+
"""
|
|
674
|
+
return self.__c_self__()
|
|
675
|
+
|
|
676
|
+
cdef object __c_self__(self):
|
|
677
|
+
cdef PyObject *self_ptr
|
|
678
|
+
if PyWeakref_GetRef(<PyObject*>self.__weakself__, &self_ptr) == 1:
|
|
679
|
+
# 1 is success
|
|
680
|
+
return <object>self_ptr
|
|
681
|
+
raise ReferenceError(self)
|
|
682
|
+
|
|
683
|
+
def map(
|
|
684
|
+
self,
|
|
685
|
+
*iterables: AnyIterable[I],
|
|
686
|
+
concurrency: Optional[int] = None,
|
|
687
|
+
task_name: str = "",
|
|
688
|
+
**kwargs: P.kwargs,
|
|
689
|
+
) -> "TaskMapping[I, T]":
|
|
690
|
+
"""
|
|
691
|
+
Create a TaskMapping for this method.
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
*iterables: Iterables to map over.
|
|
695
|
+
concurrency: Optional concurrency limit.
|
|
696
|
+
task_name: Optional name for the task.
|
|
697
|
+
**kwargs: Additional keyword arguments.
|
|
698
|
+
|
|
699
|
+
Returns:
|
|
700
|
+
A TaskMapping instance for this method.
|
|
701
|
+
|
|
702
|
+
Examples:
|
|
703
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
704
|
+
>>> task_mapping = bound_method.map(iterable1, iterable2, concurrency=5)
|
|
705
|
+
TODO briefly include how someone would then use task_mapping
|
|
706
|
+
"""
|
|
707
|
+
return self.c_map(iterables, concurrency, task_name, kwargs)
|
|
708
|
+
|
|
709
|
+
cdef object c_map(self, tuple iterables, object concurrency, str task_name, dict kwargs):
|
|
710
|
+
if TaskMapping is None:
|
|
711
|
+
_import_TaskMapping()
|
|
712
|
+
|
|
713
|
+
return TaskMapping(
|
|
714
|
+
self, *iterables, concurrency=concurrency, name=task_name, **kwargs
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
async def any(
|
|
718
|
+
self,
|
|
719
|
+
*iterables: AnyIterable[I],
|
|
720
|
+
concurrency: Optional[int] = None,
|
|
721
|
+
task_name: str = "",
|
|
722
|
+
**kwargs: P.kwargs,
|
|
723
|
+
) -> bool:
|
|
724
|
+
"""
|
|
725
|
+
Check if any of the results are truthy.
|
|
726
|
+
|
|
727
|
+
Args:
|
|
728
|
+
*iterables: Iterables to map over.
|
|
729
|
+
concurrency: Optional concurrency limit.
|
|
730
|
+
task_name: Optional name for the task.
|
|
731
|
+
**kwargs: Additional keyword arguments.
|
|
732
|
+
|
|
733
|
+
Examples:
|
|
734
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
735
|
+
>>> result = await bound_method.any(iterable1, iterable2)
|
|
736
|
+
"""
|
|
737
|
+
return await self.c_map(iterables, concurrency, task_name, kwargs).any(pop=True, sync=False)
|
|
738
|
+
|
|
739
|
+
async def all(
|
|
740
|
+
self,
|
|
741
|
+
*iterables: AnyIterable[I],
|
|
742
|
+
concurrency: Optional[int] = None,
|
|
743
|
+
task_name: str = "",
|
|
744
|
+
**kwargs: P.kwargs,
|
|
745
|
+
) -> bool:
|
|
746
|
+
"""
|
|
747
|
+
Check if all of the results are truthy.
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
*iterables: Iterables to map over.
|
|
751
|
+
concurrency: Optional concurrency limit.
|
|
752
|
+
task_name: Optional name for the task.
|
|
753
|
+
**kwargs: Additional keyword arguments.
|
|
754
|
+
|
|
755
|
+
Examples:
|
|
756
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
757
|
+
>>> result = await bound_method.all(iterable1, iterable2)
|
|
758
|
+
"""
|
|
759
|
+
return await self.c_map(iterables, concurrency, task_name, kwargs).all(pop=True, sync=False)
|
|
760
|
+
|
|
761
|
+
async def min(
|
|
762
|
+
self,
|
|
763
|
+
*iterables: AnyIterable[I],
|
|
764
|
+
concurrency: Optional[int] = None,
|
|
765
|
+
task_name: str = "",
|
|
766
|
+
**kwargs: P.kwargs,
|
|
767
|
+
) -> T:
|
|
768
|
+
"""
|
|
769
|
+
Find the minimum result.
|
|
770
|
+
|
|
771
|
+
Args:
|
|
772
|
+
*iterables: Iterables to map over.
|
|
773
|
+
concurrency: Optional concurrency limit.
|
|
774
|
+
task_name: Optional name for the task.
|
|
775
|
+
**kwargs: Additional keyword arguments.
|
|
776
|
+
|
|
777
|
+
Examples:
|
|
778
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
779
|
+
>>> result = await bound_method.min(iterable1, iterable2)
|
|
780
|
+
"""
|
|
781
|
+
return await self.c_map(iterables, concurrency, task_name, kwargs).min(pop=True, sync=False)
|
|
782
|
+
|
|
783
|
+
async def max(
|
|
784
|
+
self,
|
|
785
|
+
*iterables: AnyIterable[I],
|
|
786
|
+
concurrency: Optional[int] = None,
|
|
787
|
+
task_name: str = "",
|
|
788
|
+
**kwargs: P.kwargs,
|
|
789
|
+
) -> T:
|
|
790
|
+
"""
|
|
791
|
+
Find the maximum result.
|
|
792
|
+
|
|
793
|
+
Args:
|
|
794
|
+
*iterables: Iterables to map over.
|
|
795
|
+
concurrency: Optional concurrency limit.
|
|
796
|
+
task_name: Optional name for the task.
|
|
797
|
+
**kwargs: Additional keyword arguments.
|
|
798
|
+
|
|
799
|
+
Examples:
|
|
800
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
801
|
+
>>> result = await bound_method.max(iterable1, iterable2)
|
|
802
|
+
"""
|
|
803
|
+
return await self.c_map(iterables, concurrency, task_name, kwargs).max(pop=True, sync=False)
|
|
804
|
+
|
|
805
|
+
async def sum(
|
|
806
|
+
self,
|
|
807
|
+
*iterables: AnyIterable[I],
|
|
808
|
+
concurrency: Optional[int] = None,
|
|
809
|
+
task_name: str = "",
|
|
810
|
+
**kwargs: P.kwargs,
|
|
811
|
+
) -> T:
|
|
812
|
+
"""
|
|
813
|
+
Calculate the sum of the results.
|
|
814
|
+
|
|
815
|
+
Args:
|
|
816
|
+
*iterables: Iterables to map over.
|
|
817
|
+
concurrency: Optional concurrency limit.
|
|
818
|
+
task_name: Optional name for the task.
|
|
819
|
+
**kwargs: Additional keyword arguments.
|
|
820
|
+
|
|
821
|
+
Examples:
|
|
822
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
823
|
+
>>> result = await bound_method.sum(iterable1, iterable2)
|
|
824
|
+
"""
|
|
825
|
+
return await self.c_map(iterables, concurrency, task_name, kwargs).sum(pop=True, sync=False)
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
class ASyncBoundMethod(_ASyncBoundMethod, Generic[I, P, T]):
|
|
829
|
+
def __init__(
|
|
830
|
+
_ASyncFunction self,
|
|
831
|
+
instance: I,
|
|
832
|
+
unbound: AnyFn[Concatenate[I, P], T],
|
|
833
|
+
async_def: bool,
|
|
834
|
+
**modifiers: Unpack[ModifierKwargs],
|
|
835
|
+
) -> None:
|
|
836
|
+
"""
|
|
837
|
+
Initialize the bound method.
|
|
838
|
+
|
|
839
|
+
Args:
|
|
840
|
+
instance: The instance to bind the method to.
|
|
841
|
+
unbound: The unbound function.
|
|
842
|
+
async_def: Whether the original function is an async def.
|
|
843
|
+
**modifiers: Additional modifiers for the function.
|
|
844
|
+
|
|
845
|
+
Examples:
|
|
846
|
+
>>> class MyClass:
|
|
847
|
+
... def __init__(self, value):
|
|
848
|
+
... self.value = value
|
|
849
|
+
...
|
|
850
|
+
... @ASyncMethodDescriptor
|
|
851
|
+
... async def my_method(self):
|
|
852
|
+
... return self.value
|
|
853
|
+
...
|
|
854
|
+
>>> obj = MyClass(42)
|
|
855
|
+
>>> bound_method = ASyncBoundMethod(obj, MyClass.my_method, True)
|
|
856
|
+
"""
|
|
857
|
+
_ASyncBoundMethod.__init__(self, instance, unbound, async_def, modifiers)
|
|
858
|
+
update_wrapper(self, unbound)
|
|
859
|
+
|
|
860
|
+
@overload
|
|
861
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T: ...
|
|
862
|
+
@overload
|
|
863
|
+
def __call__(
|
|
864
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
865
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
866
|
+
@overload
|
|
867
|
+
def __call__(
|
|
868
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
869
|
+
) -> T: ...
|
|
870
|
+
@overload
|
|
871
|
+
def __call__(
|
|
872
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
873
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
874
|
+
@overload
|
|
875
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]: ...
|
|
876
|
+
|
|
877
|
+
def __cancel_cache_handle(self, instance: I) -> None:
|
|
878
|
+
"""
|
|
879
|
+
Cancel the cache handle.
|
|
880
|
+
|
|
881
|
+
Args:
|
|
882
|
+
instance: The instance associated with the cache handle.
|
|
883
|
+
|
|
884
|
+
Examples:
|
|
885
|
+
>>> bound_method = ASyncBoundMethod(instance, my_function, True)
|
|
886
|
+
>>> bound_method.__cancel_cache_handle(instance)
|
|
887
|
+
"""
|
|
888
|
+
handle = self._cache_handle
|
|
889
|
+
if handle is not None:
|
|
890
|
+
cancel_handle(handle)
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
class ASyncBoundMethodSyncDefault(ASyncBoundMethod[I, P, T]):
|
|
894
|
+
"""
|
|
895
|
+
A bound method with synchronous default behavior.
|
|
896
|
+
|
|
897
|
+
This class is a specialized version of :class:`ASyncBoundMethod` that defaults to synchronous execution.
|
|
898
|
+
It overrides the `__call__` method to enforce synchronous default behavior.
|
|
899
|
+
|
|
900
|
+
Examples:
|
|
901
|
+
>>> class MyClass:
|
|
902
|
+
... def __init__(self, value):
|
|
903
|
+
... self.value = value
|
|
904
|
+
...
|
|
905
|
+
... @ASyncMethodDescriptorSyncDefault
|
|
906
|
+
... def my_method(self):
|
|
907
|
+
... return self.value
|
|
908
|
+
...
|
|
909
|
+
>>> obj = MyClass(42)
|
|
910
|
+
>>> obj.my_method()
|
|
911
|
+
42
|
|
912
|
+
>>> await obj.my_method(sync=False)
|
|
913
|
+
42
|
|
914
|
+
|
|
915
|
+
See Also:
|
|
916
|
+
- :class:`ASyncBoundMethod`
|
|
917
|
+
- :class:`ASyncMethodDescriptorSyncDefault`
|
|
918
|
+
"""
|
|
919
|
+
|
|
920
|
+
@overload
|
|
921
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T: ...
|
|
922
|
+
@overload
|
|
923
|
+
def __call__(
|
|
924
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
925
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
926
|
+
@overload
|
|
927
|
+
def __call__(
|
|
928
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
929
|
+
) -> T: ...
|
|
930
|
+
@overload
|
|
931
|
+
def __call__(
|
|
932
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
933
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
934
|
+
@overload
|
|
935
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: ...
|
|
936
|
+
|
|
937
|
+
def __call__(self, *args, **kwargs):
|
|
938
|
+
"""
|
|
939
|
+
Call the bound method with synchronous default behavior.
|
|
940
|
+
|
|
941
|
+
Args:
|
|
942
|
+
*args: Positional arguments.
|
|
943
|
+
**kwargs: Keyword arguments.
|
|
944
|
+
|
|
945
|
+
Examples:
|
|
946
|
+
>>> bound_method = ASyncBoundMethodSyncDefault(instance, my_function, True)
|
|
947
|
+
>>> bound_method(arg1, arg2, kwarg1=value1)
|
|
948
|
+
"""
|
|
949
|
+
return _ASyncBoundMethod.__call__(self, *args, **kwargs)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
class ASyncBoundMethodAsyncDefault(ASyncBoundMethod[I, P, T]):
|
|
953
|
+
"""
|
|
954
|
+
A bound method with asynchronous default behavior.
|
|
955
|
+
|
|
956
|
+
This class is a specialized version of :class:`ASyncBoundMethod` that defaults to asynchronous execution.
|
|
957
|
+
It overrides the `__call__` method to enforce asynchronous default behavior.
|
|
958
|
+
|
|
959
|
+
Examples:
|
|
960
|
+
>>> class MyClass:
|
|
961
|
+
... def __init__(self, value):
|
|
962
|
+
... self.value = value
|
|
963
|
+
...
|
|
964
|
+
... @ASyncMethodDescriptorAsyncDefault
|
|
965
|
+
... async def my_method(self):
|
|
966
|
+
... return self.value
|
|
967
|
+
...
|
|
968
|
+
>>> obj = MyClass(42)
|
|
969
|
+
>>> await obj.my_method()
|
|
970
|
+
42
|
|
971
|
+
>>> obj.my_method(sync=True)
|
|
972
|
+
42
|
|
973
|
+
|
|
974
|
+
See Also:
|
|
975
|
+
- :class:`ASyncBoundMethod`
|
|
976
|
+
- :class:`ASyncMethodDescriptorAsyncDefault`
|
|
977
|
+
"""
|
|
978
|
+
|
|
979
|
+
@overload
|
|
980
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T: ...
|
|
981
|
+
@overload
|
|
982
|
+
def __call__(
|
|
983
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
984
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
985
|
+
@overload
|
|
986
|
+
def __call__(
|
|
987
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
988
|
+
) -> T: ...
|
|
989
|
+
@overload
|
|
990
|
+
def __call__(
|
|
991
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
992
|
+
) -> Coroutine[Any, Any, T]: ...
|
|
993
|
+
@overload
|
|
994
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T]: ...
|
|
995
|
+
|
|
996
|
+
def __call__(self, *args, **kwargs):
|
|
997
|
+
"""
|
|
998
|
+
Call the bound method with asynchronous default behavior.
|
|
999
|
+
|
|
1000
|
+
Args:
|
|
1001
|
+
*args: Positional arguments.
|
|
1002
|
+
**kwargs: Keyword arguments.
|
|
1003
|
+
|
|
1004
|
+
Examples:
|
|
1005
|
+
>>> bound_method = ASyncBoundMethodAsyncDefault(instance, my_function, True)
|
|
1006
|
+
>>> await bound_method(arg1, arg2, kwarg1=value1)
|
|
1007
|
+
"""
|
|
1008
|
+
return _ASyncBoundMethod.__call__(self, *args, **kwargs)
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
cdef inline void _import_ASyncABC():
|
|
1012
|
+
global ASyncABC
|
|
1013
|
+
from a_sync.a_sync.abstract import ASyncABC
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
cdef inline void _import_TaskMapping():
|
|
1017
|
+
global TaskMapping
|
|
1018
|
+
from a_sync import TaskMapping
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
del asyncio, inspect, typing, typing_extensions
|
|
1022
|
+
del _descriptor, function
|
|
1023
|
+
|