xoscar 0.4.5__cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 0.5.0__cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.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 xoscar might be problematic. Click here for more details.

xoscar/_utils.pyx CHANGED
@@ -208,28 +208,32 @@ def create_actor_ref(*args, **kwargs):
208
208
 
209
209
  address = to_str(kwargs.pop('address', None))
210
210
  uid = kwargs.pop('uid', None)
211
+ proxy_addresses = kwargs.pop("proxy_addresses", None)
211
212
 
212
213
  if kwargs:
213
214
  raise ValueError('Only `address` or `uid` keywords are supported')
214
215
 
215
- if len(args) == 2:
216
+ if 2 <= len(args) <= 3:
216
217
  if address:
217
218
  raise ValueError('address has been specified')
218
219
  address = to_str(args[0])
219
220
  uid = args[1]
221
+ if len(args) == 3:
222
+ proxy_addresses = args[2]
220
223
  elif len(args) == 1:
221
224
  tp0 = type(args[0])
222
225
  if tp0 is ActorRef or tp0 is LocalActorRef:
223
226
  existing_ref = <ActorRef>(args[0])
224
227
  uid = existing_ref.uid
225
228
  address = to_str(address or existing_ref.address)
229
+ proxy_addresses = existing_ref.proxy_addresses
226
230
  else:
227
231
  uid = args[0]
228
232
 
229
233
  if uid is None:
230
234
  raise ValueError('Actor uid should be provided')
231
235
 
232
- return ActorRef(address, uid)
236
+ return ActorRef(address, uid, proxy_addresses=proxy_addresses)
233
237
 
234
238
 
235
239
  cdef class Timer:
@@ -13,7 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from .base import Channel, Client, Server
16
+ from .base import Channel, ChannelType, Client, Server
17
17
  from .core import gen_local_address, get_client_type, get_server_type
18
18
  from .dummy import DummyChannel, DummyClient, DummyServer
19
19
  from .socket import (
@@ -315,7 +315,11 @@ class SocketClient(Client):
315
315
  except asyncio.TimeoutError:
316
316
  raise ConnectionError("connect timeout")
317
317
  channel = SocketChannel(
318
- reader, writer, local_address=local_address, dest_address=dest_address
318
+ reader,
319
+ writer,
320
+ local_address=local_address,
321
+ dest_address=dest_address,
322
+ channel_type=ChannelType.remote,
319
323
  )
320
324
  return SocketClient(local_address, dest_address, channel)
321
325
 
@@ -431,6 +435,10 @@ class UnixSocketClient(Client):
431
435
  "Cannot connect unix socket due to file not exists"
432
436
  )
433
437
  channel = SocketChannel(
434
- reader, writer, local_address=local_address, dest_address=dest_address
438
+ reader,
439
+ writer,
440
+ local_address=local_address,
441
+ dest_address=dest_address,
442
+ channel_type=ChannelType.ipc,
435
443
  )
436
444
  return UnixSocketClient(local_address, dest_address, channel)
xoscar/backends/config.py CHANGED
@@ -33,6 +33,8 @@ class ActorPoolConfig:
33
33
  self._conf["metrics"] = dict()
34
34
  if "comm" not in self._conf:
35
35
  self._conf["comm"] = dict()
36
+ if "proxy" not in self._conf:
37
+ self._conf["proxy"] = dict()
36
38
 
37
39
  @property
38
40
  def n_pool(self):
@@ -143,3 +145,13 @@ class ActorPoolConfig:
143
145
 
144
146
  def get_comm_config(self) -> dict:
145
147
  return self._conf["comm"]
148
+
149
+ def get_proxy_config(self) -> dict:
150
+ return self._conf["proxy"]
151
+
152
+ def add_proxy_config(self, proxy_config: dict[str, str] | None):
153
+ if proxy_config:
154
+ self._conf["proxy"].update(proxy_config)
155
+
156
+ def remove_proxy(self, from_addr: str):
157
+ del self._conf["proxy"][from_addr]
@@ -72,15 +72,24 @@ class IndigenActorContext(BaseActorContext):
72
72
  self._caller.cancel_tasks()
73
73
 
74
74
  async def _call(
75
- self, address: str, message: _MessageBase, wait: bool = True
75
+ self,
76
+ address: str,
77
+ message: _MessageBase,
78
+ wait: bool = True,
79
+ proxy_addresses: list[str] | None = None,
76
80
  ) -> Union[ResultMessage, ErrorMessage, asyncio.Future]:
77
81
  return await self._caller.call(
78
- Router.get_instance_or_empty(), address, message, wait=wait
82
+ Router.get_instance_or_empty(),
83
+ address,
84
+ message,
85
+ wait=wait,
86
+ proxy_addresses=proxy_addresses,
79
87
  )
80
88
 
81
89
  async def _call_with_client(
82
90
  self, client: Client, message: _MessageBase, wait: bool = True
83
91
  ) -> Union[ResultMessage, ErrorMessage, asyncio.Future]:
92
+ # NOTE: used by copyto, cannot support proxy
84
93
  return await self._caller.call_with_client(client, message, wait)
85
94
 
86
95
  async def _call_send_buffers(
@@ -146,7 +155,12 @@ class IndigenActorContext(BaseActorContext):
146
155
  message = HasActorMessage(
147
156
  new_message_id(), actor_ref, protocol=DEFAULT_PROTOCOL
148
157
  )
149
- future = await self._call(actor_ref.address, message, wait=False)
158
+ future = await self._call(
159
+ actor_ref.address,
160
+ message,
161
+ wait=False,
162
+ proxy_addresses=actor_ref.proxy_addresses,
163
+ )
150
164
  result = await self._wait(future, actor_ref.address, message) # type: ignore
151
165
  return self._process_result_message(result)
152
166
 
@@ -154,7 +168,12 @@ class IndigenActorContext(BaseActorContext):
154
168
  message = DestroyActorMessage(
155
169
  new_message_id(), actor_ref, protocol=DEFAULT_PROTOCOL
156
170
  )
157
- future = await self._call(actor_ref.address, message, wait=False)
171
+ future = await self._call(
172
+ actor_ref.address,
173
+ message,
174
+ wait=False,
175
+ proxy_addresses=actor_ref.proxy_addresses,
176
+ )
158
177
  result = await self._wait(future, actor_ref.address, message) # type: ignore
159
178
  return self._process_result_message(result)
160
179
 
@@ -168,7 +187,7 @@ class IndigenActorContext(BaseActorContext):
168
187
  protocol=DEFAULT_PROTOCOL,
169
188
  )
170
189
  main_address = self._process_result_message(
171
- await self._call(actor_ref.address, control_message) # type: ignore
190
+ await self._call(actor_ref.address, control_message, proxy_addresses=actor_ref.proxy_addresses) # type: ignore
172
191
  )
173
192
  real_actor_ref = await self.actor_ref(actor_ref)
174
193
  if real_actor_ref.address == main_address:
@@ -182,7 +201,9 @@ class IndigenActorContext(BaseActorContext):
182
201
  protocol=DEFAULT_PROTOCOL,
183
202
  )
184
203
  # stop server
185
- result = await self._call(main_address, stop_message)
204
+ result = await self._call(
205
+ main_address, stop_message, proxy_addresses=actor_ref.proxy_addresses
206
+ )
186
207
  return self._process_result_message(result) # type: ignore
187
208
 
188
209
  async def actor_ref(self, *args, **kwargs):
@@ -194,7 +215,12 @@ class IndigenActorContext(BaseActorContext):
194
215
  message = ActorRefMessage(
195
216
  new_message_id(), actor_ref, protocol=DEFAULT_PROTOCOL
196
217
  )
