xoscar 0.3.3__tar.gz → 0.4.1__tar.gz

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 (83) hide show
  1. {xoscar-0.3.3 → xoscar-0.4.1}/PKG-INFO +2 -2
  2. {xoscar-0.3.3 → xoscar-0.4.1}/pyproject.toml +5 -4
  3. {xoscar-0.3.3 → xoscar-0.4.1}/setup.cfg +7 -7
  4. {xoscar-0.3.3 → xoscar-0.4.1}/setup.py +53 -0
  5. xoscar-0.4.1/xoscar/aio/__init__.py +16 -0
  6. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/socket.py +34 -9
  7. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/ucx.py +6 -5
  8. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/core.py +51 -40
  9. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/message.pyx +8 -0
  10. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/pool.py +29 -18
  11. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/constants.py +2 -0
  12. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/aio.py +5 -1
  13. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/core.pyx +1 -11
  14. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/utils.py +12 -2
  15. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/PKG-INFO +2 -2
  16. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/SOURCES.txt +0 -1
  17. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/requires.txt +3 -9
  18. xoscar-0.3.3/xoscar/aio/__init__.py +0 -25
  19. xoscar-0.3.3/xoscar/aio/_threads.py +0 -35
  20. {xoscar-0.3.3 → xoscar-0.4.1}/MANIFEST.in +0 -0
  21. {xoscar-0.3.3 → xoscar-0.4.1}/versioneer.py +0 -0
  22. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/__init__.py +0 -0
  23. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/_utils.pxd +0 -0
  24. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/_utils.pyx +0 -0
  25. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/_version.py +0 -0
  26. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/aio/base.py +0 -0
  27. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/aio/file.py +0 -0
  28. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/aio/lru.py +0 -0
  29. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/aio/parallelism.py +0 -0
  30. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/api.py +0 -0
  31. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backend.py +0 -0
  32. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/__init__.py +0 -0
  33. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/allocate_strategy.py +0 -0
  34. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/__init__.py +0 -0
  35. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/base.py +0 -0
  36. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/core.py +0 -0
  37. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/dummy.py +0 -0
  38. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/errors.py +0 -0
  39. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/communication/utils.py +0 -0
  40. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/config.py +0 -0
  41. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/context.py +0 -0
  42. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/indigen/__init__.py +0 -0
  43. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/indigen/backend.py +0 -0
  44. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/indigen/driver.py +0 -0
  45. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/indigen/pool.py +0 -0
  46. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/router.py +0 -0
  47. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/test/__init__.py +0 -0
  48. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/test/backend.py +0 -0
  49. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/backends/test/pool.py +0 -0
  50. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/batch.py +0 -0
  51. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/collective/__init__.py +0 -0
  52. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/collective/common.py +0 -0
  53. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/collective/core.py +0 -0
  54. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/collective/process_group.py +0 -0
  55. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/collective/utils.py +0 -0
  56. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/context.pxd +0 -0
  57. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/context.pyx +0 -0
  58. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/core.pxd +0 -0
  59. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/core.pyx +0 -0
  60. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/debug.py +0 -0
  61. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/driver.py +0 -0
  62. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/errors.py +0 -0
  63. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/libcpp.pxd +0 -0
  64. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/__init__.py +0 -0
  65. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/api.py +0 -0
  66. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/__init__.py +0 -0
  67. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/console/__init__.py +0 -0
  68. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/console/console_metric.py +0 -0
  69. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/metric.py +0 -0
  70. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/prometheus/__init__.py +0 -0
  71. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/metrics/backends/prometheus/prometheus_metric.py +0 -0
  72. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/nvutils.py +0 -0
  73. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/profiling.py +0 -0
  74. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/__init__.py +0 -0
  75. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/core.pxd +0 -0
  76. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/cuda.py +0 -0
  77. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/exception.py +0 -0
  78. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/numpy.py +0 -0
  79. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/pyfury.py +0 -0
  80. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar/serialization/scipy.py +0 -0
  81. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/dependency_links.txt +0 -0
  82. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/not-zip-safe +0 -0
  83. {xoscar-0.3.3 → xoscar-0.4.1}/xoscar.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xoscar
3
- Version: 0.3.3
3
+ Version: 0.4.1
4
4
  Summary: Python actor framework for heterogeneous computing.
5
5
  Home-page: http://github.com/xorbitsai/xoscar
