Stopping Python program from Supervisor

Arseni Mourzenko
Founder and lead developer
176
articles
September 16, 2023
Tags: python 5 supervisor 1

I was quite puz­zled re­cent­ly by a fol­low­ing prob­lem. I need­ed to run a Python pro­gram that had an in­fi­nite loop: every N sec­onds, the script would per­form a task. In or­der to en­sure that the script would be restart­ed au­to­mat­i­cal­ly if it failed, I was us­ing Su­per­vi­sor.

As the pro­gram was run­ning in a vir­tu­al en­vi­ron­ment, Su­per­vi­sor would not call di­rect­ly it, but rather a Bash script, that would pre­pare the vir­tu­al en­vi­ron­ment, and then start the pro­gram.

It ap­peared, how­ev­er, that supervisorctl stop all or service supervisor stop would not ac­tu­al­ly stop the Python pro­gram—it would con­tin­ue to run un­at­tend­ed. And, nat­u­ral­ly, if I would start Su­per­vi­sor, it would pop up an­oth­er in­stance of the pro­gram.

Af­ter scratch­ing my head a bit, I found that the is­sue was the in­ter­me­di­ary Bash script. When re­ceiv­ing SIGTERM sig­nal, it would sim­ply stop, while the pro­gram would con­tin­ue to run.

Here's the cor­rect way to do it. First, the pro­gram it­self:

#!/usr/bin/env python

import signal
import time

if __name__ == '__main__':
    should_run = True

    def stop(_, _2):
        print("Should stop.", flush=True)
        global should_run
        should_run = False

    signal.signal(signal.SIGTERM, stop)

    while should_run:
        print("Still running.", flush=True)
        time.sleep(5)

The Bash script:

#!/bin/bash

source .venv/bin/activate

term() {
    kill -TERM "$child"
}

trap term SIGTERM

./example.py &

child=$!
wait "$child"

Note that to keep them sim­ple, those pieces of code han­dle only the SIGTERM sig­nal. This means that if one runs the Bash script from ter­mi­nal, Ctrl+C will stop only the Bash script, while Python pro­gram will still go on.