How to Mock a SIGTERM Handler in Python

Create a graceful shutdown handler for SIGTERM and SIGINT signals, then test it by simulating a signal delivery without terminating the process.

Easy Python 3.9+ Aug 9, 2026 Production deployment patterns 13 views 0 copies

Python code

31 lines
Python 3.9+
import signal
import time

class Service:
    def __init__(self):
        self.running = True

    def shutdown(self, signum, frame):
        print(f"Received signal {signum}, shutting down gracefully...")
        self.running = False

    def run(self):
        signal.signal(signal.SIGTERM, self.shutdown)
        signal.signal(signal.SIGINT, self.shutdown)
        print("Service started, press Ctrl+C to stop")
        while self.running:
            time.sleep(0.5)
        print("Service stopped")

    def mock_signal(self, signum):
        # Simulate receiving a signal without killing the process
        self.shutdown(signum, None)

if __name__ == "__main__":
    service = Service()
    # Instead of running forever, simulate a SIGTERM after 1 second
    import threading
    timer = threading.Timer(1.0, service.mock_signal, args=[signal.SIGTERM])
    timer.start()
    service.run()
    timer.join()

Output

stdout
Service started, press Ctrl+C to stop
Received signal 15, shutting down gracefully...
Service stopped

How it works

The signal.signal calls register the shutdown method as the handler for both SIGTERM (15) and SIGINT (2). The mock_signal method directly calls the same handler, simulating what happens when the OS delivers a signal. The threading.Timer fires after one second, exercising the shutdown path without actually killing the process. The main loop checks the running flag every 0.5 seconds, so it exits cleanly after the mock signal flips it to False. This pattern is critical for production services that must flush data or close resources before exiting.

Common mistakes

  • Calling `signal.signal` inside the loop instead of once at startup
  • Forgetting to pass both `signum` and `frame` arguments to the handler
  • Using `sys.exit()` directly instead of setting a running flag
  • Creating the timer without joining it, leaving threads dangling

Variations

  1. Use `signal.signal(signal.SIGTERM, lambda s, f: service.shutdown())` to wrap the handler
  2. Replace the Timer with `asyncio.get_event_loop().call_later(1, service.mock_signal, signal.SIGTERM)` for async services

Real-world use cases

  • Web servers like Flask or Django that need to close database connections before pod termination.
  • Kubernetes-managed containers that must respond to SIGTERM within a grace period.
  • Data pipeline workers that must finish in-flight batch jobs before exiting.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Production deployment patterns

Related tutorials and quizzes for this topic.