6
6
  Author: Qin Xuye
@@ -11,10 +11,10 @@ License: Apache License 2.0
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Topic :: Software Development :: Libraries
20
20
  Description-Content-Type: text/markdown
@@ -1,6 +1,7 @@
1
1
  [build-system]
2
2
  requires = [
3
- "setuptools<64",
3
+ "setuptools<64; python_version<'3.12'",
4
+ "setuptools>=75; python_version>='3.12'",
4
5
  "packaging",
5
6
  "wheel",
6
7
  "oldest-supported-numpy",
@@ -21,7 +22,7 @@ requires = [
21
22
  "pandas==1.4.0; python_version>='3.10' and python_version<'3.11' and platform_machine=='arm64'",
22
23
  "pandas==1.5.1; python_version>='3.11' and python_version<'3.12'",
23
24
  "pandas>=2.1.1; python_version>'3.11'",
24
- "numpy<2.0.0",
25
+ "numpy",
25
26
  "cython>=0.29.33",
26
27
  "requests>=2.4.0",
27
28
  "cloudpickle>=2.2.1; python_version>='3.11'",
@@ -42,6 +43,6 @@ markers = [
42
43
  ]
43
44
 
44
45
  [tool.cibuildwheel]
45
- build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*"]
46
- skip = "pp* *musllinux* *i686 cp36* cp38-win32 cp39-win32 cp310-win32 cp311-win32"
46
+ build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*"]
47
+ skip = "pp* *musllinux* *i686 cp36* cp38-win32 cp39-win32 cp310-win32 cp311-win32 cp312-win32"
47
48
  manylinux-x86_64-image = "manylinux2014"
@@ -7,15 +7,15 @@ maintainer = Qin Xuye
7
7
  maintainer_email = qinxuye@xprobe.io
8
8
  license = Apache License 2.0
9
9
  url = http://github.com/xorbitsai/xoscar
10
- python_requires = >=3.8
10
+ python_requires = >=3.9
11
11
  classifier =
12
12
  Operating System :: OS Independent
13
13
  Programming Language :: Python
14
14
  Programming Language :: Python :: 3
15
- Programming Language :: Python :: 3.8
16
15
  Programming Language :: Python :: 3.9
17
16
  Programming Language :: Python :: 3.10
18
17
  Programming Language :: Python :: 3.11
18
+ Programming Language :: Python :: 3.12
19
19
  Programming Language :: Python :: Implementation :: CPython
20
20
  Topic :: Software Development :: Libraries
21
21
 
@@ -24,14 +24,13 @@ zip_safe = False
24
24
  include_package_data = True
25
25
  packages = find:
26
26
  install_requires =
27
- numpy>=1.14.0,<2.0.0
27
+ numpy>=1.14.0
28
28
  pandas>=1.0.0
29
29
  scipy>=1.0.0; sys_platform!="win32" or python_version>="3.10"
30
30
  scipy>=1.0.0,<=1.9.1; sys_platform=="win32" and python_version<"3.10"
31
31
  cloudpickle>=1.5.0
32
32
  psutil>=5.9.0
33
33
  tblib>=1.7.0
34
- pickle5; python_version<"3.8"
35
34
  uvloop>=0.14.0; sys_platform!="win32"
36
35
  packaging
37
36
 
@@ -50,15 +49,14 @@ dev =
50
49
  pytest-forked>=1.0
51
50
  pytest-asyncio>=0.14.0
52
51
  ipython>=6.5.0
53
- sphinx>=3.0.0,<5.0.0
52
+ sphinx
54
53
  pydata-sphinx-theme>=0.3.0
55
54
  sphinx-intl>=0.9.9
56
- mock>=4.0.0; python_version<"3.8"
57
55
  flake8>=3.8.0
58
56
  black
59
57
  doc =
60
58
  ipython>=6.5.0
61
- sphinx>=3.0.0,<5.0.0
59
+ sphinx
62
60
  pydata-sphinx-theme>=0.3.0
63
61
  sphinx-intl>=0.9.9
64
62
  extra =
@@ -82,6 +80,8 @@ omit =
82
80
  xoscar/nvutils.py
83
81
  *.pxd
84
82
  */tests/*
83
+ disable_warnings =
84
+ include-ignored
85
85
 
86
86
  [coverage:report]
87
87
  exclude_lines =
@@ -17,6 +17,7 @@ import platform
17
17
  import re
18
18
  import subprocess
19
19
  import sys
20
+ import sysconfig
20
21
  from distutils.command.build_ext import build_ext as _du_build_ext
21
22
  from distutils.file_util import copy_file, move_file
22
23
  from pathlib import Path
@@ -28,6 +29,7 @@ from Cython.Build import cythonize
28
29
  from packaging.version import Version
29
30
  from setuptools import Extension, setup
30
31
  from setuptools.command.build_ext import build_ext
32
+ from setuptools.command.install_lib import install_lib
31
33
  from setuptools.extension import Library
32
34
 
33
35
  try:
@@ -143,6 +145,45 @@ PLAT_TO_CMAKE = {
143
145
  "win-arm64": "ARM64",
144
146
  }
145
147
 
148
+ TARGET_TO_PLAT = {
149
+ 'x86': 'win32',
150
+ 'x64': 'win-amd64',
151
+ 'arm': 'win-arm32',
152
+ 'arm64': 'win-arm64',
153
+ }
154
+
155
+
156
+ # Copied from https://github.com/pypa/setuptools/blob/main/setuptools/_distutils/util.py#L50
157
+ def get_host_platform():
158
+ """
159
+ Return a string that identifies the current platform. Use this
160
+ function to distinguish platform-specific build directories and
161
+ platform-specific built distributions.
162
+ """
163
+
164
+ # This function initially exposed platforms as defined in Python 3.9
165
+ # even with older Python versions when distutils was split out.
166
+ # Now it delegates to stdlib sysconfig, but maintains compatibility.
167
+ return sysconfig.get_platform()
168
+
169
+
170
+ def get_platform():
171
+ if os.name == 'nt':
172
+ target = os.environ.get('VSCMD_ARG_TGT_ARCH')
173
+ return TARGET_TO_PLAT.get(target) or get_host_platform()
174
+ return get_host_platform()
175
+
176
+
177
+ plat_specifier = ".{}-{}".format(get_platform(), sys.implementation.cache_tag)
178
+
179
+
180
+ def get_build_lib():
181
+ return os.path.join("build", "lib" + plat_specifier)
182
+
183
+
184
+ def get_build_temp():
185
+ return os.path.join("build", 'temp' + plat_specifier)
186
+
146
187
 
147
188
  # A CMakeExtension needs a sourcedir instead of a file list.
148
189
  # The name must be the _single_ output extension from the CMake build.
@@ -154,6 +195,18 @@ class XoscarCmakeExtension(Extension):
154
195
 
155
196
 
156
197
  class CMakeBuild(build_ext):
198
+ def finalize_options(self):
199
+ """
200
+ For python 3.12, the build_temp and build_lib dirs are temp dirs which are depended on your OS,
201
+ which leads to that cannot find the copy directory during C++ compiled process.
202
+ However, for Python < 3.12, these two dirs can be automatically located in the `build` directory of the project directory.
203
+ Therefore, in order to be compatible with all Python versions,
204
+ directly using fixed dirs here by coping source codes from `setuptools`.
205
+ """
206
+ self.build_temp = get_build_temp()
207
+ self.build_lib = get_build_lib()
208
+ super().finalize_options()
209
+
157
210
  def copy_extensions_to_source(self):
158
211
  build_py = self.get_finalized_command('build_py')
159
212
  for ext in self.extensions:
@@ -0,0 +1,16 @@
1
+ # Copyright 2022-2023 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
+ from .file import AioFileObject
15
+ from .lru import alru_cache
16
+ from .parallelism import AioEvent
@@ -17,6 +17,7 @@ from __future__ import annotations
17
17
 
18
18
  import asyncio
19
19
  import concurrent.futures as futures
20
+ import logging
20
21
  import os
21
22
  import socket
22
23
  import sys
@@ -28,9 +29,9 @@ from typing import Any, Callable, Coroutine, Dict, Type
28
29
  from urllib.parse import urlparse
29
30
 
30
31
  from ..._utils import to_binary
31
- from ...constants import XOSCAR_UNIX_SOCKET_DIR
32
+ from ...constants import XOSCAR_CONNECT_TIMEOUT, XOSCAR_UNIX_SOCKET_DIR
32
33
  from ...serialization import AioDeserializer, AioSerializer, deserialize
33
- from ...utils import classproperty, implements, is_v6_ip
34
+ from ...utils import classproperty, implements, is_py_312, is_v6_ip
34
35
  from .base import Channel, ChannelType, Client, Server
35
36
  from .core import register_client, register_server
36
37
  from .utils import read_buffers, write_buffers
@@ -38,6 +39,9 @@ from .utils import read_buffers, write_buffers
38
39
  _is_windows: bool = sys.platform.startswith("win")
39
40
 
40
41
 
42
+ logger = logging.getLogger(__name__)
43
+
44
+
41
45
  class SocketChannel(Channel):
42
46
  __slots__ = "reader", "writer", "_channel_type", "_send_lock", "_recv_lock"
43
47
 
@@ -131,11 +135,23 @@ class _BaseSocketServer(Server, metaclass=ABCMeta):
131
135
  if timeout is None:
132
136
  await self._aio_server.serve_forever()
133
137
  else:
134
- future = asyncio.create_task(self._aio_server.serve_forever())
135
- try:
136
- await asyncio.wait_for(future, timeout=timeout)
137
- except (futures.TimeoutError, asyncio.TimeoutError):
138
- future.cancel()
138
+ if is_py_312():
139
+ # For python 3.12, there's a bug for `serve_forever`:
140
+ # https://github.com/python/cpython/issues/123720,
141
+ # which is unable to be cancelled.
142
+ # Here is really a simulation of `wait_for`
143
+ task = asyncio.create_task(self._aio_server.serve_forever())
144
+ await asyncio.sleep(timeout)
145
+ if task.done():
146
+ logger.warning(f"`serve_forever` should never be done.")
147
+ else:
148
+ task.cancel()
149
+ else:
150
+ future = asyncio.create_task(self._aio_server.serve_forever())
151
+ try:
152
+ await asyncio.wait_for(future, timeout=timeout)
153
+ except (futures.TimeoutError, asyncio.TimeoutError, TimeoutError):
154
+ future.cancel()
139
155
 
140
156
  @implements(Server.on_connected)
141
157
  async def on_connected(self, *args, **kwargs):
@@ -161,7 +177,10 @@ class _BaseSocketServer(Server, metaclass=ABCMeta):
161
177
  @implements(Server.stop)
162
178
  async def stop(self):
163
179
  self._aio_server.close()
164
- await self._aio_server.wait_closed()
180
+ # Python 3.12: # https://github.com/python/cpython/issues/104344
181
+ # `wait_closed` leads to hang
182
+ if not is_py_312():
183
+ await self._aio_server.wait_closed()
165
184
  # close all channels
166
185
  await asyncio.gather(
167
186
  *(channel.close() for channel in self._channels if not channel.closed)
@@ -272,7 +291,13 @@ class SocketClient(Client):
272
291
  ) -> "Client":
273
292
  host, port_str = dest_address.rsplit(":", 1)
274
293
  port = int(port_str)
275
- (reader, writer) = await asyncio.open_connection(host=host, port=port, **kwargs)
294
+ config = kwargs.get("config", {})
295
+ connect_timeout = config.get("connect_timeout", XOSCAR_CONNECT_TIMEOUT)
296
+ fut = asyncio.open_connection(host=host, port=port)
297
+ try:
298
+ reader, writer = await asyncio.wait_for(fut, timeout=connect_timeout)
299
+ except asyncio.TimeoutError:
300
+ raise ConnectionError("connect timeout")
276
301
  channel = SocketChannel(
277
302
  reader, writer, local_address=local_address, dest_address=dest_address
278
303
  )
@@ -34,7 +34,7 @@ from .base import Channel, ChannelType, Client, Server
34
34
  from .core import register_client, register_server
35
35
  from .errors import ChannelClosed
36
36
 
37
- ucp = lazy_import("ucp")
37
+ ucp = lazy_import("ucxx")
38
38
  numba_cuda = lazy_import("numba.cuda")
39
39
  rmm = lazy_import("rmm")
40
40
 
@@ -86,7 +86,7 @@ class UCXInitializer:
86
86
  tls += ",cuda_copy"
87
87
 
88
88
  if ucx_config.get("infiniband"): # pragma: no cover
89
- tls = "rc," + tls
89
+ tls = "ib," + tls
90
90
  if ucx_config.get("nvlink"): # pragma: no cover
91
91
  tls += ",cuda_ipc"
92
92
 
@@ -177,7 +177,8 @@ class UCXInitializer:
177
177
  new_environ.update(envs)
178
178
  os.environ = new_environ # type: ignore
179
179
  try:
180
- ucp.init(options=options, env_takes_precedence=True)
180
+ # let UCX determine the appropriate transports
181
+ ucp.init()
181
182
  finally:
182
183
  os.environ = original_environ
183
184
 
@@ -313,7 +314,7 @@ class UCXChannel(Channel):
313
314
  await self.ucp_endpoint.send(buf)
314
315
  for buffer in buffers:
315
316
  await self.ucp_endpoint.send(buffer)
316
- except ucp.exceptions.UCXBaseException: # pragma: no cover
317
+ except ucp.exceptions.UCXError: # pragma: no cover
317
318
  self.abort()
318
319
  raise ChannelClosed("While writing, the connection was closed")
319
320
 
@@ -516,7 +517,7 @@ class UCXClient(Client):
516
517
 
517
518
  try:
518
519
  ucp_endpoint = await ucp.create_endpoint(host, port)
519
- except ucp.exceptions.UCXBaseException as e: # pragma: no cover
520
+ except ucp.exceptions.UCXError as e: # pragma: no cover
520
521
  raise ChannelClosed(
521
522
  f"Connection closed before handshake completed, "
522
523
  f"local address: {local_address}, dest address: {dest_address}"
@@ -70,50 +70,61 @@ class ActorCaller:
70
70
  return client
71
71
 
72
72
  async def _listen(self, client: Client):
73
- while not client.closed:
74
- try:
73
+ try:
74
+ while not client.closed:
75
75
  try:
76
- message: _MessageBase = await client.recv()
77
- except (EOFError, ConnectionError, BrokenPipeError):
78
- # remote server closed, close client and raise ServerClosed
79
76
  try:
80
- await client.close()
81
- except (ConnectionError, BrokenPipeError):
82
- # close failed, ignore it
77
+ message: _MessageBase = await client.recv()
78
+ except (EOFError, ConnectionError, BrokenPipeError) as e:
79
+ # AssertionError is from get_header
80
+ # remote server closed, close client and raise ServerClosed
81
+ logger.debug(f"{client.dest_address} close due to {e}")
82
+ try:
83
+ await client.close()
84
+ except (ConnectionError, BrokenPipeError):
85
+ # close failed, ignore it
86
+ pass
87
+ raise ServerClosed(
88
+ f"Remote server {client.dest_address} closed: {e}"
89
+ ) from None
90
+ future = self._client_to_message_futures[client].pop(
91
+ message.message_id
92
+ )
93
+ if not future.done():
94
+ future.set_result(message)
95
+ except DeserializeMessageFailed as e:
96
+ message_id = e.message_id
97
+ future = self._client_to_message_futures[client].pop(message_id)
98
+ future.set_exception(e.__cause__) # type: ignore
99
+ except Exception as e: # noqa: E722 # pylint: disable=bare-except
100
+ message_futures = self._client_to_message_futures[client]
101
+ self._client_to_message_futures[client] = dict()
102
+ for future in message_futures.values():
103
+ future.set_exception(copy.copy(e))
104
+ finally:
105
+ # message may have Ray ObjectRef, delete it early in case next loop doesn't run
106
+ # as soon as expected.
107
+ try:
108
+ del message
109
+ except NameError:
83
110
  pass
84
- raise ServerClosed(
85
- f"Remote server {client.dest_address} closed"
86
- ) from None
87
- future = self._client_to_message_futures[client].pop(message.message_id)
88
- if not future.done():
89
- future.set_result(message)
90
- except DeserializeMessageFailed as e:
91
- message_id = e.message_id
92
- future = self._client_to_message_futures[client].pop(message_id)
93
- future.set_exception(e.__cause__) # type: ignore
94
- except Exception as e: # noqa: E722 # pylint: disable=bare-except
95
- message_futures = self._client_to_message_futures[client]
96
- self._client_to_message_futures[client] = dict()
97
- for future in message_futures.values():
98
- future.set_exception(copy.copy(e))
99
- finally:
100
- # message may have Ray ObjectRef, delete it early in case next loop doesn't run
101
- # as soon as expected.
102
- try:
103
- del message
104
- except NameError:
105
- pass
106
- try:
107
- del future
108
- except NameError:
109
- pass
110
- await asyncio.sleep(0)
111
+ try:
112
+ del future
113
+ except NameError:
114
+ pass
115
+ await asyncio.sleep(0)
111
116
 
112
- message_futures = self._client_to_message_futures[client]
113
- self._client_to_message_futures[client] = dict()
114
- error = ServerClosed(f"Remote server {client.dest_address} closed")
115
- for future in message_futures.values():
116
- future.set_exception(copy.copy(error))
117
+ message_futures = self._client_to_message_futures[client]
118
+ self._client_to_message_futures[client] = dict()
119
+ error = ServerClosed(f"Remote server {client.dest_address} closed")
120
+ for future in message_futures.values():
121
+ future.set_exception(copy.copy(error))
122
+ finally:
123
+ try:
124
+ await client.close()
125
+ except: # noqa: E722 # nosec # pylint: disable=bare-except
126
+ # ignore all error if fail to close at last
127
+ pass
117
128
 
118
129
  async def call_with_client(
119
130
  self, client: Client, message: _MessageBase, wait: bool = True
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ import asyncio
16
17
  from enum import Enum
17
18
  from types import TracebackType
18
19
  from typing import Any, Type
@@ -21,7 +22,9 @@ from tblib import pickling_support
21
22
 
22
23
  from ..core cimport ActorRef, BufferRef
23
24
  from ..serialization.core cimport Serializer
25
+
24
26
  from ..utils import wrap_exception
27
+
25
28
  from .._utils cimport new_random_id
26
29
 
27
30
  # make sure traceback can be pickled
@@ -245,6 +248,11 @@ cdef class ErrorMessage(_MessageBase):
245
248
  if issubclass(self.error_type, _AsCauseBase):
246
249
  return self.error.with_traceback(self.traceback)
247
250
 
251
+ # for being compatible with Python 3.12 `asyncio.wait_for`
252
+ # https://github.com/python/cpython/pull/113850
253
+ if isinstance(self.error, asyncio.CancelledError):
254
+ return asyncio.CancelledError(f"[address={self.address}, pid={self.pid}]").with_traceback(self.traceback)
255
+
248
256
  return wrap_exception(
249
257
  self.error,
250
258
  (_AsCauseBase,),
@@ -41,7 +41,7 @@ from ..errors import (
41
41
  ServerClosed,
42
42
  )
43
43
  from ..metrics import init_metrics
44
- from ..utils import implements, register_asyncio_task_timeout_detector
44
+ from ..utils import implements, is_zero_ip, register_asyncio_task_timeout_detector
45
45
  from .allocate_strategy import AddressSpecified, allocated_type
46
46
  from .communication import (
47
47
  Channel,
@@ -164,7 +164,10 @@ class AbstractActorPool(ABC):
164
164
  ):
165
165
  # register local pool for local actor lookup.
166
166
  # The pool is weakrefed, so we don't need to unregister it.
167
- register_local_pool(external_address, self)
167
+ if not is_zero_ip(external_address):
168
+ # Only register_local_pool when we listen on non-zero ip (because all-zero ip is wildcard address),
169
+ # avoid mistaken with another remote service listen on non-zero ip with the same port.
170
+ register_local_pool(external_address, self)
168
171
  self.process_index = process_index
169
172
  self.label = label
170
173
  self.external_address = external_address
@@ -548,23 +551,31 @@ class AbstractActorPool(ABC):
548
551
  return False
549
552
 
550
553
  async def on_new_channel(self, channel: Channel):
551
- while not self._stopped.is_set():
552
- try:
553
- message = await channel.recv()
554
- except EOFError:
555
- # no data to read, check channel
554
+ try:
555
+ while not self._stopped.is_set():
556
556
  try:
557
- await channel.close()
558
- except (ConnectionError, EOFError):
559
- # close failed, ignore
560
- pass
561
- return
562
- if await self._handle_ucx_meta_message(message, channel):
563
- continue
564
- asyncio.create_task(self.process_message(message, channel))
565
- # delete to release the reference of message
566
- del message
567
- await asyncio.sleep(0)
557
+ message = await channel.recv()
558
+ except (EOFError, ConnectionError, BrokenPipeError) as e:
559
+ logger.debug(f"pool: close connection due to {e}")
560
+ # no data to read, check channel
561
+ try:
562
+ await channel.close()
563
+ except (ConnectionError, EOFError):
564
+ # close failed, ignore
565
+ pass
566
+ return
567
+ if await self._handle_ucx_meta_message(message, channel):
568
+ continue
569
+ asyncio.create_task(self.process_message(message, channel))
570
+ # delete to release the reference of message
571
+ del message
572
+ await asyncio.sleep(0)
573
+ finally:
574
+ try:
575
+ await channel.close()
576
+ except: # noqa: E722 # nosec # pylint: disable=bare-except
577
+ # ignore all error if fail to close at last
578
+ pass
568
579
 
569
580
  async def __aenter__(self):
570
581
  await self.start()
@@ -19,3 +19,5 @@ XOSCAR_TEMP_DIR = Path(os.getenv("XOSCAR_DIR", Path.home())) / ".xoscar"
19
19
 
20
20
  # unix socket.
21
21
  XOSCAR_UNIX_SOCKET_DIR = XOSCAR_TEMP_DIR / "socket"
22
+
23
+ XOSCAR_CONNECT_TIMEOUT = 8
@@ -77,7 +77,11 @@ MALFORMED_MSG = "Received malformed data, please check Xoscar version on both si
77
77
  def get_header_length(header_bytes: bytes):
78
78
  version = struct.unpack("B", header_bytes[:1])[0]
79
79
  # now we only have default version
80
- assert version == DEFAULT_SERIALIZATION_VERSION, MALFORMED_MSG
80
+ if version != DEFAULT_SERIALIZATION_VERSION:
81
+ # when version not matched,
82
+ # we will immediately abort the connection
83
+ # EOFError will be captured by channel
84
+ raise EOFError(MALFORMED_MSG)
81
85
  # header length
82
86
  header_length = struct.unpack("<Q", header_bytes[1:9])[0]
83
87
  # compress
@@ -55,7 +55,6 @@ from .pyfury import get_fury
55
55
 
56
56
  BUFFER_PICKLE_PROTOCOL = max(pickle.DEFAULT_PROTOCOL, 5)
57
57
  cdef bint HAS_PICKLE_BUFFER = pickle.HIGHEST_PROTOCOL >= 5
58
- cdef bint _PANDAS_HAS_MGR = hasattr(pd.Series([0]), "_mgr")
59
58
 
60
59
  cdef TypeDispatcher _serial_dispatcher = TypeDispatcher()
61
60
  cdef dict _deserializers = dict()
@@ -260,16 +259,7 @@ def unpickle_buffers(list buffers):
260
259
  else:
261
260
  result = cloudpickle.loads(buffers[0], buffers=buffers[1:])
262
261
 
263
- # as pandas prior to 1.1.0 use _data instead of _mgr to hold BlockManager,
264
- # deserializing from high versions may produce mal-functioned pandas objects,
265
- # thus the patch is needed
266
- if _PANDAS_HAS_MGR:
267
- return result
268
- else: # pragma: no cover
269
- if hasattr(result, "_mgr") and isinstance(result, (pd.DataFrame, pd.Series)):
270
- result._data = getattr(result, "_mgr")
271
- delattr(result, "_mgr")
272
- return result
262
+ return result
273
263
 
274
264
 
275
265
  cdef class PickleSerializer(Serializer):
@@ -19,17 +19,18 @@ import asyncio
19
19
  import dataclasses
20
20
  import functools
21
21
  import importlib
22
+ import importlib.util as importlib_utils
22
23
  import inspect
23
24
  import io
24
25
  import logging
25
26
  import os
26
- import pkgutil
27
27
  import random
28
28
  import socket
29
29
  import sys
30
30
  import time
31
31
  import uuid
32
32
  from abc import ABC
33
+ from functools import lru_cache
33
34
  from types import TracebackType
34
35
  from typing import Callable, Type, Union
35
36
 
@@ -266,7 +267,7 @@ def lazy_import(
266
267
  self._on_loads.append(func)
267
268
  return func
268
269
 
269
- if pkgutil.find_loader(prefix_name) is not None:
270
+ if importlib_utils.find_spec(prefix_name) is not None:
270
271
  return LazyModule()
271
272
  elif placeholder:
272
273
  return ModulePlaceholder(prefix_name)
@@ -464,6 +465,11 @@ def is_linux():
464
465
  return sys.platform.startswith("linux")
465
466
 
466
467
 
468
+ @lru_cache
469
+ def is_py_312():
470
+ return sys.version_info[:2] == (3, 12)
471
+
472
+
467
473
  def is_v4_zero_ip(ip_port_addr: str) -> bool:
468
474
  return ip_port_addr.split("://")[-1].startswith("0.0.0.0:")
469
475
 
@@ -480,6 +486,10 @@ def is_v6_zero_ip(ip_port_addr: str) -> bool:
480
486
  return True
481
487
 
482
488
 
489
+ def is_zero_ip(ip_port_addr: str) -> bool:
490
+ return is_v4_zero_ip(ip_port_addr) or is_v6_zero_ip(ip_port_addr)
491
+
492
+
483
493
  def is_v6_ip(ip_port_addr: str) -> bool:
484
494
  arr = ip_port_addr.split("://", 1)[-1].split(":")
485
495
  return len(arr) > 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xoscar
3
- Version: 0.3.3
3
+ Version: 0.4.1
4
4
  Summary: Python actor framework for heterogeneous computing.
5
5
  Home-page: http://github.com/xorbitsai/xoscar
6
6
  Author: Qin Xuye
@@ -11,10 +11,10 @@ License: Apache License 2.0
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Topic :: Software Development :: Libraries
20
20
  Description-Content-Type: text/markdown
@@ -29,7 +29,6 @@ xoscar.egg-info/not-zip-safe
29
29
  xoscar.egg-info/requires.txt
30
30
  xoscar.egg-info/top_level.txt
31
31
  xoscar/aio/__init__.py
32
- xoscar/aio/_threads.py
33
32
  xoscar/aio/base.py
34
33
  xoscar/aio/file.py
35
34
  xoscar/aio/lru.py
@@ -1,13 +1,10 @@
1
- numpy<2.0.0,>=1.14.0
1
+ numpy>=1.14.0
2
2
  pandas>=1.0.0
3
3
  cloudpickle>=1.5.0
4
4
  psutil>=5.9.0
5
5
  tblib>=1.7.0
6
6
  packaging
7
7
 
8
- [:python_version < "3.8"]
9
- pickle5
10
-
11
8
  [:sys_platform != "win32"]
12
9
  uvloop>=0.14.0
13
10
 
@@ -25,18 +22,15 @@ pytest-timeout>=1.2.0
25
22
  pytest-forked>=1.0
26
23
  pytest-asyncio>=0.14.0
27
24
  ipython>=6.5.0
28
- sphinx<5.0.0,>=3.0.0
25
+ sphinx
29
26
  pydata-sphinx-theme>=0.3.0
30
27
  sphinx-intl>=0.9.9
31
28
  flake8>=3.8.0
32
29
  black
33
30
 
34
- [dev:python_version < "3.8"]
35
- mock>=4.0.0
36
-
37
31
  [doc]
38
32
  ipython>=6.5.0
39
- sphinx<5.0.0,>=3.0.0
33
+ sphinx
40
34
  pydata-sphinx-theme>=0.3.0
41
35
  sphinx-intl>=0.9.9
42
36
 
@@ -1,25 +0,0 @@
1
- # Copyright 2022-2023 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
- import asyncio
16
- import sys
17
-
18
- from .file import AioFileObject
19
- from .lru import alru_cache
20
- from .parallelism import AioEvent
21
-
22
- if sys.version_info[:2] < (3, 9):
23
- from ._threads import to_thread
24
-
25
- asyncio.to_thread = to_thread
@@ -1,35 +0,0 @@
1
- # Copyright 2022-2023 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
- import contextvars
16
- import functools
17
- from asyncio import events
18
-
19
- __all__ = ("to_thread",)
20
-
21
-
22
- async def to_thread(func, *args, **kwargs):
23
- """Asynchronously run function *func* in a separate thread.
24
-
25
- Any *args and **kwargs supplied for this function are directly passed
26
- to *func*. Also, the current :class:`contextvars.Context` is propagated,
27
- allowing context variables from the main thread to be accessed in the
28
- separate thread.
29
-
30
- Return a coroutine that can be awaited to get the eventual result of *func*.
31
- """
32
- loop = events.get_running_loop()
33
- ctx = contextvars.copy_context()
34
- func_call = functools.partial(ctx.run, func, *args, **kwargs)
35
- return await loop.run_in_executor(None, func_call)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes