Reputation: 35149
I need to run a unittest
on part of my package, and the best thing for me to do this is to control launching it and then killing all processes spawned by multiprocessing
module.
Here is what I'm talking about:
import logging
import multiprocessing
import time
import random
log = logging.getLogger(__name__)
def start_consumers(conf, worker_count=5):
manager = WorkerManager(conf, worker_count)
manager.start()
class WorkerManager():
def __init__(self, conf, worker_count=5):
self.workers = []
for num in range(worker_count):
self.workers.append(WorkHandler(conf))
def start(self):
for worker in self.workers:
worker.daemon = True
worker.start()
print 'started'
for worker in self.workers:
worker.join()
class WorkHandler(multiprocessing.Process):
def __init__(self, conf, *args, **kwargs):
super(WorkHandler, self).__init__(*args, **kwargs)
self.conf = conf
self.name = str(random.randint(0,100))
def run(self):
while True:
print self.conf['foo'], self.name
time.sleep(3)
if __name__ == "__main__":
conf = {'foo': 'bar'}
start_consumers(conf)
Now if I run this test from linux terminal, I can see print statements, also if i do:
ps aux | grep python
I see all child processes where spawned:
sergey 4081 0.0 0.1 71684 13164 pts/3 S+ 08:58 0:00 python
sergey 4108 0.3 0.0 39092 6472 pts/3 S+ 08:59 0:00 python test.py
sergey 4109 0.0 0.0 39092 4576 pts/3 S+ 08:59 0:00 python test.py
sergey 4110 0.0 0.0 39092 4568 pts/3 S+ 08:59 0:00 python test.py
sergey 4111 0.0 0.0 39092 4576 pts/3 S+ 08:59 0:00 python test.py
sergey 4112 0.0 0.0 39092 4584 pts/3 S+ 08:59 0:00 python test.py
sergey 4113 0.0 0.0 39092 4580 pts/3 S+ 08:59 0:00 python test.py
sergey 4115 0.0 0.0 13588 944 pts/7 S+ 08:59 0:00 grep --color=auto python
Now if I'm trying to run the same test.py
with subprocess
everything works fine up to the point until I need to kill them all:
>>> import subprocess as s
>>> p = s.Popen(['python', 'test.py'], stdout=s.PIPE)
What is the most elegant way to do something with this variable p
in order to achieve similar behaviour when I press Ctrl+C
in terminal?
In other words I want to achive a result similar to pkill -f "test.py"
p.kill()
or p.terminate()
doesn't do what I need:
Here is the result after I do eather of them:
sergey 4438 0.0 0.0 0 0 pts/3 Z+ 09:16 0:00 [python] <defunct>
sergey 4439 0.0 0.0 39092 4572 pts/3 S+ 09:16 0:00 python test.py
sergey 4440 0.0 0.0 39092 4568 pts/3 S+ 09:16 0:00 python test.py
sergey 4441 0.0 0.0 39092 4576 pts/3 S+ 09:16 0:00 python test.py
sergey 4442 0.0 0.0 39092 4580 pts/3 S+ 09:16 0:00 python test.py
sergey 4443 0.0 0.0 39092 4580 pts/3 S+ 09:16 0:00 python test.py
Upvotes: 2
Views: 3014
Reputation: 414745
You could try to create a new session and kill all descendant processes using os.killpg()
:
import os
import signal
from subprocess import Popen
p = Popen('python test.py', shell=True, preexec_fn=os.setsid)
# ... later
os.killpg(p.pid, signal.SIGTERM) # send signal to the process group
See How to terminate a python subprocess launched with shell=True.
Upvotes: 1
Reputation: 9105
I believe what you're looking for is the subprocess.terminate()
and subprocess.kill()
methods of subprocess objects.
You will likely have to combine that with a method registered with atexit that iterates across your list of subprocesses and terminates them. EX:
def terminate_children(children):
for process in children:
process.terminate()
...
# Somewhere else in your code
children = [s.Popen(['python', 'test.py'], stdout=s.PIPE) for i in range(number_processes)] # Spools up number_processes child processes
atexit.register(terminate_children, children) # where children is a list of subprocesses
This will terminate all child processes gracefully when you terminate the parent process gracefully.
If you're trying to kill the processes from a completely separate script with no direct reference to the child code, see here. You'll basically want to look at the python os
process management methods
Upvotes: 2