How to terminate a python process and enter the finally block in python try-except statement?

Viewed 249

I have two python files: script.py and counting.py.

counting.py

from time import sleep

try:
    for n in range(1, 10):
        print('counting', n)
        sleep(1)
except Exception as e:
    print('program terminated with exception', e)
else:
    print('program ended')
finally:
    sleep(4)
    print('this is the finally block')

script.py

import sys
import pathlib
import subprocess
import signal


on_windows = True if sys.platform == 'win32' else False
on_linux = True if sys.platform == 'linux' else False

if on_windows:
    process = subprocess.Popen(
        ['python', 'counting.py'],
        cwd=pathlib.Path(__file__).parent.resolve(),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True,
        creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
    )
elif on_linux:
    process = subprocess.Popen(
        ['python', 'counting.py'],
        cwd=pathlib.Path(__file__).parent.resolve(),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True,
        preexec_fn=os.setsid,
    )
try:
    out, err = process.communicate(timeout=4)
    print(out, err)
except subprocess.TimeoutExpired:
    if on_windows:
        process.send_signal(signal.CTRL_BREAK_EVENT)
        process.kill()
    elif on_linux:
        os.killpg(process.pid, signal.SIGTERM)
    print('time out')

When I run script.py the output is:

time out

Process returned 0 (0x0)        execution time : 4.778 s

How can I terminate the process, and enter the finally block in the counting.py file?

P.S. When I run counting.py in the terminal, the program enters the finally block, after a CTRL+C on the keyboard:

>>python counting.py
counting 1
counting 2
counting 3
this is the finally block
Traceback (most recent call last):
  File "counting.py", line 6, in <module>
    sleep(1)
KeyboardInterrupt
^C
>>

How can I achieve this using the subprocess module and killing the child process? I have tested the above codes in Windows (Windows 10) and Linux (CentOS 8).

1 Answers

First of all, counting.py won't print anything unless you let it emit something to stdout:

process = subprocess.Popen(
    ['python', 'counting.py'],
    cwd=pathlib.Path(__file__).parent.resolve(),
    stdout=None,
    stderr=None,
    universal_newlines=True,
    preexec_fn=os.setsid,
)

Second, your exception handler in script.py doesn't get called, because it's not a timeout exception, so change to:

try:
    out, err = process.communicate(timeout=4)
    print(out, err)
except:

Third, at least on Linux, os.killpg() will terminate the process rather than interrupt it, so the exception handler in counting.py wouldn't be called. Instead of SIGTERM, use:

elif on_linux:
    os.killpg(process.pid, signal.SIGINT)

With those 2 changes:

$ ./script.py 
counting 1
counting 2
time out
$ this is the finally block
Traceback (most recent call last):
  File "counting.py", line 8, in <module>
    sleep(1)
KeyboardInterrupt
Related