197
- future = await self._call(actor_ref.address, message, wait=False)
218
+ future = await self._call(
219
+ actor_ref.address,
220
+ message,
221
+ wait=False,
222
+ proxy_addresses=actor_ref.proxy_addresses,
223
+ )
198
224
  result = await self._wait(future, actor_ref.address, message)
199
225
  res = self._process_result_message(result)
200
226
  if res.address != connect_addr:
@@ -225,7 +251,12 @@ class IndigenActorContext(BaseActorContext):
225
251
  actor_ref.address,
226
252
  ):
227
253
  detect_cycle_send(send_message, wait_response)
228
- future = await self._call(actor_ref.address, send_message, wait=False)
254
+ future = await self._call(
255
+ actor_ref.address,
256
+ send_message,
257
+ wait=False,
258
+ proxy_addresses=actor_ref.proxy_addresses,
259
+ )
229
260
  if wait_response:
230
261
  result = await self._wait(future, actor_ref.address, send_message) # type: ignore
231
262
  return self._process_result_message(result)
@@ -315,6 +346,8 @@ class IndigenActorContext(BaseActorContext):
315
346
  async def _get_client(self, address: str) -> Client:
316
347
  router = Router.get_instance()
317
348
  assert router is not None, "`copy_to` can only be used inside pools"
349
+ if router.get_proxy(address):
350
+ raise RuntimeError("Cannot run `copy_to` when enabling proxy")
318
351
  return await self._get_copy_to_client(router, address)
319
352
 
320
353
  async def copy_to_buffers(
xoscar/backends/core.py CHANGED
@@ -26,8 +26,15 @@ from typing import Type, Union
26
26
  from .._utils import Timer
27
27
  from ..errors import ServerClosed
28
28
  from ..profiling import get_profiling_data
29
- from .communication import Client, UCXClient
30
- from .message import DeserializeMessageFailed, ErrorMessage, ResultMessage, _MessageBase
29
+ from .communication import ChannelType, Client, UCXClient
30
+ from .message import (
31
+ DeserializeMessageFailed,
32
+ ErrorMessage,
33
+ ForwardMessage,
34
+ MessageType,
35
+ ResultMessage,
36
+ _MessageBase,
37
+ )
31
38
  from .router import Router
32
39
 
33
40
  ResultMessageType = Union[ResultMessage, ErrorMessage]
@@ -67,8 +74,15 @@ class ActorCallerThreadLocal:
67
74
  self._listen_client(client)
68
75
  return client
69
76
 
70
- async def get_client(self, router: Router, dest_address: str) -> Client:
71
- client = await router.get_client(dest_address, from_who=self)
77
+ async def get_client(
78
+ self,
79
+ router: Router,
80
+ dest_address: str,
81
+ proxy_addresses: list[str] | None = None,
82
+ ) -> Client:
83
+ client = await router.get_client(
84
+ dest_address, from_who=self, proxy_addresses=proxy_addresses
85
+ )
72
86
  self._listen_client(client)
73
87
  return client
74
88
 
@@ -191,8 +205,20 @@ class ActorCallerThreadLocal:
191
205
  dest_address: str,
192
206
  message: _MessageBase,
193
207
  wait: bool = True,
208
+ proxy_addresses: list[str] | None = None,
194
209
  ) -> ResultMessage | ErrorMessage | asyncio.Future:
195
- client = await self.get_client(router, dest_address)
210
+ client = await self.get_client(
211
+ router, dest_address, proxy_addresses=proxy_addresses
212
+ )
213
+ if (
214
+ client.channel_type == ChannelType.remote
215
+ and client.dest_address != dest_address
216
+ and message.message_type != MessageType.control
217
+ ):
218
+ # wrap message with forward message
219
+ message = ForwardMessage(
220
+ message_id=message.message_id, address=dest_address, raw_message=message
221
+ )
196
222
  return await self.call_with_client(client, message, wait)
197
223
 
198
224
  async def stop(self):
@@ -16,6 +16,7 @@
16
16
  from __future__ import annotations
17
17
 
18
18
  import asyncio
19
+ import atexit
19
20
  import concurrent.futures as futures
20
21
  import configparser
21
22
  import contextlib
@@ -29,6 +30,7 @@ import sys
29
30
  import threading
30
31
  import uuid
31
32
  from dataclasses import dataclass
33
+ from multiprocessing import util
32
34
  from types import TracebackType
33
35
  from typing import List, Optional
34
36
 
@@ -79,6 +81,19 @@ logger = logging.getLogger(__name__)
79
81
  _init_main_suspended_local = threading.local()
80
82
 
81
83
 
84
+ def _terminate_children():
85
+ for c in multiprocessing.active_children():
86
+ try:
87
+ c.terminate()
88
+ except Exception:
89
+ pass
90
+
91
+
92
+ if util:
93
+ # Import multiprocessing.util to register _exit_function at exit.
94
+ atexit.register(_terminate_children)
95
+
96
+
82
97
  def _patch_spawn_get_preparation_data():
83
98
  try:
84
99
  from multiprocessing import spawn as mp_spawn
@@ -309,6 +324,7 @@ class MainActorPool(MainActorPoolBase):
309
324
  raise
310
325
  finally:
311
326
  status_queue.put(process_status)
327
+ status_queue.cancel_join_thread()
312
328
  await pool.join()
313
329
 
