xoscar 0.9.0__cp312-cp312-macosx_10_13_x86_64.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.
Files changed (94) hide show
  1. xoscar/__init__.py +61 -0
  2. xoscar/_utils.cpython-312-darwin.so +0 -0
  3. xoscar/_utils.pxd +36 -0
  4. xoscar/_utils.pyx +246 -0
  5. xoscar/_version.py +693 -0
  6. xoscar/aio/__init__.py +16 -0
  7. xoscar/aio/base.py +86 -0
  8. xoscar/aio/file.py +59 -0
  9. xoscar/aio/lru.py +228 -0
  10. xoscar/aio/parallelism.py +39 -0
  11. xoscar/api.py +527 -0
  12. xoscar/backend.py +67 -0
  13. xoscar/backends/__init__.py +14 -0
  14. xoscar/backends/allocate_strategy.py +160 -0
  15. xoscar/backends/communication/__init__.py +30 -0
  16. xoscar/backends/communication/base.py +315 -0
  17. xoscar/backends/communication/core.py +69 -0
  18. xoscar/backends/communication/dummy.py +253 -0
  19. xoscar/backends/communication/errors.py +20 -0
  20. xoscar/backends/communication/socket.py +444 -0
  21. xoscar/backends/communication/ucx.py +538 -0
  22. xoscar/backends/communication/utils.py +97 -0
  23. xoscar/backends/config.py +157 -0
  24. xoscar/backends/context.py +437 -0
  25. xoscar/backends/core.py +352 -0
  26. xoscar/backends/indigen/__init__.py +16 -0
  27. xoscar/backends/indigen/__main__.py +19 -0
  28. xoscar/backends/indigen/backend.py +51 -0
  29. xoscar/backends/indigen/driver.py +26 -0
  30. xoscar/backends/indigen/fate_sharing.py +221 -0
  31. xoscar/backends/indigen/pool.py +515 -0
  32. xoscar/backends/indigen/shared_memory.py +548 -0
  33. xoscar/backends/message.cpython-312-darwin.so +0 -0
  34. xoscar/backends/message.pyi +255 -0
  35. xoscar/backends/message.pyx +646 -0
  36. xoscar/backends/pool.py +1630 -0
  37. xoscar/backends/router.py +285 -0
  38. xoscar/backends/test/__init__.py +16 -0
  39. xoscar/backends/test/backend.py +38 -0
  40. xoscar/backends/test/pool.py +233 -0
  41. xoscar/batch.py +256 -0
  42. xoscar/collective/__init__.py +27 -0
  43. xoscar/collective/backend/__init__.py +13 -0
  44. xoscar/collective/backend/nccl_backend.py +160 -0
  45. xoscar/collective/common.py +102 -0
  46. xoscar/collective/core.py +737 -0
  47. xoscar/collective/process_group.py +687 -0
  48. xoscar/collective/utils.py +41 -0
  49. xoscar/collective/xoscar_pygloo.cpython-312-darwin.so +0 -0
  50. xoscar/collective/xoscar_pygloo.pyi +239 -0
  51. xoscar/constants.py +23 -0
  52. xoscar/context.cpython-312-darwin.so +0 -0
  53. xoscar/context.pxd +21 -0
  54. xoscar/context.pyx +368 -0
  55. xoscar/core.cpython-312-darwin.so +0 -0
  56. xoscar/core.pxd +51 -0
  57. xoscar/core.pyx +664 -0
  58. xoscar/debug.py +188 -0
  59. xoscar/driver.py +42 -0
  60. xoscar/errors.py +63 -0
  61. xoscar/libcpp.pxd +31 -0
  62. xoscar/metrics/__init__.py +21 -0
  63. xoscar/metrics/api.py +288 -0
  64. xoscar/metrics/backends/__init__.py +13 -0
  65. xoscar/metrics/backends/console/__init__.py +13 -0
  66. xoscar/metrics/backends/console/console_metric.py +82 -0
  67. xoscar/metrics/backends/metric.py +149 -0
  68. xoscar/metrics/backends/prometheus/__init__.py +13 -0
  69. xoscar/metrics/backends/prometheus/prometheus_metric.py +70 -0
  70. xoscar/nvutils.py +717 -0
  71. xoscar/profiling.py +260 -0
  72. xoscar/serialization/__init__.py +20 -0
  73. xoscar/serialization/aio.py +141 -0
  74. xoscar/serialization/core.cpython-312-darwin.so +0 -0
  75. xoscar/serialization/core.pxd +28 -0
  76. xoscar/serialization/core.pyi +57 -0
  77. xoscar/serialization/core.pyx +944 -0
  78. xoscar/serialization/cuda.py +111 -0
  79. xoscar/serialization/exception.py +48 -0
  80. xoscar/serialization/mlx.py +67 -0
  81. xoscar/serialization/numpy.py +82 -0
  82. xoscar/serialization/pyfury.py +37 -0
  83. xoscar/serialization/scipy.py +72 -0
  84. xoscar/serialization/torch.py +180 -0
  85. xoscar/utils.py +522 -0
  86. xoscar/virtualenv/__init__.py +34 -0
  87. xoscar/virtualenv/core.py +268 -0
  88. xoscar/virtualenv/platform.py +56 -0
  89. xoscar/virtualenv/utils.py +100 -0
  90. xoscar/virtualenv/uv.py +321 -0
  91. xoscar-0.9.0.dist-info/METADATA +230 -0
  92. xoscar-0.9.0.dist-info/RECORD +94 -0
  93. xoscar-0.9.0.dist-info/WHEEL +6 -0
  94. xoscar-0.9.0.dist-info/top_level.txt +2 -0
