xoscar 0.4.0__cp312-cp312-macosx_10_9_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.

Potentially problematic release.


This version of xoscar might be problematic. Click here for more details.

Files changed (82) hide show
  1. xoscar/__init__.py +60 -0
  2. xoscar/_utils.cpython-312-darwin.so +0 -0
  3. xoscar/_utils.pxd +36 -0
  4. xoscar/_utils.pyx +241 -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 +493 -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 +242 -0
  19. xoscar/backends/communication/errors.py +20 -0
  20. xoscar/backends/communication/socket.py +414 -0
  21. xoscar/backends/communication/ucx.py +531 -0
  22. xoscar/backends/communication/utils.py +97 -0
  23. xoscar/backends/config.py +145 -0
  24. xoscar/backends/context.py +404 -0
  25. xoscar/backends/core.py +193 -0
  26. xoscar/backends/indigen/__init__.py +16 -0
  27. xoscar/backends/indigen/backend.py +51 -0
  28. xoscar/backends/indigen/driver.py +26 -0
  29. xoscar/backends/indigen/pool.py +469 -0
  30. xoscar/backends/message.cpython-312-darwin.so +0 -0
  31. xoscar/backends/message.pyi +239 -0
  32. xoscar/backends/message.pyx +599 -0
  33. xoscar/backends/pool.py +1596 -0
  34. xoscar/backends/router.py +207 -0
  35. xoscar/backends/test/__init__.py +16 -0
  36. xoscar/backends/test/backend.py +38 -0
  37. xoscar/backends/test/pool.py +208 -0
  38. xoscar/batch.py +256 -0
  39. xoscar/collective/__init__.py +27 -0
  40. xoscar/collective/common.py +102 -0
  41. xoscar/collective/core.py +737 -0
  42. xoscar/collective/process_group.py +687 -0
  43. xoscar/collective/utils.py +41 -0
  44. xoscar/collective/xoscar_pygloo.cpython-312-darwin.so +0 -0
  45. xoscar/collective/xoscar_pygloo.pyi +239 -0
  46. xoscar/constants.py +21 -0
  47. xoscar/context.cpython-312-darwin.so +0 -0
  48. xoscar/context.pxd +21 -0
  49. xoscar/context.pyx +368 -0
  50. xoscar/core.cpython-312-darwin.so +0 -0
  51. xoscar/core.pxd +50 -0
  52. xoscar/core.pyx +658 -0
  53. xoscar/debug.py +188 -0
  54. xoscar/driver.py +42 -0
  55. xoscar/errors.py +63 -0
  56. xoscar/libcpp.pxd +31 -0
  57. xoscar/metrics/__init__.py +21 -0
  58. xoscar/metrics/api.py +288 -0
  59. xoscar/metrics/backends/__init__.py +13 -0
  60. xoscar/metrics/backends/console/__init__.py +13 -0
  61. xoscar/metrics/backends/console/console_metric.py +82 -0
  62. xoscar/metrics/backends/metric.py +149 -0
  63. xoscar/metrics/backends/prometheus/__init__.py +13 -0
  64. xoscar/metrics/backends/prometheus/prometheus_metric.py +70 -0
  65. xoscar/nvutils.py +717 -0
  66. xoscar/profiling.py +260 -0
  67. xoscar/serialization/__init__.py +20 -0
  68. xoscar/serialization/aio.py +138 -0
  69. xoscar/serialization/core.cpython-312-darwin.so +0 -0
  70. xoscar/serialization/core.pxd +28 -0
  71. xoscar/serialization/core.pyi +57 -0
  72. xoscar/serialization/core.pyx +944 -0
  73. xoscar/serialization/cuda.py +111 -0
  74. xoscar/serialization/exception.py +48 -0
  75. xoscar/serialization/numpy.py +82 -0
  76. xoscar/serialization/pyfury.py +37 -0
  77. xoscar/serialization/scipy.py +72 -0
  78. xoscar/utils.py +517 -0
  79. xoscar-0.4.0.dist-info/METADATA +223 -0
  80. xoscar-0.4.0.dist-info/RECORD +82 -0
  81. xoscar-0.4.0.dist-info/WHEEL +5 -0
  82. xoscar-0.4.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,207 @@
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
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import threading
20
+ from typing import Any, Dict, List, Optional, Type
21
+
22
+ from .communication import Client, get_client_type
23
+
24
+
25
+ class Router:
26
+ """
27
+ Router provides mapping from external address to internal address.
28
+ """
29
+
30
+ __slots__ = (
31
+ "_curr_external_addresses",
32
+ "_local_mapping",
33
+ "_mapping",
34
+ "_comm_config",
35
+ "_cache_local",
36
+ )
37
+
38
+ _instance: "Router" | None = None
39
+
40
+ @staticmethod
41
+ def set_instance(router: Optional["Router"]):
42
+ # Default router is set when an actor pool started
43
+ Router._instance = router
44
+
45
+ @staticmethod
46
+ def get_instance() -> "Router" | None:
47
+ return Router._instance
48
+
49
+ @staticmethod
50
+ def get_instance_or_empty() -> "Router":
51
+ return Router._instance or Router(list(), None)
52
+
53
+ def __init__(
54
+ self,
55
+ external_addresses: list[str],
56
+ local_address: str | None,
57
+ mapping: dict[str, str] | None = None,
58
+ comm_config: dict | None = None,
59
+ ):
60
+ self._curr_external_addresses = external_addresses
61
+ self._local_mapping = dict()
62
+ for addr in self._curr_external_addresses:
63
+ self._local_mapping[addr] = local_address
64
+ if mapping is None:
65
+ mapping = dict()
66
+ self._mapping = mapping
67
+ self._comm_config = comm_config or dict()
68
+ self._cache_local = threading.local()
69
+
70
+ @property
71
+ def _cache(self) -> dict[tuple[str, Any, Optional[Type[Client]]], Client]:
72
+ try:
73
+ return self._cache_local.cache
74
+ except AttributeError:
75
+ cache = self._cache_local.cache = dict()
76
+ return cache
77
+
78
+ @property
79
+ def _lock(self) -> asyncio.Lock:
80
+ try:
81
+ return self._cache_local.lock
82
+ except AttributeError:
83
+ lock = self._cache_local.lock = asyncio.Lock()
84
+ return lock
85
+
86
+ def set_mapping(self, mapping: dict[str, str]):
87
+ self._mapping = mapping
88
+ self._cache_local = threading.local()
89
+
90
+ def add_router(self, router: "Router"):
91
+ self._curr_external_addresses.extend(router._curr_external_addresses)
92
+ self._local_mapping.update(router._local_mapping)
93
+ self._mapping.update(router._mapping)
94
+ self._comm_config.update(router._comm_config)
95
+ self._cache_local = threading.local()
96
+
97
+ def remove_router(self, router: "Router"):
98
+ for external_address in router._curr_external_addresses:
99
+ try:
100
+ self._curr_external_addresses.remove(external_address)
101
+ except ValueError:
102
+ pass
103
+ for addr in router._local_mapping:
104
+ self._local_mapping.pop(addr, None)
105
+ for addr in router._mapping:
106
+ self._mapping.pop(addr, None)
107
+ self._cache_local = threading.local()
108
+
109
+ @property
110
+ def external_address(self):
111
+ if self._curr_external_addresses:
112
+ return self._curr_external_addresses[0]
113
+
114
+ def get_internal_address(self, external_address: str) -> str | None:
115
+ try:
116
+ # local address, use dummy address
117
+ return self._local_mapping[external_address]
118
+ except KeyError:
119
+ # try to lookup inner address from address mapping
120
+ return self._mapping.get(external_address)
121
+
122
+ async def get_client(
123
+ self,
124
+ external_address: str,
125
+ from_who: Any = None,
126
+ cached: bool = True,
127
+ **kw,
128
+ ) -> Client:
129
+ 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]
132
+ if cached_client.closed:
133
+ # closed before, ignore it
134
+ del self._cache[external_address, from_who, None]
135
+ else:
136
+ return cached_client
137
+
138
+ address = self.get_internal_address(external_address)
139
+ if address is None:
140
+ # no inner address, just use external address
141
+ address = external_address
142
+ client_type: Type[Client] = get_client_type(address)
143
+ client = await self._create_client(client_type, address, **kw)
144
+ if cached:
145
+ self._cache[external_address, from_who, None] = client
146
+ return client
147
+
148
+ async def _create_client(
149
+ self, client_type: Type[Client], address: str, **kw
150
+ ) -> Client:
151
+ config = client_type.parse_config(self._comm_config)
152
+ if config:
153
+ kw["config"] = config
154
+ local_address = (
155
+ self._curr_external_addresses[0] if self._curr_external_addresses else None
156
+ )
157
+ return await client_type.connect(address, local_address=local_address, **kw)
158
+
159
+ def _get_client_type_to_addresses(
160
+ self, external_address: str
161
+ ) -> Dict[Type[Client], str]:
162
+ client_type_to_addresses = dict()
163
+ client_type_to_addresses[get_client_type(external_address)] = external_address
164
+ if external_address in self._curr_external_addresses: # pragma: no cover
165
+ # local address, use dummy address
166
+ addr = self._local_mapping.get(external_address)
167
+ client_type = get_client_type(addr) # type: ignore
168
+ client_type_to_addresses[client_type] = addr # type: ignore
169
+ if external_address in self._mapping:
170
+ # try to lookup inner address from address mapping
171
+ addr = self._mapping.get(external_address)
172
+ client_type = get_client_type(addr) # type: ignore
173
+ client_type_to_addresses[client_type] = addr # type: ignore
174
+ return client_type_to_addresses
175
+
176
+ def get_all_client_types(self, external_address: str) -> List[Type[Client]]:
177
+ return list(self._get_client_type_to_addresses(external_address))
178
+
179
+ async def get_client_via_type(
180
+ self,
181
+ external_address: str,
182
+ client_type: Type[Client],
183
+ from_who: Any = None,
184
+ cached: bool = True,
185
+ **kw,
186
+ ) -> Client:
187
+ async with self._lock:
188
+ if cached and (external_address, from_who, client_type) in self._cache:
189
+ cached_client = self._cache[external_address, from_who, client_type]
190
+ if cached_client.closed: # pragma: no cover
191
+ # closed before, ignore it
192
+ del self._cache[external_address, from_who, client_type]
193
+ else:
194
+ return cached_client
195
+
196
+ client_type_to_addresses = self._get_client_type_to_addresses(
197
+ external_address
198
+ )
199
+ if client_type not in client_type_to_addresses: # pragma: no cover
200
+ raise ValueError(
201
+ f"Client type({client_type}) is not supported for {external_address}"
202
+ )
203
+ address = client_type_to_addresses[client_type]
204
+ client = await self._create_client(client_type, address, **kw)
205
+ if cached:
206
+ self._cache[external_address, from_who, client_type] = client
207
+ return client
@@ -0,0 +1,16 @@
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
+ from .backend import TestActorBackend
@@ -0,0 +1,38 @@
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
+ from __future__ import annotations
17
+
18
+ from ...backend import register_backend
19
+ from ..indigen.backend import IndigenActorBackend
20
+ from .pool import TestMainActorPool
21
+
22
+
23
+ @register_backend
24
+ class TestActorBackend(IndigenActorBackend):
25
+ @staticmethod
26
+ def name():
27
+ return "test"
28
+
29
+ @classmethod
30
+ async def create_actor_pool(
31
+ cls, address: str, n_process: int | None = None, **kwargs
32
+ ):
33
+ from ..pool import create_actor_pool
34
+
35
+ assert n_process is not None
36
+ return await create_actor_pool(
37
+ address, pool_cls=TestMainActorPool, n_process=n_process, **kwargs
38
+ )
@@ -0,0 +1,208 @@
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
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import multiprocessing
20
+ from typing import Any, Optional
21
+
22
+ from ..communication import DummyServer, gen_local_address
23
+ from ..config import ActorPoolConfig
24
+ from ..indigen.pool import MainActorPool, SubActorPool, SubpoolStatus
25
+ from ..message import ControlMessage, ControlMessageType, new_message_id
26
+ from ..pool import ActorPoolType
27
+
28
+
29
+ class TestMainActorPool(MainActorPool):
30
+ @classmethod
31
+ def get_external_addresses(
32
+ cls,
33
+ address: str,
34
+ n_process: int | None = None,
35
+ ports: list[int] | None = None,
36
+ schemes: list[Optional[str]] | None = None,
37
+ ):
38
+ if "://" in address:
39
+ address = address.split("://", 1)[1]
40
+ return super().get_external_addresses(address, n_process=n_process, ports=ports)
41
+
42
+ @classmethod
43
+ def gen_internal_address(
44
+ cls, process_index: int, external_address: str | None = None
45
+ ) -> str:
46
+ return f"dummy://{process_index}"
47
+
48
+ @classmethod
49
+ async def start_sub_pool(
50
+ cls,
51
+ actor_pool_config: ActorPoolConfig,
52
+ process_index: int,
53
+ start_method: str | None = None,
54
+ ):
55
+ status_queue: multiprocessing.Queue = multiprocessing.Queue()
56
+ return (
57
+ asyncio.create_task(
58
+ cls._create_sub_pool(actor_pool_config, process_index, status_queue, 0)
59
+ ),
60
+ status_queue,
61
+ )
62
+
63
+ @classmethod
64
+ async def wait_sub_pools_ready(cls, create_pool_tasks: list[asyncio.Task]):
65
+ addresses = []
66
+ tasks = []
67
+ for t in create_pool_tasks:
68
+ pool_task, queue = await t
69
+ tasks.append(pool_task)
70
+ status = await asyncio.to_thread(queue.get)
71
+ addresses.append(status.external_addresses)
72
+ return tasks, addresses
73
+
74
+ @classmethod
75
+ async def _create_sub_pool(
76
+ cls,
77
+ actor_config: ActorPoolConfig,
78
+ process_index: int,
79
+ status_queue: multiprocessing.Queue,
80
+ main_pool_pid: int,
81
+ ):
82
+ pool: TestSubActorPool = await TestSubActorPool.create(
83
+ {
84
+ "actor_pool_config": actor_config,
85
+ "process_index": process_index,
86
+ "main_pool_pid": main_pool_pid,
87
+ }
88
+ )
89
+ await pool.start()
90
+ status_queue.put(
91
+ SubpoolStatus(status=0, external_addresses=[pool.external_address])
92
+ )
93
+ actor_config.reset_pool_external_address(process_index, [pool.external_address])
94
+ await pool.join()
95
+
96
+ def _sync_pool_config(self, actor_pool_config: ActorPoolConfig):
97
+ # test pool does not create routers, thus can skip this step
98
+ pass
99
+
100
+ async def append_sub_pool(
101
+ self,
102
+ label: str | None = None,
103
+ internal_address: str | None = None,
104
+ external_address: str | None = None,
105
+ env: dict | None = None,
106
+ modules: list[str] | None = None,
107
+ suspend_sigint: bool | None = None,
108
+ use_uvloop: bool | None = None,
109
+ logging_conf: dict | None = None,
110
+ start_method: str | None = None,
111
+ kwargs: dict | None = None,
112
+ ):
113
+ external_address = (
114
+ external_address
115
+ or TestMainActorPool.get_external_addresses(
116
+ self.external_address, n_process=1
117
+ )[-1]
118
+ )
119
+
120
+ # use last process index's logging_conf and use_uv_loop config if not provide
121
+ actor_pool_config = self._config.as_dict()
122
+ last_process_index = self._config.get_process_indexes()[-1]
123
+ last_logging_conf = actor_pool_config["pools"][last_process_index][
124
+ "logging_conf"
125
+ ]
126
+ last_use_uv_loop = actor_pool_config["pools"][last_process_index]["use_uvloop"]
127
+ _logging_conf = logging_conf or last_logging_conf
128
+ _use_uv_loop = use_uvloop if use_uvloop is not None else last_use_uv_loop
129
+
130
+ process_index = next(TestMainActorPool.process_index_gen(external_address))
131
+ internal_address = internal_address or TestMainActorPool.gen_internal_address(
132
+ process_index, external_address
133
+ )
134
+
135
+ self._config.add_pool_conf(
136
+ process_index,
137
+ label,
138
+ internal_address,
139
+ external_address,
140
+ env,
141
+ modules,
142
+ suspend_sigint,
143
+ _use_uv_loop,
144
+ _logging_conf,
145
+ kwargs,
146
+ )
147
+ pool_task = asyncio.create_task(
148
+ TestMainActorPool.start_sub_pool(self._config, process_index)
149
+ )
150
+ tasks, addresses = await TestMainActorPool.wait_sub_pools_ready([pool_task])
151
+
152
+ self.attach_sub_process(addresses[0][0], tasks[0])
153
+
154
+ control_message = ControlMessage(
155
+ message_id=new_message_id(),
156
+ address=self.external_address,
157
+ control_message_type=ControlMessageType.sync_config,
158
+ content=self._config,
159
+ )
160
+ await self.handle_control_command(control_message)
161
+
162
+ return addresses[0][0]
163
+
164
+ async def kill_sub_pool(
165
+ self, process: multiprocessing.Process, force: bool = False
166
+ ):
167
+ process.cancel() # type: ignore
168
+
169
+ async def is_sub_pool_alive(self, process: multiprocessing.Process):
170
+ return not process.cancelled() # type: ignore
171
+
172
+
173
+ class TestSubActorPool(SubActorPool):
174
+ def _sync_pool_config(self, actor_pool_config: ActorPoolConfig):
175
+ # test pool does not create routers, thus can skip this step
176
+ pass
177
+
178
+ @classmethod
179
+ async def create(cls, config: dict) -> ActorPoolType:
180
+ kw: dict[str, Any] = dict()
181
+ cls._parse_config(config, kw)
182
+ process_index: int = kw["process_index"]
183
+ actor_pool_config = kw["config"] # type: ActorPoolConfig
184
+ external_addresses = actor_pool_config.get_pool_config(process_index)[
185
+ "external_address"
186
+ ]
187
+
188
+ def handle_channel(channel):
189
+ return pool.on_new_channel(channel)
190
+
191
+ # create servers
192
+ server_addresses = external_addresses + [gen_local_address(process_index)]
193
+ server_addresses = sorted(set(server_addresses))
194
+ servers = await cls._create_servers(
195
+ server_addresses, handle_channel, actor_pool_config.get_comm_config()
196
+ )
197
+ cls._update_stored_addresses(servers, server_addresses, actor_pool_config, kw)
198
+
199
+ # create pool
200
+ pool = cls(**kw)
201
+ return pool # type: ignore
202
+
203
+ async def stop(self):
204
+ # do not close dummy server
205
+ self._servers = [
206
+ s for s in self._servers[:-1] if not isinstance(s, DummyServer)
207
+ ]
208
+ await super().stop()