314
330
  async def append_sub_pool(
@@ -47,6 +47,7 @@ class MessageType(Enum):
47
47
  cancel = 9
48
48
  copy_to_buffers = 10
49
49
  copy_to_fileobjs = 11
50
+ forward = 12
50
51
 
51
52
 
52
53
  class ControlMessageType(Enum):
@@ -350,13 +351,14 @@ cdef class DestroyActorMessage(_MessageBase):
350
351
  cdef _MessageSerialItem serial(self):
351
352
  cdef _MessageSerialItem item = _MessageBase.serial(self)
352
353
  item.serialized += (
353
- self.actor_ref.address, self.actor_ref.uid, self.from_main
354
+ self.actor_ref.address, self.actor_ref.uid,
355
+ self.actor_ref.proxy_addresses, self.from_main
354
356
  )
355
357
  return item
356
358
 
357
359
  cdef deserial_members(self, tuple serialized, list subs):
358
360
  _MessageBase.deserial_members(self, serialized, subs)
359
- self.actor_ref = ActorRef(serialized[-3], serialized[-2])
361
+ self.actor_ref = ActorRef(serialized[-4], serialized[-3], serialized[-2])
360
362
  self.from_main = serialized[-1]
361
363
 
362
364
 
@@ -381,13 +383,13 @@ cdef class HasActorMessage(_MessageBase):
381
383
  cdef _MessageSerialItem serial(self):
382
384
  cdef _MessageSerialItem item = _MessageBase.serial(self)
383
385
  item.serialized += (
384
- self.actor_ref.address, self.actor_ref.uid
386
+ self.actor_ref.address, self.actor_ref.uid, self.actor_ref.proxy_addresses
385
387
  )
386
388
  return item
387
389
 
388
390
  cdef deserial_members(self, tuple serialized, list subs):
389
391
  _MessageBase.deserial_members(self, serialized, subs)
390
- self.actor_ref = ActorRef(serialized[-2], serialized[-1])
392
+ self.actor_ref = ActorRef(serialized[-3], serialized[-2], serialized[-1])
391
393
 
392
394
 
393
395
  cdef class ActorRefMessage(_MessageBase):
@@ -411,13 +413,13 @@ cdef class ActorRefMessage(_MessageBase):
411
413
  cdef _MessageSerialItem serial(self):
412
414
  cdef _MessageSerialItem item = _MessageBase.serial(self)
413
415
  item.serialized += (
414
- self.actor_ref.address, self.actor_ref.uid
416
+ self.actor_ref.address, self.actor_ref.uid, self.actor_ref.proxy_addresses
415
417
  )
416
418
  return item
417
419
 
418
420
  cdef deserial_members(self, tuple serialized, list subs):
419
421
  _MessageBase.deserial_members(self, serialized, subs)
420
- self.actor_ref = ActorRef(serialized[-2], serialized[-1])
422
+ self.actor_ref = ActorRef(serialized[-3], serialized[-2], serialized[-1])
421
423
 
422
424
 
423
425
  cdef class SendMessage(_MessageBase):
@@ -449,14 +451,14 @@ cdef class SendMessage(_MessageBase):
449
451
  cdef _MessageSerialItem serial(self):
450
452
  cdef _MessageSerialItem item = _MessageBase.serial(self)
451
453
  item.serialized += (
452
- self.actor_ref.address, self.actor_ref.uid
454
+ self.actor_ref.address, self.actor_ref.uid, self.actor_ref.proxy_addresses
453
455
  )
454
456
  item.subs = [self.content]
455
457
  return item
456
458
 
457
459
  cdef deserial_members(self, tuple serialized, list subs):
458
460
  _MessageBase.deserial_members(self, serialized, subs)
459
- self.actor_ref = ActorRef(serialized[-2], serialized[-1])
461
+ self.actor_ref = ActorRef(serialized[-3], serialized[-2], serialized[-1])
460
462
  self.content = subs[0]
461
463
 
462
464
 
@@ -533,6 +535,50 @@ cdef class CopyToFileObjectsMessage(CopyToBuffersMessage):
533
535
  message_type = MessageType.copy_to_fileobjs
534
536
 
535
537
 
538
+ cdef class ForwardMessage(_MessageBase):
539
+ message_type = MessageType.forward
540
+
541
+ cdef:
542
+ public str address
543
+ public _MessageBase raw_message
544
+
545
+ def __init__(
546
+ self,
547
+ bytes message_id = None,
548
+ str address = None,
549
+ _MessageBase raw_message = None,
550
+ int protocol = DEFAULT_PROTOCOL,
551
+ list message_trace = None,
552
+ ):
553
+ _MessageBase.__init__(
554
+ self,
555
+ message_id,
556
+ protocol=protocol,
557
+ message_trace=message_trace
558
+ )
559
+ self.address = address
560
+ self.raw_message = raw_message
561
+
562
+ cdef _MessageSerialItem serial(self):
563
+ cdef _MessageSerialItem item = _MessageBase.serial(self)
564
+ cdef _MessageSerialItem raw_message_serialized = self.raw_message.serial()
565
+ item.serialized += (self.address,)
566
+ item.serialized += raw_message_serialized.serialized
567
+ item.subs += raw_message_serialized.subs
568
+ return item
569
+
570
+ cdef deserial_members(self, tuple serialized, list subs):
571
+ # 5 is magic number that means serialized for _MessageBase
572
+ base_serialized = serialized[:5]
573
+ _MessageBase.deserial_members(self, base_serialized, [])
574
+ self.address = serialized[5]
575
+ # process raw message
576
+ tp = _message_type_to_message_cls[serialized[6]]
577
+ cdef _MessageBase raw_message = <_MessageBase>(tp())
578
+ raw_message.deserial_members(serialized[6:], subs)
579
+ self.raw_message = raw_message
580
+
581
+
536
582
  cdef dict _message_type_to_message_cls = {
537
583
  MessageType.control.value: ControlMessage,
538
584
  MessageType.result.value: ResultMessage,
@@ -545,7 +591,8 @@ cdef dict _message_type_to_message_cls = {
545
591
  MessageType.tell.value: TellMessage,
546
592
  MessageType.cancel.value: CancelMessage,
547
593
  MessageType.copy_to_buffers.value: CopyToBuffersMessage,
548
- MessageType.copy_to_fileobjs.value: CopyToFileObjectsMessage
594
+ MessageType.copy_to_fileobjs.value: CopyToFileObjectsMessage,
595
+ MessageType.forward.value: ForwardMessage,
549
596
  }
550
597
 
551
598
 
xoscar/backends/pool.py CHANGED
@@ -62,6 +62,7 @@ from .message import (
62
62
  CreateActorMessage,
63
63
  DestroyActorMessage,
64
64
  ErrorMessage,
65
+ ForwardMessage,
65
66
  HasActorMessage,
66
67
  MessageType,
67
68
  ResultMessage,
@@ -123,6 +124,7 @@ def _register_message_handler(pool_type: Type["AbstractActorPool"]):
123
124
  (MessageType.send, pool_type.send),
124
125
  (MessageType.tell, pool_type.tell),
125
126
  (MessageType.cancel, pool_type.cancel),
127
+ (MessageType.forward, pool_type.forward),
126
128
  (MessageType.control, pool_type.handle_control_command),
127
129
  (MessageType.copy_to_buffers, pool_type.handle_copy_to_buffers_message),
128
130
  (MessageType.copy_to_fileobjs, pool_type.handle_copy_to_fileobjs_message),
@@ -311,6 +313,22 @@ class AbstractActorPool(ABC):
311
313
  result or error message
312
314
  """
313
315
 
316
+ async def forward(self, message: ForwardMessage) -> ResultMessageType:
317
+ """
318
+ Forward message
319
+
320
+ Parameters
321
+ ----------
322
+ message: ForwardMessage
323
+ Forward message.
324
+
325
+ Returns
326
+ -------
327
+ result_message
328
+ result or error message
329
+ """
330
+ return await self.call(message.address, message.raw_message)
331
+
314
332
  def _sync_pool_config(self, actor_pool_config: ActorPoolConfig):
315
333
  self._config = actor_pool_config
316
334
  # remove router from global one
@@ -377,7 +395,7 @@ class AbstractActorPool(ABC):
377
395
  try:
378
396
  await channel.send(result)
379
397
  except (ChannelClosed, ConnectionResetError):
380
- if not self._stopped.is_set():
398
+ if not self._stopped.is_set() and not channel.closed:
381
399
  raise
382
400
  except Exception as ex:
383
401
  logger.exception(
@@ -443,6 +461,7 @@ class AbstractActorPool(ABC):
443
461
  gen_local_address(process_index),
444
462
  actor_pool_config.external_to_internal_address_map,
445
463
  comm_config=actor_pool_config.get_comm_config(),
464
+ proxy_config=actor_pool_config.get_proxy_config(),
446
465
  )
447
466
  kw["env"] = curr_pool_config["env"]
448
467
 
@@ -605,7 +624,8 @@ class ActorPoolBase(AbstractActorPool, metaclass=ABCMeta):
605
624
  self._actors[actor_id] = actor
606
625
  await self._run_coro(message.message_id, actor.__post_create__())
607
626
 
608
- result = ActorRef(address, actor_id)
627
+ proxies = self._router.get_proxies(address)
628
+ result = ActorRef(address, actor_id, proxy_addresses=proxies)
609
629
  # ensemble result message
610
630
  processor.result = ResultMessage(
611
631
  message.message_id, result, protocol=message.protocol
@@ -647,9 +667,10 @@ class ActorPoolBase(AbstractActorPool, metaclass=ABCMeta):
647
667
  actor_id = message.actor_ref.uid
648
668
  if actor_id not in self._actors:
649
669
  raise ActorNotExist(f"Actor {actor_id} does not exist")
670
+ proxies = self._router.get_proxies(self.external_address)
650
671
  result = ResultMessage(
651
672
  message.message_id,
652
- ActorRef(self.external_address, actor_id),
673
+ ActorRef(self.external_address, actor_id, proxy_addresses=proxies),
653
674
  protocol=message.protocol,
654
675
  )
655
676
  processor.result = result
@@ -762,6 +783,7 @@ class ActorPoolBase(AbstractActorPool, metaclass=ABCMeta):
762
783
  gen_local_address(process_index),
763
784
  actor_pool_config.external_to_internal_address_map,
764
785
  comm_config=actor_pool_config.get_comm_config(),
786
+ proxy_config=actor_pool_config.get_proxy_config(),
765
787
  )
766
788
 
767
789
  @classmethod
@@ -1155,6 +1177,7 @@ class MainActorPoolBase(ActorPoolBase):
1155
1177
  actor_ref = message.actor_ref
1156
1178
  actor_ref.uid = to_binary(actor_ref.uid)
1157
1179
  if actor_ref.address == self.external_address and actor_ref.uid in self._actors:
1180
+ actor_ref.proxy_addresses = self._router.get_proxies(actor_ref.address)
1158
1181
  return ResultMessage(
1159
1182
  message.message_id, actor_ref, protocol=message.protocol
1160
1183
  )
@@ -1163,6 +1186,7 @@ class MainActorPoolBase(ActorPoolBase):
1163
1186
  for address, item in self._allocated_actors.items():
1164
1187
  ref = create_actor_ref(address, actor_ref.uid)
1165
1188
  if ref in item:
1189
+ ref.proxy_addresses = self._router.get_proxies(ref.address)
1166
1190
  return ResultMessage(message.message_id, ref, protocol=message.protocol)
1167
1191
 
1168
1192
  with _ErrorProcessor(
@@ -1503,6 +1527,7 @@ async def create_actor_pool(
1503
1527
  suspend_sigint: bool | None = None,
1504
1528
  use_uvloop: str | bool = "auto",
1505
1529
  logging_conf: dict | None = None,
1530
+ proxy_conf: dict | None = None,
1506
1531
  on_process_down: Callable[[MainActorPoolType, str], None] | None = None,
1507
1532
  on_process_recover: Callable[[MainActorPoolType, str], None] | None = None,
1508
1533
  extra_conf: dict | None = None,
@@ -1549,6 +1574,8 @@ async def create_actor_pool(
1549
1574
  )
1550
1575
  actor_pool_config = ActorPoolConfig()
1551
1576
  actor_pool_config.add_metric_configs(kwargs.get("metrics", {}))
1577
+ # add proxy config
1578
+ actor_pool_config.add_proxy_config(proxy_conf)
1552
1579
  # add main config
1553
1580
  process_index_gen = pool_cls.process_index_gen(address)
1554
1581
  main_process_index = next(process_index_gen)
xoscar/backends/router.py CHANGED
@@ -17,10 +17,15 @@ from __future__ import annotations
17
17
 
18
18
  import asyncio
19
19
  import threading
20
- from typing import Any, Dict, List, Optional, Type
20
+ from typing import Any, Optional, Type, Union
21
21
 
22
22
  from .communication import Client, get_client_type
23
23
 
24
+ _CACHE_KEY_TYPE = Union[
25
+ tuple[str, Any, Optional[Type[Client]]],
26
+ tuple[str, Any, Optional[Type[Client]], Optional[tuple[str, ...]]],
27
+ ]
28
+
24
29
 
25
30
  class Router:
26
31
  """
@@ -32,6 +37,7 @@ class Router:
32
37
  "_local_mapping",
33
38
  "_mapping",
34
39
  "_comm_config",
40
+ "_proxy_config",
35
41
  "_cache_local",
36
42
  )
37
43
 
@@ -56,6 +62,7 @@ class Router:
56
62
  local_address: str | None,
57
63
  mapping: dict[str, str] | None = None,
58
64
  comm_config: dict | None = None,
65
+ proxy_config: dict | None = None,
59
66
  ):
60
67
  self._curr_external_addresses = external_addresses
61
68
  self._local_mapping = dict()
@@ -65,10 +72,11 @@ class Router:
65
72
  mapping = dict()
66
73
  self._mapping = mapping
67
74
  self._comm_config = comm_config or dict()
75
+ self._proxy_config = proxy_config or dict()
68
76
  self._cache_local = threading.local()
69
77
 
70
78
  @property
71
- def _cache(self) -> dict[tuple[str, Any, Optional[Type[Client]]], Client]:
79
+ def _cache(self) -> dict[_CACHE_KEY_TYPE, Client]:
72
80
  try:
73
81
  return self._cache_local.cache
74
82
  except AttributeError:
@@ -92,6 +100,7 @@ class Router:
92
100
  self._local_mapping.update(router._local_mapping)
93
101
  self._mapping.update(router._mapping)
94
102
  self._comm_config.update(router._comm_config)
103
+ self._proxy_config.update(router._proxy_config)
95
104
  self._cache_local = threading.local()
96
105
 
97
106
  def remove_router(self, router: "Router"):
@@ -124,14 +133,23 @@ class Router:
124
133
  external_address: str,
125
134
  from_who: Any = None,
126
135
  cached: bool = True,
136
+ proxy_addresses: list[str] | None = None,
127
137
  **kw,
128
138
  ) -> Client:
129
139
  async with self._lock:
130
- if cached and (external_address, from_who, None) in self._cache:
131
- cached_client = self._cache[external_address, from_who, None]
140
+ proxy_addrs: tuple[str, ...] | None = (
141
+ tuple(proxy_addresses) if proxy_addresses else None
142
+ )
143
+ if (
144
+ cached
145
+ and (external_address, from_who, None, proxy_addrs) in self._cache
146
+ ):
147
+ cached_client = self._cache[
148
+ external_address, from_who, None, proxy_addrs
149
+ ]
132
150
  if cached_client.closed:
133
151
  # closed before, ignore it
134
- del self._cache[external_address, from_who, None]
152
+ del self._cache[external_address, from_who, None, proxy_addrs]
135
153
  else:
136
154
  return cached_client
137
155
 
@@ -139,10 +157,22 @@ class Router:
139
157
  if address is None:
140
158
  # no inner address, just use external address
141
159
  address = external_address
160
+ # check if proxy address exists
161
+ proxy_address = proxy_addresses[-1] if proxy_addresses else None
162
+ if proxy_address is None:
163
+ proxy_address = self.get_proxy(address)
164
+ if proxy_address and proxy_address != self.external_address:
165
+ address = proxy_address
166
+ else:
167
+ if new_proxy_address := self.get_proxy(proxy_address):
168
+ address = new_proxy_address
169
+ else:
170
+ address = proxy_address
171
+
142
172
  client_type: Type[Client] = get_client_type(address)
143
173
  client = await self._create_client(client_type, address, **kw)
144
174
  if cached:
145
- self._cache[external_address, from_who, None] = client
175
+ self._cache[external_address, from_who, None, proxy_addrs] = client
146
176
  return client
147
177
 
148
178
  async def _create_client(
@@ -158,7 +188,7 @@ class Router:
158
188
 
159
189
  def _get_client_type_to_addresses(
160
190
  self, external_address: str
161
- ) -> Dict[Type[Client], str]:
191
+ ) -> dict[Type[Client], str]:
162
192
  client_type_to_addresses = dict()
163
193
  client_type_to_addresses[get_client_type(external_address)] = external_address
164
194
  if external_address in self._curr_external_addresses: # pragma: no cover
@@ -173,7 +203,7 @@ class Router:
173
203
  client_type_to_addresses[client_type] = addr # type: ignore
174
204
  return client_type_to_addresses
175
205
 
176
- def get_all_client_types(self, external_address: str) -> List[Type[Client]]:
206
+ def get_all_client_types(self, external_address: str) -> list[Type[Client]]:
177
207
  return list(self._get_client_type_to_addresses(external_address))
178
208
 
179
209
  async def get_client_via_type(
@@ -205,3 +235,51 @@ class Router:
205
235
  if cached:
206
236
  self._cache[external_address, from_who, client_type] = client
207
237
  return client
238
+
239
+ def get_proxy(self, from_addr: str) -> str | None:
240
+ """
241
+ Get proxy address that sent to.
242
+
243
+ Some patterns can be supported:
244
+
245
+ 1. Direct address mapping, e.g. mapping 127.0.0.1:12345 -> 127.0.0.1:12346
246
+ The message will be sent to 127.0.0.1:12346 as forward one.
247
+ 2. Host match, e.g. mapping 127.0.0.1 -> 127.0.0.1:12346
248
+ All the messages that match the host, e.g. 127.0.0.1:12345 and 127.0.0.1:12347
249
+ will be sent to 127.0.0.1:12346 as forward one.
250
+ 3. Wildcard, e.g. mapping * -> 127.0.0.1:12346
251
+ All the messages will be sent to 127.0.0.1:12346 as forward one.
252
+ """
253
+
254
+ host = from_addr.split(":", 1)[0]
255
+
256
+ proxy_map = self._proxy_config
257
+ addr = proxy_map.get(from_addr)
258
+ if addr and addr != from_addr:
259
+ return addr
260
+ addr = proxy_map.get(host)
261
+ if addr and addr != from_addr:
262
+ return addr
263
+ addr = proxy_map.get("*")
264
+ if addr and addr != from_addr:
265
+ return addr
266
+ return None
267
+
268
+ def get_proxies(self, from_addr: str) -> list[str] | None:
269
+ """
270
+ Get all proxies
271
+
272
+ e.g. Proxy mapping {'a': 'b', 'b': 'c'}
273
+ get_proxies('a') will return ['b', 'c']
274
+ """
275
+
276
+ proxies: list[str] = []
277
+ while True:
278
+ proxy = self.get_proxy(from_addr)
279
+ if not proxies and not proxy:
280
+ return None
281
+ elif not proxy:
282
+ return proxies
283
+ else:
284
+ proxies.append(proxy)
285
+ from_addr = proxy
xoscar/core.pxd CHANGED
@@ -18,6 +18,7 @@ cdef class ActorRef:
18
18
  cdef object __weakref__
19
19
  cdef public str address
20
20
  cdef public object uid
21
+ cdef public list proxy_addresses
21
22
  cdef dict _methods
22
23
 
23
24
 
xoscar/core.pyx CHANGED
@@ -95,7 +95,7 @@ cpdef create_local_actor_ref(address, uid):
95
95
  return None
96
96
 
97
97
 
98
- cpdef create_actor_ref(address, uid):
98
+ cpdef create_actor_ref(address, uid, list proxy_addresses = None):
99
99
  """
100
100
  Create an actor reference.
101
101
  TODO(fyrestone): Remove the create_actor_ref in _utils.pyx
@@ -105,18 +105,20 @@ cpdef create_actor_ref(address, uid):
105
105
  ActorRef or LocalActorRef
106
106
  """
107
107
  actor = _get_local_actor(address, uid)
108
- return ActorRef(address, uid) if actor is None else LocalActorRef(actor)
108
+ return ActorRef(address, uid, proxy_addresses=proxy_addresses) \
109
+ if actor is None else LocalActorRef(actor)
109
110
 
110
111
 
111
112
  cdef class ActorRef:
112
113
  """
113
114
  Reference of an Actor at user side
114
115
  """
115
- def __init__(self, str address, object uid):
116
+ def __init__(self, str address, object uid, list proxy_addresses = None):
116
117
  if isinstance(uid, str):
117
118
  uid = uid.encode()
118
119
  self.uid = uid
119
120
  self.address = address
121
+ self.proxy_addresses = proxy_addresses
120
122
  self._methods = dict()
121
123
 
122
124
  def destroy(self, object callback=None):
@@ -124,7 +126,7 @@ cdef class ActorRef:
124
126
  return ctx.destroy_actor(self)
125
127
 
126
128
  def __reduce__(self):
127
- return create_actor_ref, (self.address, self.uid)
129
+ return create_actor_ref, (self.address, self.uid, self.proxy_addresses)
128
130
 
129
131
  def __getattr__(self, item):
130
132
  if item.startswith('_') and item not in ["__xoscar_next__", "__xoscar_destroy_generator__"]:
@@ -146,7 +148,11 @@ cdef class ActorRef:
146
148
  return False
147
149
 
148
150
  def __repr__(self):
149
- return 'ActorRef(uid={!r}, address={!r})'.format(self.uid, self.address)
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})")
150
156
 
151
157
 
152
158
  cdef class _DelayedArgument:
@@ -13,8 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from . import cuda, exception, numpy, scipy
16
+ from . import cuda, exception, mlx, numpy, scipy
17
17
  from .aio import AioDeserializer, AioSerializer
18
18
  from .core import Serializer, deserialize, serialize, serialize_with_spawn
19
19
 
20
- del cuda, numpy, scipy, exception
20
+ del cuda, numpy, scipy, mlx, exception
@@ -0,0 +1,63 @@
1
+ # Copyright 2022-2025 XProbe Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Any, List
16
+
17
+ import numpy as np
18
+
19
+ from ..utils import lazy_import
20
+ from .core import Serializer, buffered
21
+
22
+ mx = lazy_import("mlx.core")
23
+
24
+
25
+ dtype_map = {
26
+ "b": np.int8,
27
+ "B": np.uint8,
28
+ "h": np.int16,
29
+ "H": np.uint16,
30
+ "i": np.int32,
31
+ "I": np.uint32,
32
+ "q": np.int64,
33
+ "Q": np.uint64,
34
+ "e": np.float16,
35
+ "f": np.float32,
36
+ "d": np.float64,
37
+ }
38
+
39
+
40
+ class MLXSerislizer(Serializer):
41
+ @buffered
42
+ def serial(self, obj: "mx.array", context: dict): # type: ignore
43
+ mv = memoryview(obj)
44
+ header = dict(shape=mv.shape, format=mv.format)
45
+ if not mv.c_contiguous:
46
+ mv = memoryview(bytes(mv))
47
+ return (header,), [mv], True
48
+
49
+ def deserial(self, serialized: tuple, context: dict, subs: List[Any]):
50
+ header = serialized[0]
51
+ shape, format = header["shape"], header["format"]
52
+ mv = memoryview(subs[0])
53
+ if mv.format != format:
54
+ dtype = dtype_map.get(format, np.uint8)
55
+ np_arr = np.frombuffer(mv, dtype=dtype).reshape(shape) # parse
56
+ mv = memoryview(np_arr) # recreate memoryview
57
+ elif mv.shape != shape:
58
+ mv = mv.cast(format, shape) # cast directly
59
+ return mx.array(mv)
60
+
61
+
62
+ if mx is not None:
63
+ MLXSerislizer.register(mx.array)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xoscar
3
- Version: 0.4.5
3
+ Version: 0.5.0
4
4
  Summary: Python actor framework for heterogeneous computing.
