xoscar 0.6.2__cp312-cp312-macosx_11_0_arm64.whl → 0.7.0__cp312-cp312-macosx_11_0_arm64.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.

Binary file
xoscar/_utils.pyx CHANGED
@@ -213,7 +213,8 @@ def create_actor_ref(*args, **kwargs):
213
213
  if kwargs:
214
214
  raise ValueError('Only `address` or `uid` keywords are supported')
215
215
 
216
- if 2 <= len(args) <= 3:
216
+ # args: [address, uid], or [address, uid, proxy_addresses]
217
+ if 2 <= len(args) <= 3 and isinstance(args[0], (str, bytes)):
217
218
  if address:
218
219
  raise ValueError('address has been specified')
219
220
  address = to_str(args[0])
@@ -0,0 +1,19 @@
1
+ if __name__ == "__main__":
2
+ import click
3
+
4
+ @click.group(
5
+ invoke_without_command=True,
6
+ name="xoscar",
7
+ help="Xoscar command-line interface.",
8
+ )
9
+ def main():
10
+ pass
11
+
12
+ @main.command("start_sub_pool", help="Start a sub pool.")
13
+ @click.option("shm_name", "-sn", type=str, help="Shared memory name.")
14
+ def start_sub_pool(shm_name):
15
+ from xoscar.backends.indigen.pool import MainActorPool
16
+
17
+ MainActorPool._start_sub_pool_in_child(shm_name)
18
+
19
+ main()
@@ -0,0 +1,221 @@
1
+ # Copyright 2017 The Ray Authors.
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 logging
17
+ import subprocess
18
+ import sys
19
+
20
+ import psutil
21
+
22
+ # Linux can bind child processes' lifetimes to that of their parents via prctl.
23
+ # prctl support is detected dynamically once, and assumed thereafter.
24
+ linux_prctl = None
25
+
26
+ # Windows can bind processes' lifetimes to that of kernel-level "job objects".
27
+ # We keep a global job object to tie its lifetime to that of our own process.
28
+ win32_job = None
29
+ win32_AssignProcessToJobObject = None
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ def detect_fate_sharing_support_win32():
35
+ global win32_job, win32_AssignProcessToJobObject
36
+ if win32_job is None and sys.platform == "win32":
37
+ import ctypes
38
+
39
+ try:
40
+ from ctypes.wintypes import BOOL, DWORD, HANDLE, LPCWSTR, LPVOID
41
+
42
+ kernel32 = ctypes.WinDLL("kernel32")
43
+ kernel32.CreateJobObjectW.argtypes = (LPVOID, LPCWSTR)
44
+ kernel32.CreateJobObjectW.restype = HANDLE
45
+ sijo_argtypes = (HANDLE, ctypes.c_int, LPVOID, DWORD)
46
+ kernel32.SetInformationJobObject.argtypes = sijo_argtypes
47
+ kernel32.SetInformationJobObject.restype = BOOL
48
+ kernel32.AssignProcessToJobObject.argtypes = (HANDLE, HANDLE)
49
+ kernel32.AssignProcessToJobObject.restype = BOOL
50
+ kernel32.IsDebuggerPresent.argtypes = ()
51
+ kernel32.IsDebuggerPresent.restype = BOOL
52
+ except (AttributeError, TypeError, ImportError):
53
+ kernel32 = None
54
+ job = kernel32.CreateJobObjectW(None, None) if kernel32 else None
55
+ job = subprocess.Handle(job) if job else job
56
+ if job:
57
+ from ctypes.wintypes import DWORD, LARGE_INTEGER, ULARGE_INTEGER
58
+
59
+ class JOBOBJECT_BASIC_LIMIT_INFORMATION(ctypes.Structure):
60
+ _fields_ = [
61
+ ("PerProcessUserTimeLimit", LARGE_INTEGER),
62
+ ("PerJobUserTimeLimit", LARGE_INTEGER),
63
+ ("LimitFlags", DWORD),
64
+ ("MinimumWorkingSetSize", ctypes.c_size_t),
65
+ ("MaximumWorkingSetSize", ctypes.c_size_t),
66
+ ("ActiveProcessLimit", DWORD),
67
+ ("Affinity", ctypes.c_size_t),
68
+ ("PriorityClass", DWORD),
69
+ ("SchedulingClass", DWORD),
70
+ ]
71
+
72
+ class IO_COUNTERS(ctypes.Structure):
73
+ _fields_ = [
74
+ ("ReadOperationCount", ULARGE_INTEGER),
75
+ ("WriteOperationCount", ULARGE_INTEGER),
76
+ ("OtherOperationCount", ULARGE_INTEGER),
77
+ ("ReadTransferCount", ULARGE_INTEGER),
78
+ ("WriteTransferCount", ULARGE_INTEGER),
79
+ ("OtherTransferCount", ULARGE_INTEGER),
80
+ ]
81
+
82
+ class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(ctypes.Structure):
83
+ _fields_ = [
84
+ ("BasicLimitInformation", JOBOBJECT_BASIC_LIMIT_INFORMATION),
85
+ ("IoInfo", IO_COUNTERS),
86
+ ("ProcessMemoryLimit", ctypes.c_size_t),
87
+ ("JobMemoryLimit", ctypes.c_size_t),
88
+ ("PeakProcessMemoryUsed", ctypes.c_size_t),
89
+ ("PeakJobMemoryUsed", ctypes.c_size_t),
90
+ ]
91
+
92
+ debug = kernel32.IsDebuggerPresent()
93
+
94
+ # Defined in <WinNT.h>; also available here:
95
+ # https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-setinformationjobobject
96
+ JobObjectExtendedLimitInformation = 9
97
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
98
+ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400
99
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
100
+ buf = JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
101
+ buf.BasicLimitInformation.LimitFlags = (
102
+ (0 if debug else JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)
103
+ | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
104
+ | JOB_OBJECT_LIMIT_BREAKAWAY_OK
105
+ )
106
+ infoclass = JobObjectExtendedLimitInformation
107
+ if not kernel32.SetInformationJobObject(
108
+ job, infoclass, ctypes.byref(buf), ctypes.sizeof(buf)
109
+ ):
110
+ job = None
111
+ win32_AssignProcessToJobObject = (
112
+ kernel32.AssignProcessToJobObject if kernel32 is not None else False
113
+ )
114
+ win32_job = job if job else False
115
+ return bool(win32_job)
116
+
117
+
118
+ def detect_fate_sharing_support_linux():
119
+ global linux_prctl
120
+ if linux_prctl is None and sys.platform.startswith("linux"):
121
+ try:
122
+ from ctypes import CDLL, c_int, c_ulong
123
+
124
+ prctl = CDLL(None).prctl
125
+ prctl.restype = c_int
126
+ prctl.argtypes = [c_int, c_ulong, c_ulong, c_ulong, c_ulong]
127
+ except (AttributeError, TypeError):
128
+ prctl = None
129
+ linux_prctl = prctl if prctl else False
130
+ return bool(linux_prctl)
131
+
132
+
133
+ def detect_fate_sharing_support():
134
+ result = None
135
+ if sys.platform == "win32":
136
+ result = detect_fate_sharing_support_win32()
137
+ elif sys.platform.startswith("linux"):
138
+ result = detect_fate_sharing_support_linux()
139
+ return result
140
+
141
+
142
+ if detect_fate_sharing_support():
143
+ logger.info("Using kernel-level fate-sharing.")
144
+
145
+
146
+ def set_kill_on_parent_death_linux():
147
+ """Ensures this process dies if its parent dies (fate-sharing).
148
+
149
+ Linux-only. Must be called in preexec_fn (i.e. by the child).
150
+ """
151
+ if detect_fate_sharing_support_linux():
152
+ import signal
153
+
154
+ PR_SET_PDEATHSIG = 1
155
+ if linux_prctl(PR_SET_PDEATHSIG, signal.SIGKILL, 0, 0, 0) != 0:
156
+ import ctypes
157
+
158
+ raise OSError(ctypes.get_errno(), "prctl(PR_SET_PDEATHSIG) failed")
159
+ else:
160
+ assert False, "PR_SET_PDEATHSIG used despite being unavailable"
161
+
162
+
163
+ def set_kill_child_on_death_win32(child_proc):
164
+ """Ensures the child process dies if this process dies (fate-sharing).
165
+
166
+ Windows-only. Must be called by the parent, after spawning the child.
167
+
168
+ Args:
169
+ child_proc: The subprocess.Popen or subprocess.Handle object.
170
+ """
171
+
172
+ if isinstance(child_proc, subprocess.Popen):
173
+ child_proc = child_proc._handle
174
+ assert isinstance(child_proc, subprocess.Handle)
175
+
176
+ if detect_fate_sharing_support_win32():
177
+ if not win32_AssignProcessToJobObject(win32_job, int(child_proc)):
178
+ import ctypes
179
+
180
+ raise OSError(ctypes.get_last_error(), "AssignProcessToJobObject() failed")
181
+ else:
182
+ assert False, "AssignProcessToJobObject used despite being unavailable"
183
+
184
+
185
+ def preexec_fn():
186
+ import signal
187
+
188
+ signal.pthread_sigmask(signal.SIG_BLOCK, {signal.SIGINT})
189
+ if sys.platform.startswith("linux"):
190
+ set_kill_on_parent_death_linux()
191
+
192
+
193
+ async def create_subprocess_exec(*args, **kwargs):
194
+ win32 = sys.platform == "win32"
195
+ # With Windows fate-sharing, we need special care:
196
+ # The process must be added to the job before it is allowed to execute.
197
+ # Otherwise, there's a race condition: the process might spawn children
198
+ # before the process itself is assigned to the job.
199
+ # After that point, its children will not be added to the job anymore.
200
+ CREATE_SUSPENDED = 0x00000004 # from Windows headers
201
+ creationflags = CREATE_SUSPENDED if win32 else 0
202
+ if win32 and detect_fate_sharing_support_win32():
203
+ creationflags |= subprocess.CREATE_NEW_PROCESS_GROUP
204
+ # CREATE_NEW_PROCESS_GROUP is used to send Ctrl+C on Windows:
205
+ # https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal
206
+ process: asyncio.subprocess.Process = await asyncio.create_subprocess_exec(
207
+ *args,
208
+ **kwargs,
209
+ preexec_fn=preexec_fn if not win32 else None,
210
+ creationflags=creationflags,
211
+ )
212
+ if win32:
213
+ proc = process._transport._proc
214
+ try:
215
+ set_kill_child_on_death_win32(proc)
216
+ psutil.Process(process.pid).resume()
217
+ except (psutil.Error, OSError):
218
+ logger.exception("Resume process failed, kill %s.", process.pid)
219
+ process.kill()
220
+ raise
221
+ return process