@@ -65,6 +65,45 @@ def _terminate_process(proc: subprocess.Popen[bytes]) -> None:
6565 pass
6666
6767
68+ def _terminate_process_async_atexit (proc : asyncio .subprocess .Process ) -> None :
69+ if proc .returncode is not None :
70+ return
71+
72+ try :
73+ if sys .platform != "win32" :
74+ os .killpg (proc .pid , signal .SIGTERM )
75+ else :
76+ proc .terminate ()
77+ except Exception :
78+ pass
79+
80+
81+ async def _terminate_process_async (proc : asyncio .subprocess .Process ) -> None :
82+ if proc .returncode is not None :
83+ return
84+
85+ try :
86+ if sys .platform != "win32" :
87+ os .killpg (proc .pid , signal .SIGTERM )
88+ else :
89+ proc .terminate ()
90+ await asyncio .wait_for (proc .wait (), timeout = 3 )
91+ return
92+ except Exception :
93+ pass
94+
95+ try :
96+ if sys .platform != "win32" :
97+ os .killpg (proc .pid , signal .SIGKILL )
98+ else :
99+ proc .kill ()
100+ finally :
101+ try :
102+ await asyncio .wait_for (proc .wait (), timeout = 3 )
103+ except Exception :
104+ pass
105+
106+
68107def _wait_ready_sync (* , base_url : str , timeout_s : float ) -> None :
69108 deadline = time .monotonic () + timeout_s
70109 with httpx .Client (timeout = 1.0 ) as client :
@@ -111,6 +150,7 @@ def __init__(
111150 self ._async_lock = asyncio .Lock ()
112151
113152 self ._proc : subprocess .Popen [bytes ] | None = None
153+ self ._async_proc : asyncio .subprocess .Process | None = None
114154 self ._base_url : str | None = None
115155 self ._atexit_registered : bool = False
116156
@@ -150,12 +190,12 @@ def ensure_running_sync(self) -> str:
150190
151191 async def ensure_running_async (self ) -> str :
152192 async with self ._async_lock :
153- if self ._proc is not None and self ._proc . poll () is None and self ._base_url is not None :
193+ if self ._async_proc is not None and self ._async_proc . returncode is None and self ._base_url is not None :
154194 return self ._base_url
155195
156196 base_url , proc = await self ._start_async ()
157197 self ._base_url = base_url
158- self ._proc = proc
198+ self ._async_proc = proc
159199 return base_url
160200
161201 def close (self ) -> None :
@@ -174,10 +214,10 @@ async def aclose(self) -> None:
174214 return
175215
176216 async with self ._async_lock :
177- if self ._proc is None :
217+ if self ._async_proc is None :
178218 return
179- _terminate_process (self ._proc )
180- self ._proc = None
219+ await _terminate_process_async (self ._async_proc )
220+ self ._async_proc = None
181221 self ._base_url = None
182222
183223 def _start_sync (self ) -> tuple [str , subprocess .Popen [bytes ]]:
@@ -219,7 +259,7 @@ def _start_sync(self) -> tuple[str, subprocess.Popen[bytes]]:
219259
220260 return base_url , proc
221261
222- async def _start_async (self ) -> tuple [str , subprocess .Popen [ bytes ] ]:
262+ async def _start_async (self ) -> tuple [str , asyncio . subprocess .Process ]:
223263 if not self ._binary_path .exists ():
224264 raise FileNotFoundError (
225265 f"Stagehand SEA binary not found at { self ._binary_path } . "
@@ -230,30 +270,22 @@ async def _start_async(self) -> tuple[str, subprocess.Popen[bytes]]:
230270 base_url = _build_base_url (host = self ._config .host , port = port )
231271 proc_env = self ._build_process_env (port = port )
232272
233- preexec_fn = None
234- creationflags = 0
235- if sys .platform != "win32" :
236- preexec_fn = os .setsid
237- else :
238- creationflags = subprocess .CREATE_NEW_PROCESS_GROUP
239-
240- proc = subprocess .Popen (
241- [str (self ._binary_path )],
273+ proc = await asyncio .create_subprocess_exec (
274+ str (self ._binary_path ),
242275 env = proc_env ,
243276 stdout = None ,
244277 stderr = None ,
245- preexec_fn = preexec_fn ,
246- creationflags = creationflags ,
278+ start_new_session = True ,
247279 )
248280
249281 if not self ._atexit_registered :
250- atexit .register (_terminate_process , proc )
282+ atexit .register (_terminate_process_async_atexit , proc )
251283 self ._atexit_registered = True
252284
253285 try :
254286 await _wait_ready_async (base_url = base_url , timeout_s = self ._config .ready_timeout_s )
255287 except Exception :
256- _terminate_process (proc )
288+ await _terminate_process_async (proc )
257289 raise
258290
259291 return base_url , proc
0 commit comments