5
5
  Home-page: http://github.com/xorbitsai/xoscar
6
6
  Author: Qin Xuye
@@ -1,79 +1,80 @@
1
- xoscar-0.4.5.dist-info/top_level.txt,sha256=vYlqqY4Nys8Thm1hePIuUv8eQePdULVWMmt7lXtX_ZA,21
2
- xoscar-0.4.5.dist-info/RECORD,,
3
- xoscar-0.4.5.dist-info/METADATA,sha256=R6Zr_mb9l4xkf5mOtPo6XBDzFlR_XjHbq-oOrmdc3uM,9042
4
- xoscar-0.4.5.dist-info/WHEEL,sha256=Zqf17R98VQuI9JZEwsfDDfsqkexN9tbZpiCgdvPM64U,154
1
+ xoscar-0.5.0.dist-info/WHEEL,sha256=Zqf17R98VQuI9JZEwsfDDfsqkexN9tbZpiCgdvPM64U,154
2
+ xoscar-0.5.0.dist-info/RECORD,,
3
+ xoscar-0.5.0.dist-info/top_level.txt,sha256=vYlqqY4Nys8Thm1hePIuUv8eQePdULVWMmt7lXtX_ZA,21
4
+ xoscar-0.5.0.dist-info/METADATA,sha256=O_aAm6bQaZF9HvTXJsquvDrePWla--QdgpuPPnlqFds,9042
5
+ xoscar/core.pxd,sha256=I_C2ka7XryyGnnAVXUVm8xfS1gtIrCs6X-9rswgOcUU,1317
6
+ xoscar/core.pyx,sha256=phN-yYV0A0QI8WFi2jCu0nc4CnShTepfDi0V7ZrLYPY,22092
7
+ xoscar/backend.py,sha256=is436OPkZfSpQXaoqTRVta5eoye_pp45RFgCstAk2hU,1850
8
+ xoscar/_version.py,sha256=ClSPrUjgGRGHIkVMQV9XQnkQ-n0akJMnq_rh819nqFE,23719
9
+ xoscar/constants.py,sha256=QHHSREw6uWBBjQDCFqlNfTvBZgniJPGy42KSIsR8Fqw,787
10
+ xoscar/_utils.cpython-310-aarch64-linux-gnu.so,sha256=V_porKVy2B_zPPErcYVSiPBa846EwXG0zKuOwjy1AYM,1108632
11
+ xoscar/api.py,sha256=3hztPoOxg8A_mlhWyWgVP7FMXG0PATA1TP4Rbaj7A-g,13327
12
+ xoscar/driver.py,sha256=498fowtJr6b3FE8FIOA_Tc1Vwx88nfZw7p0FxrML0h4,1372
5
13
  xoscar/errors.py,sha256=wBlQOKsXf0Fc4skN39tDie0YZT-VIAuLNRgoDl2pZcA,1241
6
- xoscar/nvutils.py,sha256=qmW4mKLU0WB2yCs198ccQOgLL02zB7Fsa-AotO3NOmg,20412
7
14
  xoscar/utils.py,sha256=jUw6OICZUPBbmS1b3GE4vLctJf6fCKXrYtLtBuK-Oqc,16483
8
15
  xoscar/context.pxd,sha256=qKa0OyDPZtVymftSh447m-RzFZgmz8rGqQBa7qlauvc,725
9
- xoscar/_utils.pyx,sha256=UR1FtYXAYKIdEWR9HulEpMbSOrkQWi6xGz63d4IQmG0,7059
10
- xoscar/_utils.pxd,sha256=5KYAL3jfPdejsHnrGGT2s--ZUX5SXznQWpHVSno429k,1157
11
- xoscar/core.pyx,sha256=Aqc2i8Fetsd5wRAPF4kL0ddnBZn3E2HRNCvup79BbQc,21730
12
- xoscar/_utils.cpython-310-aarch64-linux-gnu.so,sha256=zOw--rc0okFaW9hsbx0gvqoPMicAkXofFFfNi1aqJw0,1099896
13
- xoscar/debug.py,sha256=9Z8SgE2WaKYQcyDo-5-DxEJQ533v7kWjrvCd28pSx3E,5069
14
- xoscar/constants.py,sha256=QHHSREw6uWBBjQDCFqlNfTvBZgniJPGy42KSIsR8Fqw,787
15
- xoscar/driver.py,sha256=498fowtJr6b3FE8FIOA_Tc1Vwx88nfZw7p0FxrML0h4,1372
16
+ xoscar/libcpp.pxd,sha256=DJqBxLFOKL4iRr9Kale5UH3rbvPRD1x5bTSOPHFpz9I,1147
17
+ xoscar/nvutils.py,sha256=qmW4mKLU0WB2yCs198ccQOgLL02zB7Fsa-AotO3NOmg,20412
16
18
  xoscar/profiling.py,sha256=BC5OF0HzSaXv8V7w-y-B8r5gV5DgxHFoTEIF6jCMioQ,8015
17
- xoscar/context.cpython-310-aarch64-linux-gnu.so,sha256=wtXssLge2fADq8HlPQysi2NLozedPglxN0quuyfBo5E,1435040
18
19
  xoscar/__init__.py,sha256=0zX8kKaio3ZIrlzB79WybcravMJw1OxPWjDspTgJFyQ,1608
19
- xoscar/api.py,sha256=3hztPoOxg8A_mlhWyWgVP7FMXG0PATA1TP4Rbaj7A-g,13327
20
- xoscar/core.pxd,sha256=4lBq8J0kjcXcsGuvN7Kv4xcL5liHwTTFWlqyK7XAEnw,1280
21
- xoscar/_version.py,sha256=ClSPrUjgGRGHIkVMQV9XQnkQ-n0akJMnq_rh819nqFE,23719
22
- xoscar/context.pyx,sha256=8CdgPnWcE9eOp3N600WgDQ03MCi8P73eUOGcfV7Zksg,10942
23
- xoscar/backend.py,sha256=is436OPkZfSpQXaoqTRVta5eoye_pp45RFgCstAk2hU,1850
24
- xoscar/libcpp.pxd,sha256=DJqBxLFOKL4iRr9Kale5UH3rbvPRD1x5bTSOPHFpz9I,1147
25
20
  xoscar/batch.py,sha256=DpArS0L3WYJ_HVPG-6hSYEwoAFY1mY2-mlC4Jp5M_Dw,7872
26
- xoscar/core.cpython-310-aarch64-linux-gnu.so,sha256=GxpyvAR5iaLe87ID8mNPmYdtL6OvzV_QTIp5ugd3vcQ,3476280
21
+ xoscar/core.cpython-310-aarch64-linux-gnu.so,sha256=qc2vx7kBeEkB9nyzJCyB9370u0AcyvSKRYk6hQ_-cto,3503960
22
+ xoscar/context.cpython-310-aarch64-linux-gnu.so,sha256=N3CIj1rcWp88snuYFC9w0943gkOJcmDiAYZ54id9Wdc,1435072
23
+ xoscar/_utils.pxd,sha256=5KYAL3jfPdejsHnrGGT2s--ZUX5SXznQWpHVSno429k,1157
24
+ xoscar/context.pyx,sha256=8CdgPnWcE9eOp3N600WgDQ03MCi8P73eUOGcfV7Zksg,10942
25
+ xoscar/debug.py,sha256=9Z8SgE2WaKYQcyDo-5-DxEJQ533v7kWjrvCd28pSx3E,5069
26
+ xoscar/_utils.pyx,sha256=6iqO4eTwEI-n9i39n_TKz7MWqbytMRnVNubdJ5egL6o,7279
27
27
  xoscar/collective/process_group.py,sha256=zy7LcIFnEcmrcxuECI89v0bQlUbSqQMkVyBw468WBnk,22599
