@@ -85,34 +85,54 @@ async def send_signal(self, signum: int) -> None:
8585 os .killpg (self .pgid , signum )
8686 return
8787 except OSError :
88- pass
89- try :
90- self .process .send_signal (signum )
91- except OSError :
92- pass
88+ pass # We'll retry sending the signal to only the process below
89+
90+ # If we're here, send the signal to the process and let caller handle exceptions
91+ self .process .send_signal (signum )
9392 return
9493
9594 async def kill (self , restart : bool = False ) -> None :
9695 if self .process :
96+ if hasattr (signal , "SIGKILL" ):
97+ # If available, give preference to signalling the process-group over `kill()`.
98+ try :
99+ await self .send_signal (signal .SIGKILL )
100+ return
101+ except OSError :
102+ pass
97103 try :
98104 self .process .kill ()
99105 except OSError as e :
100- # In Windows, we will get an Access Denied error if the process
101- # has already terminated. Ignore it.
102- if sys .platform == 'win32' :
103- if e .winerror != 5 :
104- raise
105- # On Unix, we may get an ESRCH error if the process has already
106- # terminated. Ignore it.
107- else :
108- from errno import ESRCH
109-
110- if e .errno != ESRCH :
111- raise
106+ LocalProvisioner ._tolerate_no_process (e )
112107
113108 async def terminate (self , restart : bool = False ) -> None :
114109 if self .process :
115- return self .process .terminate ()
110+ if hasattr (signal , "SIGTERM" ):
111+ # If available, give preference to signalling the process group over `terminate()`.
112+ try :
113+ await self .send_signal (signal .SIGTERM )
114+ return
115+ except OSError :
116+ pass
117+ try :
118+ self .process .terminate ()
119+ except OSError as e :
120+ LocalProvisioner ._tolerate_no_process (e )
121+
122+ @staticmethod
123+ def _tolerate_no_process (os_error : OSError ):
124+ # In Windows, we will get an Access Denied error if the process
125+ # has already terminated. Ignore it.
126+ if sys .platform == 'win32' :
127+ if os_error .winerror != 5 :
128+ raise
129+ # On Unix, we may get an ESRCH error (or ProcessLookupError instance) if
130+ # the process has already terminated. Ignore it.
131+ else :
132+ from errno import ESRCH
133+
134+ if not isinstance (os_error , ProcessLookupError ) or os_error .errno != ESRCH :
135+ raise
116136
117137 async def cleanup (self , restart : bool = False ) -> None :
118138 if self .ports_cached and not restart :
0 commit comments