11import  asyncio 
22import  logging 
33import  os 
4- import  signal 
4+ import  subprocess 
55import  uuid 
66from  abc  import  ABC , abstractmethod 
77from  dataclasses  import  dataclass 
88from  typing  import  Any , AsyncGenerator , Optional 
99
10- import  psutil 
1110import  pytest_asyncio 
1211
1312import  temporalio .api .workflowservice .v1 
@@ -60,15 +59,11 @@ async def start() -> "ExternalGolangServer":
6059        namespace  =  f"test-namespace-{ uuid .uuid4 ()}  
6160        # TODO(cretz): Make this configurable? 
6261        port  =  "9233" 
63-         process  =  await  asyncio .create_subprocess_exec (
64-             "go" ,
65-             "run" ,
66-             "." ,
62+         process  =  await  start_external_go_process (
63+             os .path .join (os .path .dirname (__file__ ), "golangserver" ),
64+             "golangserver" ,
6765            port ,
6866            namespace ,
69-             cwd = os .path .abspath (
70-                 os .path .join (os .path .dirname (__file__ ), "golangserver" )
71-             ),
7267        )
7368        server  =  ExternalGolangServer (f"localhost:{ port }  , namespace , process )
7469        # Try to get the client multiple times to check whether server is online 
@@ -99,7 +94,7 @@ def namespace(self) -> str:
9994        return  self ._namespace 
10095
10196    async  def  close (self ):
102-         kill_proc_tree ( self ._process .pid )
97+         self ._process .terminate ( )
10398        await  self ._process .wait ()
10499
105100
@@ -143,19 +138,15 @@ async def close(self):
143138class  ExternalGolangWorker (Worker ):
144139    @staticmethod  
145140    async  def  start (host_port : str , namespace : str ) ->  "ExternalGolangWorker" :
146-         task_queue  =  uuid .uuid4 ()
147-         process  =  await  asyncio .create_subprocess_exec (
148-             "go" ,
149-             "run" ,
150-             "." ,
141+         task_queue  =  str (uuid .uuid4 ())
142+         process  =  await  start_external_go_process (
143+             os .path .join (os .path .dirname (__file__ ), "golangworker" ),
144+             "golangworker" ,
151145            host_port ,
152146            namespace ,
153-             str (task_queue ),
154-             cwd = os .path .abspath (
155-                 os .path .join (os .path .dirname (__file__ ), "golangworker" )
156-             ),
147+             task_queue ,
157148        )
158-         return  ExternalGolangWorker (str ( task_queue ) , process )
149+         return  ExternalGolangWorker (task_queue , process )
159150
160151    def  __init__ (self , task_queue : str , process : asyncio .subprocess .Process ) ->  None :
161152        super ().__init__ ()
@@ -167,7 +158,7 @@ def task_queue(self) -> str:
167158        return  self ._task_queue 
168159
169160    async  def  close (self ):
170-         kill_proc_tree ( self ._process .pid )
161+         self ._process .terminate ( )
171162        await  self ._process .wait ()
172163
173164
@@ -178,24 +169,14 @@ async def worker(server: Server) -> AsyncGenerator[Worker, None]:
178169    await  worker .close ()
179170
180171
181- # See https://psutil.readthedocs.io/en/latest/#kill-process-tree 
182- def  kill_proc_tree (
183-     pid , sig = signal .SIGTERM , include_parent = True , timeout = None , on_terminate = None 
184- ):
185-     """Kill a process tree (including grandchildren) with signal 
186-     "sig" and return a (gone, still_alive) tuple. 
187-     "on_terminate", if specified, is a callback function which is 
188-     called as soon as a child terminates. 
189-     """ 
190-     assert  pid  !=  os .getpid (), "won't kill myself" 
191-     parent  =  psutil .Process (pid )
192-     children  =  parent .children (recursive = True )
193-     if  include_parent :
194-         children .append (parent )
195-     for  p  in  children :
196-         try :
197-             p .send_signal (sig )
198-         except  psutil .NoSuchProcess :
199-             pass 
200-     gone , alive  =  psutil .wait_procs (children , timeout = timeout , callback = on_terminate )
201-     return  (gone , alive )
172+ async  def  start_external_go_process (
173+     source_dir : str , exe_name : str , * args : str 
174+ ) ->  asyncio .subprocess .Process :
175+     # First, build the executable. We accept the performance issues of building 
176+     # this each run. 
177+     logger .info ("Building %s" , exe_name )
178+     subprocess .run (["go" , "build" , "-o" , exe_name , "." ], cwd = source_dir , check = True )
179+     logger .info ("Starting %s" , exe_name )
180+     return  await  asyncio .create_subprocess_exec (
181+         os .path .join (source_dir , exe_name ), * args 
182+     )
0 commit comments