28
+ xoscar/collective/core.py,sha256=NVR-7Iaq3aDPCN6fgXcq9Ew6uFEszRwxYqmUG9FLcws,23502
28
29
  xoscar/collective/utils.py,sha256=3S4qF4JEnAUD3RiWVBUj-ZptL83CBSwGYyVZyIasAsE,1178
29
30
  xoscar/collective/xoscar_pygloo.cpython-310-aarch64-linux-gnu.so,sha256=xVDq_c2G5dQDZLlpyK5BcHAFXN4xJrJ8Op8LdMsbgl0,1583184
30
- xoscar/collective/common.py,sha256=INAnISbfnRicbbbDHTqbSr9ITb89ZphH5BUkSpEdXXU,3561
31
31
  xoscar/collective/__init__.py,sha256=XsClIkO_3Jd8GDifYuAbZCmJLAo9ZqGvnjUn9iuogmU,774
32
- xoscar/collective/core.py,sha256=NVR-7Iaq3aDPCN6fgXcq9Ew6uFEszRwxYqmUG9FLcws,23502
33
- xoscar/metrics/__init__.py,sha256=9Badi7rxYikGm2dQiNCrj9GgMRBxwuR3JaEKcFZmfak,705
34
- xoscar/metrics/api.py,sha256=BBlMIFvVAGVfrtpeJ1YlH9Tqhy9OzGavwvGyeHcQ0Tk,8856
35
- xoscar/metrics/backends/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
36
- xoscar/metrics/backends/metric.py,sha256=aPhyc8JgH22L3rcHP8IjsmgrhSODjg6B5TZVnre97y8,4446
37
- xoscar/metrics/backends/prometheus/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
38
- xoscar/metrics/backends/prometheus/prometheus_metric.py,sha256=MxoMvVrg0pOkKpkjJ0PcAuEaaEJR2FZljmPrLjQ1-oc,2050
39
- xoscar/metrics/backends/console/console_metric.py,sha256=y5CCtH33j3AqI5_Uhwi4mgOcAhyhb4cWv_YvR6fxcbQ,2082
40
- xoscar/metrics/backends/console/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
41
- xoscar/backends/context.py,sha256=Vr_PibRxYCDQ_gYK7r-BOlw9TXw8VQbFsVTH7K7mHPk,15470
42
- xoscar/backends/message.cpython-310-aarch64-linux-gnu.so,sha256=ptMQ5SmlZ5oy_Tl-9ZrtN6EItqzoR_TFY3-iYN6gmRE,3069392
43
- xoscar/backends/__init__.py,sha256=VHEBQcUWM5bj027W8EUf9PiJUAP7JoMrRw3Tsvy5ySw,643
44
- xoscar/backends/core.py,sha256=rXJ73IC5lgERXCWvVrDEEyGAILlwVJs7XIWBCFUEVCc,10166
32
+ xoscar/collective/common.py,sha256=INAnISbfnRicbbbDHTqbSr9ITb89ZphH5BUkSpEdXXU,3561
33
+ xoscar/backends/context.py,sha256=XfDPG2eDhAhE6hWBEkEsHTnyyOYN9R3houlMjAL7BFw,16329
34
+ xoscar/backends/message.pyx,sha256=krGVtZ1YDaZX8yWhaNHwZiudQooLvcGlw6x3Sq7jxjE,19685
35
+ xoscar/backends/pool.py,sha256=omYqaPyv1WW1YrU3OSZhk3kr0JyfI7MYhBtTemVJKZA,60895
36
+ xoscar/backends/core.py,sha256=EH-fHlV9x3bnruEHaUtGYO7osKLfLJ4AQHtuzA_mr2g,10857
45
37
  xoscar/backends/allocate_strategy.py,sha256=tC1Nbq2tJohahUwd-zoRYHEDX65wyuX8tmeY45uWj_w,4845
46
- xoscar/backends/pool.py,sha256=Z7Wdab9dBF3SdQpmzgZhY0d09oTvg5gpFgzYH7vuc4w,59841
47
- xoscar/backends/router.py,sha256=mhSvM5KVfV882jricVcpyxAqHEvhS4zL6ivczC6fOTE,7746
48
- xoscar/backends/message.pyx,sha256=uyzilPc_7SqNwGUL4U-Zbfqku8bfZyRW_Lt_S3I_LEU,17930
49
- xoscar/backends/config.py,sha256=EG26f0GwX_f4dAhwTW77RBjiK9h8R_3JrD-rBF1bAq8,4984
50
- xoscar/backends/test/__init__.py,sha256=j2ZfD6prD9WjUxRUDC7Eq5Z7N7TkL6fFr59oNyc_vY4,682
51
- xoscar/backends/test/pool.py,sha256=TW4X6J-92Pti66103poQBNDBznX6CBD3RLOc_zixjTo,7257
52
- xoscar/backends/test/backend.py,sha256=nv9WFhH5Bbq4Q1HB9yfpciZBaeHT4IQAtzugBWESrUY,1263
38
+ xoscar/backends/message.cpython-310-aarch64-linux-gnu.so,sha256=Kb741RSnrz6pU3ahXFd-gpVK_FwHO7TskkP8G8JmfM8,3330968
39
+ xoscar/backends/config.py,sha256=4tZMiXAMMS8qQ4SX_LjONLtSQVfZTx3m-IK3EqbkYdk,5375
40
+ xoscar/backends/__init__.py,sha256=VHEBQcUWM5bj027W8EUf9PiJUAP7JoMrRw3Tsvy5ySw,643
41
+ xoscar/backends/router.py,sha256=MVl5naz-FYf-Wla7XRn3kRxOpWV0SjKDsKNluifVA8M,10532
42
+ xoscar/backends/communication/ucx.py,sha256=_Dp9Ld2MWIa1txSGMnmfYwJDT0esxS-GOd2FQ4BdHiM,19960
43
+ xoscar/backends/communication/base.py,sha256=0P4Tr35GSWpRp394e9jVWUUoKKa-gIk177eYPw1BnSU,7421
53
44
  xoscar/backends/communication/dummy.py,sha256=6kLkxjNk4xTQ-IlNZD6cftNCx5UsGOur2jk7ikrNUCg,8157
54
45
  xoscar/backends/communication/errors.py,sha256=V3CdBe2xX9Rwv32f2dH2Msc84yaUhlyerZ42-739o1Q,723
55
- xoscar/backends/communication/utils.py,sha256=AmovE-hmWLXNCPwHafYuaRjOk8m42BUyT3XBqfXQRVI,3664
56
- xoscar/backends/communication/__init__.py,sha256=tB05BlK63iWQnfJgRzKt4mFKRtmWUki5hUGSZQwAotc,1050
57
46
  xoscar/backends/communication/core.py,sha256=sJeE3foRIqVPXldzYpFKHDSsabfAIFBU4JuXY4OyklY,2130
