Skip to main content

A robust thread supervisor for Python applications that monitors, restarts, and manages state for threads.

Project description

thread-warden

thread-warden is an open source helper for easily controlling your threads in multithreaded python programs.

It uses a "warden" thread which all other threads register with. The warden monitors the health of the other threads and if a thread stalls/dies/has an issue, then the warden can stop it and create another thread. Threads can check in with the warden and the warden will save their data and on starting a new thread - give the dead threads data to the new thread.

It has easily adjustable restart configs of the threads to handle cases where a thread will crash continuously; it waits longer and longer to restart the thread until it hits a restart limit. These limits and timers reset when the new thread has been alive for x seconds.

So it notes thread data states and can automatically restart threads in the same state while handling crash loops.

Tests PRs Welcome

PyPI - Python Version PyPI - Version

Why Use This?

Let's say you write a large python program using 4 concurrent threads all doing different tasks, including calling APIs. Those threads will inevitably have issues from time to time, especially if they run for hours at at time and especially any thread accessing a API.

This warden thread can automatically handle the health of your threads, and ensure you don't lose data on thread stalls.

I personally had a program just like this example, and wrote this thread-warden for exactly this purpose. One of my threads would stall on API calls frequently and this would restart the thread, give it its old job again, and send it to the exact location in my code where it died to try the task again.

Installation

Install the package from PyPI:

pip install thread-warden

Quick Start

  1. Create a Thread Class: thread-warden uses a Child_Thread class in Interfaces.py. You can use this class or make a custom class which inherits it. You should note step 4 which is that you'll need to call check_in() in your threads main loop.
  2. Define a Basic Factory: Create a simple function that returns a new instance of your thread. This function will be saved in the data of each thread and the warden will use it to create a new thread.
  3. Register with Warden: Start the warden and register your thread.
  4. Call the check_in() function: Call this function anytime you want a thread to check in with the warden, and during the threads main loop. This will cause the warden to save the threads data and tell the warden it's still alive; it also resets the threads timeout so the warden doesn't stop it. You'll call this in your threads main loop, and may call it before any dangerous task that may kill the thread (like an API call).
  5. Optional: end the warden thread: You can end the warden thread via warden.shutdown(). This takes a bool parameter stop_children which defaults to True; it tells the warden if it should stop all child threads with it, or leave them all active.

The most difficult part of using this tool is making your own factory. You have to make the factory yourself because it lets you program your thread to do whatever you want, as opposed to this project making a default factory function.

import time
import logging
from typing import Dict, Any, Optional
from Thread_Warden import Thread_Warden, Child_Thread

# 1. Subclass Child_Thread
class MyWorker(Child_Thread):
    def main_loop(self):
        while not self.stop_event.is_set():
            # Do work here
            print(f"{self.name} is working...")
            
            # Check in periodically to let Warden know we are alive
            # You can also save state here (e.g., current index, job ID)
            self.warden.check_in(self.name, state={"last_processed": time.time()})
            
            time.sleep(1)

# 2. Define a Factory Function
def worker_factory(saved_state: Optional[Dict[str, Any]]) -> Child_Thread:
    # If the thread crashed, 'saved_state' contains the last check_in data
    if saved_state:
        print(f"Restoring from state: {saved_state}")
    return MyWorker()

# 3. Setup Warden
if __name__ == "__main__":
    warden = Thread_Warden()
    warden.start()

    # Register the thread
    # timeout=5.0 means if the thread doesn't check_in() for 5 seconds, it gets restarted
    warden.register_new_child(
        name="Worker_1", 
        factory=worker_factory, 
        timeout=5.0
    )

    # Keep main thread alive
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        warden.shutdown()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

thread_warden-0.1.0.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

thread_warden-0.1.0-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file thread_warden-0.1.0.tar.gz.

File metadata

  • Download URL: thread_warden-0.1.0.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for thread_warden-0.1.0.tar.gz
Algorithm Hash digest
SHA256 560654a7c4d0d022886c0580a672403c4e5947ca823253d029ec99df600bdf70
MD5 99fe81aa73506e6bf493b2ed0b04e8e7
BLAKE2b-256 bfcd1642fa4feedd98400c3c984d73b41ee494bf9a22df88d17bbae20922b96b

See more details on using hashes here.

File details

Details for the file thread_warden-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: thread_warden-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for thread_warden-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 986fb9a679483ee91c2663e140a1fb121887324878a2b97201505ad50104f433
MD5 c545766443f2213d00cf46c18a4528ed
BLAKE2b-256 272a0e52b742ef4eba5a0bfd93990ff22366fe2a523efbd9d60cb5d13acda58e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page