I\'m trying to implement an event driven process with system call or subprocess. Basically I want to launch a non-blocking system command and upon completion of that system
To avoid polling subprocess' status, you could use SIGCHLD
signal on Unix. To combine it with tkinter's event loop, you could use the self-pipe trick. It also workarounds the possible tkinter + signal issue without the need to wake the event loop periodically.
#!/usr/bin/env python3
import logging
import os
import signal
import subprocess
import tkinter
info = logging.getLogger(__name__).info
def on_signal(pipe, mask, count=[0]):
try:
signals = os.read(pipe, 512)
except BlockingIOError:
return # signals have been already dealt with
# from asyncio/unix_events.py
#+start
# Because of signal coalescing, we must keep calling waitpid() as
# long as we're able to reap a child.
while True:
try:
pid, status = os.waitpid(-1, os.WNOHANG)
except ChildProcessError:
info('No more child processes exist.')
return
else:
if pid == 0:
info('A child process is still alive. signals=%r%s',
signals, ' SIGCHLD'*(any(signum == signal.SIGCHLD
for signum in signals)))
return
#+end
# you could call your callback here
info('{pid} child exited with status {status}'.format(**vars()))
count[0] += 1
if count[0] == 2:
root.destroy() # exit GUI
logging.basicConfig(format="%(asctime)-15s %(message)s", datefmt='%F %T',
level=logging.INFO)
root = tkinter.Tk()
root.withdraw() # hide GUI
r, w = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
signal.set_wakeup_fd(w)
root.createfilehandler(r, tkinter.READABLE, on_signal)
signal.signal(signal.SIGCHLD, lambda signum, frame: None) # enable SIGCHLD
signal.siginterrupt(signal.SIGCHLD, False) # restart interrupted syscalls automatically
info('run children')
p = subprocess.Popen('sleep 4', shell=True)
subprocess.Popen('sleep 1', shell=True)
root.after(2000, p.send_signal, signal.SIGSTOP) # show that SIGCHLD may be delivered
root.after(3000, p.send_signal, signal.SIGCONT) # while the child is still alive
root.after(5000, lambda: p.poll() is None and p.kill()) # kill it
root.mainloop()
info('done')
2015-05-20 23:39:50 run children
2015-05-20 23:39:51 16991 child exited with status 0
2015-05-20 23:39:51 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:52 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:53 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:54 16989 child exited with status 0
2015-05-20 23:39:54 No more child processes exist.
2015-05-20 23:39:54 done