58
- xoscar/backends/communication/base.py,sha256=0P4Tr35GSWpRp394e9jVWUUoKKa-gIk177eYPw1BnSU,7421
59
- xoscar/backends/communication/socket.py,sha256=_1tuBZrSmdEC6c6QIj_7JQh23ruIIQPwySDMcrndzwA,14267
60
- xoscar/backends/communication/ucx.py,sha256=_Dp9Ld2MWIa1txSGMnmfYwJDT0esxS-GOd2FQ4BdHiM,19960
47
+ xoscar/backends/communication/socket.py,sha256=6Pf9RJWHuvobjZ1eAU6bUuGY7PzYga3Vyc4PAKF-k2M,14428
48
+ xoscar/backends/communication/utils.py,sha256=AmovE-hmWLXNCPwHafYuaRjOk8m42BUyT3XBqfXQRVI,3664
49
+ xoscar/backends/communication/__init__.py,sha256=oFIg83Ga93-AhrG52TE85Z2LgpGZu1RCgQu1RWi62zQ,1063
50
+ xoscar/backends/test/backend.py,sha256=nv9WFhH5Bbq4Q1HB9yfpciZBaeHT4IQAtzugBWESrUY,1263
51
+ xoscar/backends/test/pool.py,sha256=TW4X6J-92Pti66103poQBNDBznX6CBD3RLOc_zixjTo,7257
52
+ xoscar/backends/test/__init__.py,sha256=j2ZfD6prD9WjUxRUDC7Eq5Z7N7TkL6fFr59oNyc_vY4,682
53
+ xoscar/backends/indigen/backend.py,sha256=znl_fZzWGEtLH8hZ9j9Kkf0fva25jEem2_KO7I1RVvc,1612
61
54
  xoscar/backends/indigen/driver.py,sha256=VGzkacYKykegW5qhCuhx01gdgBZEKJjNIyfNCnA6Nm8,952
55
+ xoscar/backends/indigen/pool.py,sha256=v0Ps79W0WyeFSt2YxCJnh7q_1dnRQmo9887gcW3pNoc,17226
62
56
  xoscar/backends/indigen/__init__.py,sha256=tKHP5ClzedBRBpZsLRVErR3EUNbbDm4CY4u0rCFJr44,685
63
- xoscar/backends/indigen/pool.py,sha256=mWYkOP4VVoUsXFgfpwruPuWblF6Waan5vxit8B-9_oQ,16852
64
- xoscar/backends/indigen/backend.py,sha256=znl_fZzWGEtLH8hZ9j9Kkf0fva25jEem2_KO7I1RVvc,1612
65
- xoscar/aio/file.py,sha256=PBtkLp-Q7XtYl-zk00s18TtgIrkNr60J3Itf66ctO1o,1486
57
+ xoscar/metrics/api.py,sha256=BBlMIFvVAGVfrtpeJ1YlH9Tqhy9OzGavwvGyeHcQ0Tk,8856
58
+ xoscar/metrics/__init__.py,sha256=9Badi7rxYikGm2dQiNCrj9GgMRBxwuR3JaEKcFZmfak,705
59
+ xoscar/metrics/backends/metric.py,sha256=aPhyc8JgH22L3rcHP8IjsmgrhSODjg6B5TZVnre97y8,4446
60
+ xoscar/metrics/backends/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
61
+ xoscar/metrics/backends/console/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
62
+ xoscar/metrics/backends/console/console_metric.py,sha256=y5CCtH33j3AqI5_Uhwi4mgOcAhyhb4cWv_YvR6fxcbQ,2082
63
+ xoscar/metrics/backends/prometheus/prometheus_metric.py,sha256=MxoMvVrg0pOkKpkjJ0PcAuEaaEJR2FZljmPrLjQ1-oc,2050
64
+ xoscar/metrics/backends/prometheus/__init__.py,sha256=h_JgzSqV5lP6vQ6XX_17kE4IY4BRnvKta_7VLQAL1ms,581
66
65
  xoscar/aio/lru.py,sha256=rpXCqSLtPV5xnWtd6uDwQQFGgIPEgvmWEQDkPNUx9cM,6311
67
- xoscar/aio/__init__.py,sha256=kViDKR_kJe59VQViHITKEfBcIgN4ZJblUyd8zl0E3ZI,675
66
+ xoscar/aio/file.py,sha256=PBtkLp-Q7XtYl-zk00s18TtgIrkNr60J3Itf66ctO1o,1486
68
67
  xoscar/aio/base.py,sha256=9j0f1piwfE5R5GIvV212vSD03ixdaeSzSSsO2kxJZVE,2249
69
68
  xoscar/aio/parallelism.py,sha256=VSsjk8wP-Bw7tLeUsTyLVNgp91thjxEfE3pCrw_vF5Q,1293
70
- xoscar/serialization/core.pyx,sha256=bjR-zXGm9qersk7kYPzpjpMIxDl_Auur4BCubRfKmfA,29626
71
- xoscar/serialization/__init__.py,sha256=5Y_C3cYbQJIZ09LRjeCf-jrkLma7mfN8I5bznHrdsbg,846
72
- xoscar/serialization/exception.py,sha256=Jy8Lsk0z-VJyEUaWeuZIwkmxqaoB-nLKMa1D15Cl4js,1634
69
+ xoscar/aio/__init__.py,sha256=kViDKR_kJe59VQViHITKEfBcIgN4ZJblUyd8zl0E3ZI,675
73
70
  xoscar/serialization/core.pxd,sha256=k4RoJgX5E5LGs4jdCQ7vvcn26MabXbrWoWhkO49X6YI,985
74
- xoscar/serialization/scipy.py,sha256=yOEi0NB8cqQ6e2UnCZ1w006RsB7T725tIL-DM_hNcsU,2482
71
+ xoscar/serialization/core.pyx,sha256=bjR-zXGm9qersk7kYPzpjpMIxDl_Auur4BCubRfKmfA,29626
75
72
  xoscar/serialization/aio.py,sha256=5DySPgDxU43ec7_5Ct44-Oqt7YNSJBfuf8VdQgQlChA,4731
76
- xoscar/serialization/numpy.py,sha256=5Kem87CvpJmzUMp3QHk4WeHU30FoQWTJJP2SwIcaQG0,2919
73
+ xoscar/serialization/mlx.py,sha256=N_cvbTUBKc14XWYsPIMz4kDstyRN1DNhb4BVRgnQm8Y,1872
77
74
  xoscar/serialization/pyfury.py,sha256=sifOnVMYoS82PzZEkzkfxesmMHei23k5UAUUKUyoOYQ,1163
78
- xoscar/serialization/core.cpython-310-aarch64-linux-gnu.so,sha256=BcEG4y3c4hy23J2Nf5yTR8Zq5S174PcVHk-Izg83f0Y,3203272
75
+ xoscar/serialization/scipy.py,sha256=yOEi0NB8cqQ6e2UnCZ1w006RsB7T725tIL-DM_hNcsU,2482
76
+ xoscar/serialization/__init__.py,sha256=v76XC2OQLp-Yk4_U3_IVguEylMeyRw1UrkU_DPDMh0U,856
77
+ xoscar/serialization/numpy.py,sha256=5Kem87CvpJmzUMp3QHk4WeHU30FoQWTJJP2SwIcaQG0,2919
78
+ xoscar/serialization/exception.py,sha256=Jy8Lsk0z-VJyEUaWeuZIwkmxqaoB-nLKMa1D15Cl4js,1634
79
+ xoscar/serialization/core.cpython-310-aarch64-linux-gnu.so,sha256=mjvzWU3R_HSybX--w6X9DSGzHDOqTsKahetnUG-EGu8,3203272
79
80
  xoscar/serialization/cuda.py,sha256=iFUEnN4SiquBIhyieyOrfw3TnKnW-tU_vYgqOxO_DrA,3758
File without changes