xoscar/core.pxd ADDED
@@ -0,0 +1,51 @@
1
+ # Copyright 2022-2023 XProbe Inc.
2
+ # derived from copyright 1999-2021 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ cdef class ActorRef:
18
+ cdef object __weakref__
19
+ cdef public str address
20
+ cdef public object uid
21
+ cdef public list proxy_addresses
22
+ cdef dict _methods
23
+
24
+
25
+ cdef class LocalActorRef(ActorRef):
26
+ cdef object _actor_weakref
27
+ cdef _weakref_local_actor(self)
28
+
29
+
30
+ cdef class BufferRef:
31
+ cdef public str address
32
+ cdef public bytes uid
33
+
34
+
35
+ cdef class FileObjectRef:
36
+ cdef public str address
37
+ cdef public bytes uid
38
+
39
+
40
+ cdef class _BaseActor:
41
+ cdef object __weakref__
42
+ cdef str _address
43
+ cdef object _lock
44
+ cdef object _uid
45
+
46
+ cpdef ActorRef ref(self)
47
+
48
+
49
+ cdef class ActorEnvironment:
50
+ cdef public dict actor_locks
51
+ cdef public object address
xoscar/core.pyx ADDED
@@ -0,0 +1,664 @@
1
+ # Copyright 2022-2023 XProbe Inc.
2
+ # derived from copyright 1999-2021 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import asyncio
17
+ import inspect
18
+ import logging
19
+ import sys
20
+ import weakref
21
+ from typing import Any, AsyncGenerator, Callable
22
+
23
+ cimport cython
24
+
25
+ from .aio import AioFileObject
26
+ from .context cimport get_context
27
+
28
+ from .errors import ActorNotExist, Return
29
+
30
+ from ._utils cimport is_async_generator
31
+
32
+ CALL_METHOD_DEFAULT = 0
33
+ CALL_METHOD_BATCH = 1
34
+ NO_LOCK_ATTRIBUTE_HINT = "__XOSCAR_ACTOR_METHOD_NO_LOCK__"
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+ cdef:
39
+ bint _log_unhandled_errors = False
40
+ bint _log_cycle_send = False
41
+ dict _local_pool_map = dict()
42
+ object _actor_method_wrapper
43
+
44
+ def no_lock(func: Callable):
45
+ setattr(func, NO_LOCK_ATTRIBUTE_HINT, True)
46
+ return func
47
+
48
+
49
+ def set_debug_options(options):
50
+ global _log_unhandled_errors, _log_cycle_send
51
+ if options is None:
52
+ _log_unhandled_errors = _log_cycle_send = False
53
+ else:
54
+ _log_unhandled_errors = options.log_unhandled_errors
55
+ _log_cycle_send = options.log_cycle_send
56
+
57
+
58
+ cdef _get_local_actor(address, uid):
59
+ # Do not expose this method to Python to avoid actor being
60
+ # referenced everywhere.
61
+ #
62
+ # The cycle send detection relies on send message, so we
63
+ # disabled the local actor proxy if the debug option is on.
64
+ if _log_cycle_send:
65
+ return None
66
+ pool_ref = _local_pool_map.get(address)
67
+ pool = None if pool_ref is None else pool_ref()
68
+ if pool is not None:
69
+ actor = pool._actors.get(uid)
70
+ if actor is not None:
71
+ return actor
72
+ return None
73
+
74
+
75
+ def register_local_pool(address, pool):
76
+ """
77
+ Register local actor pool for local actor lookup.
78
+ """
79
+ _local_pool_map[address] = weakref.ref(
80
+ pool, lambda _: _local_pool_map.pop(address, None)
81
+ )
82
+
83
+
84
+ cpdef create_local_actor_ref(address, uid):
85
+ """
86
+ Create a reference to local actor.
87
+
88
+ Returns
89
+ -------
90
+ LocalActorRef or None
91
+ """
92
+ actor = _get_local_actor(address, uid)
93
+ if actor is not None:
94
+ return LocalActorRef(actor)
95
+ return None
96
+
97
+
98
+ cpdef create_actor_ref(address, uid, list proxy_addresses = None):
99
+ """
100
+ Create an actor reference.
101
+ TODO(fyrestone): Remove the create_actor_ref in _utils.pyx
102
+
103
+ Returns
104
+ -------
105
+ ActorRef or LocalActorRef
106
+ """
107
+ actor = _get_local_actor(address, uid)
108
+ return ActorRef(address, uid, proxy_addresses=proxy_addresses) \
109
+ if actor is None else LocalActorRef(actor)
110
+
111
+
112
+ cdef class ActorRef:
113
+ """
114
+ Reference of an Actor at user side
115
+ """
116
+ def __init__(self, str address, object uid, list proxy_addresses = None):
117
+ if isinstance(uid, str):
118
+ uid = uid.encode()
119
+ self.uid = uid
120
+ self.address = address
121
+ self.proxy_addresses = proxy_addresses
122
+ self._methods = dict()
123
+
124
+ def destroy(self, object callback=None):
125
+ ctx = get_context()
126
+ return ctx.destroy_actor(self)
127
+
128
+ def __reduce__(self):
129
+ return create_actor_ref, (self.address, self.uid, self.proxy_addresses)
130
+
131
+ def __getattr__(self, item):
132
+ if item.startswith('_') and item not in ["__xoscar_next__", "__xoscar_destroy_generator__"]:
133
+ return object.__getattribute__(self, item)
134
+
135
+ try:
136
+ return self._methods[item]
137
+ except KeyError:
138
+ method = self._methods[item] = ActorRefMethod(self, item)
139
+ return method
140
+
141
+ def __hash__(self):
142
+ return hash((self.address, self.uid))
143
+
144
+ def __eq__(self, other):
145
+ other_type = type(other)
146
+ if other_type is ActorRef or other_type is LocalActorRef:
147
+ return self.address == other.address and self.uid == other.uid
148
+ return False
149
+
150
+ def __repr__(self):
151
+ if not self.proxy_addresses:
152
+ return 'ActorRef(uid={!r}, address={!r})'.format(self.uid, self.address)
153
+ else:
154
+ return (f"ActorRef(uid={self.uid}, address={self.address}, "
155
+ f"proxy_addresses={self.proxy_addresses})")
156
+
157
+
158
+ cdef class _DelayedArgument:
159
+ cdef readonly tuple arguments
160
+
161
+ def __init__(self, tuple arguments):
162
+ self.arguments = arguments
163
+
164
+
165
+ cdef class ActorRefMethod:
166
+ """
167
+ Wrapper for an Actor method at client
168
+ """
169
+ cdef ActorRef ref
170
+ cdef object method_name
171
+ cdef object _options
172
+
173
+ def __init__(self, ref, method_name, options=None):
174
+ self.ref = ref
175
+ self.method_name = method_name
176
+ self._options = options or {}
177
+
178
+ def __call__(self, *args, **kwargs):
179
+ return self.send(*args, **kwargs)
180
+
181
+ def options(self, **options):
182
+ return ActorRefMethod(self.ref, self.method_name, options)
183
+
184
+ def send(self, *args, **kwargs):
185
+ arg_tuple = (self.method_name, CALL_METHOD_DEFAULT, args, kwargs)
186
+ return get_context().send(self.ref, arg_tuple, **self._options)
187
+
188
+ def tell(self, *args, **kwargs):
189
+ arg_tuple = (self.method_name, CALL_METHOD_DEFAULT, args, kwargs)
190
+ return get_context().send(self.ref, arg_tuple, wait_response=False, **self._options)
191
+
192
+ def delay(self, *args, **kwargs):
193
+ arg_tuple = (self.method_name, CALL_METHOD_DEFAULT, args, kwargs)
194
+ return _DelayedArgument(arg_tuple)
195
+
196
+ def batch(self, *delays, send=True):
197
+ cdef:
198
+ long n_delays = len(delays)
199
+ bint has_kw = False
200
+ list args_list
201
+ list kwargs_list
202
+ _DelayedArgument delay
203
+
204
+ args_list = [None] * n_delays
205
+ kwargs_list = [None] * n_delays
206
+
207
+ last_method = None
208
+ for idx in range(n_delays):
209
+ delay = delays[idx]
210
+ method, _call_method, args, kwargs = delay.arguments
211
+ if last_method is not None and method != last_method:
212
+ raise ValueError('Does not support calling multiple methods in batch')
213
+ last_method = method
214
+
215
+ args_list[idx] = args
216
+ kwargs_list[idx] = kwargs
217
+ if kwargs:
218
+ has_kw = True
219
+
220
+ if not has_kw:
221
+ kwargs_list = None
222
+ if last_method is None:
223
+ last_method = self.method_name
224
+
225
+ message = (last_method, CALL_METHOD_BATCH, (args_list, kwargs_list), None)
226
+ return get_context().send(self.ref, message, wait_response=send, **self._options)
227
+
228
+ def tell_delay(self, *args, delay=None, ignore_conn_fail=True, **kwargs):
229
+ async def delay_fun():
230
+ try:
231
+ await asyncio.sleep(delay)
232
+ message = (self.method_name, CALL_METHOD_DEFAULT, args, kwargs)
233
+ await get_context().send(self.ref, message, wait_response=False, **self._options)
234
+ except Exception as ex:
235
+ if ignore_conn_fail and isinstance(ex, ConnectionRefusedError):
236
+ return
237
+
238
+ logger.error(f'Error {type(ex)} occurred when calling {self.method_name} '
239
+ f'on {self.ref.uid} at {self.ref.address} with tell_delay')
240
+ raise
241
+
242
+ return asyncio.create_task(delay_fun())
243
+
244
+
245
+ cdef class LocalActorRef(ActorRef):
246
+ def __init__(self, _BaseActor actor):
247
+ # Make sure the input actor is an instance of _BaseActor.
248
+ super().__init__(actor._address, actor._uid)
249
+ self._actor_weakref = weakref.ref(actor, lambda _: self._methods.clear())
250
+
251
+ cdef _weakref_local_actor(self):
252
+ actor = _get_local_actor(self.address, self.uid)
253
+ # Make sure the input actor is an instance of _BaseActor.
254
+ if actor is not None and isinstance(actor, _BaseActor):
255
+ self._actor_weakref = weakref.ref(actor, lambda _: self._methods.clear())
256
+ return actor
257
+ return None
258
+
259
+ def __getattr__(self, item):
260
+ try:
261
+ return self._methods[item]
262
+ except KeyError:
263
+ actor = self._actor_weakref() or self._weakref_local_actor()
264
+ if actor is None:
265
+ raise ActorNotExist(f"Actor {self.uid} does not exist") from None
266
+ # For detecting the attribute error.
267
+ getattr(actor, item)
268
+ method = self._methods[item] = LocalActorRefMethod(self, item)
269
+ return method
270
+
271
+ def __repr__(self):
272
+ return 'LocalActorRef(uid={!r}, address={!r}), actor_weakref={!r}'.format(
273
+ self.uid, self.address, self._actor_weakref)
274
+
275
+
276
+ def _has_no_lock_hint_for_method(method) -> bool:
277
+ if getattr(method, NO_LOCK_ATTRIBUTE_HINT, False) is True:
278
+ return True
279
+ if hasattr(method, "__self__"):
280
+ return getattr(method.__self__, NO_LOCK_ATTRIBUTE_HINT, False) is True
281
+ return False
282
+
283
+
284
+ async def __pyx_actor_method_wrapper(method, result_handler, lock, args, kwargs):
285
+ if _has_no_lock_hint_for_method(method):
286
+ result = method(*args, **kwargs)
287
+ if asyncio.iscoroutine(result):
288
+ result = await result
289
+ else:
290
+ async with lock:
291
+ result = method(*args, **kwargs)
292
+ if asyncio.iscoroutine(result):
293
+ result = await result
294
+ return await result_handler(result)
295
+
296
+ # Avoid global lookup.
297
+ _actor_method_wrapper = __pyx_actor_method_wrapper
298
+
299
+
300
+ cdef class LocalActorRefMethod:
301
+ cdef LocalActorRef _local_actor_ref
302
+ cdef object _method_name
303
+
304
+ def __init__(self, LocalActorRef local_actor_ref, method_name):
305
+ self._local_actor_ref = local_actor_ref
306
+ self._method_name = method_name
307
+
308
+ cdef tuple _get_referent(self):
309
+ actor = self._local_actor_ref._actor_weakref() or self._local_actor_ref._weakref_local_actor()
310
+ if actor is None:
311
+ raise ActorNotExist(f"Actor {self._local_actor_ref.uid} does not exist.")
312
+ method = getattr(actor, self._method_name)
313
+ return actor, method
314
+
315
+ def __call__(self, *args, **kwargs):
316
+ actor, method = self._get_referent()
317
+ return _actor_method_wrapper(
318
+ method, actor._handle_actor_result, (<_BaseActor>actor)._lock, args, kwargs)
319
+
320
+ def options(self, **options):
321
+ return self
322
+
323
+ def send(self, *args, **kwargs):
324
+ actor, method = self._get_referent()
325
+ return _actor_method_wrapper(
326
+ method, actor._handle_actor_result, (<_BaseActor>actor)._lock, args, kwargs)
327
+
328
+ def tell(self, *args, **kwargs):
329
+ actor, method = self._get_referent()
330
+ coro = _actor_method_wrapper(
331
+ method, actor._handle_actor_result, (<_BaseActor>actor)._lock, args, kwargs)
332
+ asyncio.create_task(coro)
333
+ return asyncio.sleep(0)
334
+
335
+ def delay(self, *args, **kwargs):
336
+ actor, method = self._get_referent()
337
+ return method.delay(*args, **kwargs)
338
+
339
+ def batch(self, *delays, send=True):
340
+ actor, method = self._get_referent()
341
+ coro = _actor_method_wrapper(
342
+ method.batch, actor._handle_actor_result, (<_BaseActor>actor)._lock, delays, dict())
343
+ if send:
344
+ return coro
345
+ else:
346
+ asyncio.create_task(coro)
347
+ return asyncio.sleep(0)
348
+
349
+ def tell_delay(self, *args, delay=None, ignore_conn_fail=True, **kwargs):
350
+ async def delay_fun():
351
+ await asyncio.sleep(delay)
352
+ await self.tell(*args, **kwargs)
353
+
354
+ return asyncio.create_task(delay_fun())
355
+
356
+
357
+ cdef class _BaseActor:
358
+ """
359
+ Base Indigen actor class, user methods implemented as methods
360
+ """
361
+ def __cinit__(self, *args, **kwargs):
362
+ self._lock = self._create_lock()
363
+
364
+ def _create_lock(self):
365
+ raise NotImplementedError
366
+
367
+ @property
368
+ def uid(self):
369
+ return self._uid
370
+
371
+ @uid.setter
372
+ def uid(self, uid):
373
+ self._uid = uid
374
+
375
+ def _set_uid(self, uid):
376
+ self._uid = uid
377
+
378
+ @property
379
+ def address(self):
380
+ return self._address
381
+
382
+ @address.setter
383
+ def address(self, addr):
384
+ self._address = addr
385
+
386
+ def _set_address(self, addr):
387
+ self._address = addr
388
+
389
+ cpdef ActorRef ref(self):
390
+ return create_actor_ref(self._address, self._uid)
391
+
392
+ async def _handle_actor_result(self, result):
393
+ cdef int idx
394
+ cdef tuple res_tuple
395
+ cdef list tasks, coros, coro_poses, values
396
+ cdef object coro
397
+ cdef bint extract_tuple = False
398
+ cdef bint cancelled = False
399
+ cdef set dones, pending
400
+
401
+ if inspect.isawaitable(result):
402
+ result = await result
403
+ elif is_async_generator(result):
404
+ result = (result,)
405
+ extract_tuple = True
406
+
407
+ if type(result) is tuple:
408
+ res_tuple = result
409
+ coros = []
410
+ coro_poses = []
411
+ values = []
412
+ for idx, res_item in enumerate(res_tuple):
413
+ if is_async_generator(res_item):
414
+ value = self._run_actor_async_generator(res_item)
415
+ coros.append(value)
416
+ coro_poses.append(idx)
417
+ elif inspect.isawaitable(res_item):
418
+ value = res_item
419
+ coros.append(value)
420
+ coro_poses.append(idx)
421
+ else:
422
+ value = res_item
423
+ values.append(value)
424
+
425
+ # when there is only one coroutine, we do not need to use
426
+ # asyncio.wait as it introduces much overhead
427
+ if len(coros) == 1:
428
+ task_result = await coros[0]
429
+ if extract_tuple:
430
+ result = task_result
431
+ else:
432
+ result = tuple(task_result if t is coros[0] else t for t in values)
433
+ elif len(coros) > 0:
434
+ tasks = [asyncio.create_task(t) for t in coros]
435
+ try:
436
+ dones, pending = await asyncio.wait(tasks)
437
+ except asyncio.CancelledError:
438
+ cancelled = True
439
+ for task in tasks:
440
+ task.cancel()
441
+ # wait till all tasks return cancelled
442
+ dones, pending = await asyncio.wait(tasks)
443
+
444
+ if extract_tuple:
445
+ result = list(dones)[0].result()
446
+ else:
447
+ for pos in coro_poses:
448
+ task = tasks[pos]
449
+ values[pos] = task.result()
450
+ result = tuple(values)
451
+
452
+ if cancelled:
453
+ # raise in case no CancelledError raised
454
+ raise asyncio.CancelledError
455
+
456
+ return result
457
+
458
+ async def _run_actor_async_generator(self, gen: AsyncGenerator):
459
+ """
460
+ Run an async generator under Actor lock
461
+ """
462
+ cdef tuple res_tuple
463
+ cdef bint is_exception = False
464
+ cdef object res
465
+ cdef object message_trace = None, pop_message_trace = None, set_message_trace = None
466
+
467
+ from .debug import debug_async_timeout, pop_message_trace, set_message_trace
468
+ try:
469
+ res = None
470
+ while True:
471
+ async with self._lock:
472
+ with debug_async_timeout('actor_lock_timeout',
473
+ 'async_generator %r hold lock timeout', gen):
474
+ if not is_exception:
475
+ res = await gen.asend(res)
476
+ else:
477
+ res = await gen.athrow(*res)
478
+ try:
479
+ if _log_cycle_send:
480
+ message_trace = pop_message_trace()
481
+
482
+ res = await self._handle_actor_result(res)
483
+ is_exception = False
484
+ except:
485
+ res = sys.exc_info()
486
+ is_exception = True
487
+ finally:
488
+ if _log_cycle_send:
489
+ set_message_trace(message_trace)
490
+ except Return as ex:
491
+ return ex.value
492
+ except StopAsyncIteration as ex:
493
+ return
494
+
495
+ async def __post_create__(self):
496
+ """
497
+ Method called after actor creation
498
+ """
499
+ pass
500
+
501
+ async def __pre_destroy__(self):
502
+ """
503
+ Method called before actor destroy
504
+ """
505
+ pass
506
+
507
+ async def __on_receive__(self, tuple message):
508
+ """
509
+ Handle message from other actors and dispatch them to user methods
510
+
511
+ Parameters
512
+ ----------
513
+ message : tuple
514
+ Message shall be (method_name,) + args + (kwargs,)
515
+ """
516
+ from .debug import debug_async_timeout
517
+ try:
518
+ method, call_method, args, kwargs = message
519
+ if call_method == CALL_METHOD_DEFAULT:
520
+ func = getattr(self, method)
521
+ if _has_no_lock_hint_for_method(func):
522
+ result = func(*args, **kwargs)
523
+ if asyncio.iscoroutine(result):
524
+ result = await result
525
+ else:
526
+ async with self._lock:
527
+ with debug_async_timeout('actor_lock_timeout',
528
+ "Method %s of actor %s hold lock timeout.",
529
+ method, self.uid):
530
+ result = func(*args, **kwargs)
531
+ if asyncio.iscoroutine(result):
532
+ result = await result
533
+ elif call_method == CALL_METHOD_BATCH:
534
+ func = getattr(self, method)
535
+ if _has_no_lock_hint_for_method(func):
536
+ args_list, kwargs_list = args
537
+ if kwargs_list is None:
538
+ kwargs_list = [{}] * len(args_list)
539
+ result = func.call_with_lists(args_list, kwargs_list)
540
+ if asyncio.iscoroutine(result):
541
+ result = await result
542
+ else:
543
+ async with self._lock:
544
+ with debug_async_timeout('actor_lock_timeout',
545
+ "Batch method %s of actor %s hold lock timeout, batch size %s.",
546
+ method, self.uid, len(args)):
547
+ args_list, kwargs_list = args
548
+ if kwargs_list is None:
549
+ kwargs_list = [{}] * len(args_list)
550
+ result = func.call_with_lists(args_list, kwargs_list)
551
+ if asyncio.iscoroutine(result):
552
+ result = await result
553
+ else: # pragma: no cover
554
+ raise ValueError(f'call_method {call_method} not valid')
555
+
556
+ return await self._handle_actor_result(result)
557
+ except Exception as ex:
558
+ if _log_unhandled_errors:
559
+ from .debug import logger as debug_logger
560
+
561
+ # use `%.500` to avoid print too long messages
562
+ debug_logger.exception('Got unhandled error when handling message %.500r '
563
+ 'in actor %s at %s', message, self.uid, self.address)
564
+ raise ex
565
+
566
+
567
+ # The @cython.binding(True) is for ray getting members.
568
+ # The value is True by default after cython >= 3.0.0
569
+ @cython.binding(True)
570
+ cdef class _Actor(_BaseActor):
571
+ def _create_lock(self):
572
+ return asyncio.locks.Lock()
573
+
574
+
575
+ cdef class _FakeLock:
576
+ async def __aenter__(self):
577
+ pass
578
+
579
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
580
+ pass
581
+
582
+
583
+ # The @cython.binding(True) is for ray getting members.
584
+ # The value is True by default after cython >= 3.0.0
585
+ @cython.binding(True)
586
+ cdef class _StatelessActor(_BaseActor):
587
+ def _create_lock(self):
588
+ return _FakeLock()
589
+
590
+
591
+ cdef class BufferRef:
592
+ """
593
+ Reference of a buffer
594
+ """
595
+ _ref_to_buffers = weakref.WeakValueDictionary()
596
+
597
+ def __init__(self, str address, bytes uid):
598
+ self.uid = uid
599
+ self.address = address
600
+
601
+ @classmethod
602
+ def create(cls, buffer: Any, address: str, uid: bytes) -> "BufferRef":
603
+ ref = BufferRef(address, uid)
604
+ cls._ref_to_buffers[ref] = buffer
605
+ return ref
606
+
607
+ @classmethod
608
+ def get_buffer(cls, ref: "BufferRef"):
609
+ return cls._ref_to_buffers[ref]
610
+
611
+ def __getstate__(self):
612
+ return self.uid, self.address
613
+
614
+ def __setstate__(self, state):
615
+ self.uid, self.address = state
616
+
617
+ def __hash__(self):
618
+ return hash((self.address, self.uid))
619
+
620
+ def __eq__(self, other):
621
+ if type(other) != BufferRef:
622
+ return False
623
+ return self.address == other.address and self.uid == other.uid
624
+
625
+ def __repr__(self):
626
+ return f'BufferRef(uid={self.uid.hex()}, address={self.address})'
627
+
628
+
629
+ cdef class FileObjectRef:
630
+ """
631
+ Reference of a file obj
632
+ """
633
+ _ref_to_fileobjs = weakref.WeakValueDictionary()
634
+
635
+ def __init__(self, str address, bytes uid):
636
+ self.uid = uid
637
+ self.address = address
638
+
639
+ @classmethod
640
+ def create(cls, fileobj: AioFileObject, address: str, uid: bytes) -> "FileObjectRef":
641
+ ref = FileObjectRef(address, uid)
642
+ cls._ref_to_fileobjs[ref] = fileobj
643
+ return ref
644
+
645
+ @classmethod
646
+ def get_local_file_object(cls, ref: "FileObjectRef") -> AioFileObject:
647
+ return cls._ref_to_fileobjs[ref]
648
+
649
+ def __getstate__(self):
650
+ return self.uid, self.address
651
+
652
+ def __setstate__(self, state):
653
+ self.uid, self.address = state
654
+
655
+ def __hash__(self):
656
+ return hash((self.address, self.uid))
657
+
658
+ def __eq__(self, other):
659
+ if type(other) != FileObjectRef:
660
+ return False
661
+ return self.address == other.address and self.uid == other.uid
662
+
663
+ def __repr__(self):
664
+ return f'FileObjectRef(uid={self.uid.hex()}, address={self.address})'