hupper
hupper is an integrated process monitor for your Python process, designed for development. It automatically tracks changes to imported Python files and custom paths, restarting the process when files are modified. Reloads can also be triggered manually from code. It is currently at version 1.12.1 and maintains an active release cadence.
Warnings
- breaking Version 1.11 dropped support for Python 2.7, 3.4, 3.5, and 3.6. Ensure your environment is Python 3.7 or newer.
- gotcha The `reload_interval` parameter, which controls how often the filesystem is scanned, must be set to a value greater than 0. Setting it to 0 or less could cause needless CPU spinning and is explicitly disallowed since version 1.11. The default is 1 second.
- gotcha Prior to version 1.10, hupper's handling of SIGTERM signals could be problematic, especially in containerized environments like Docker. It would immediately shut down, potentially stranding the worker process.
- gotcha The `hupper.start_reloader()` function behaves differently based on whether it's called from the parent (monitor) process or a child (worker) process. When called initially from the parent, it forks a new worker, starts the monitor, and *never returns* in the parent process. When called from a worker process, it returns an `IReloaderProxy` instance to communicate with the monitor.
- gotcha You can override hupper's default file monitor (which auto-selects between `watchdog` and `polling`) by setting the `HUPPER_DEFAULT_MONITOR` environment variable to a dotted Python path of an `IFileMonitorFactory` implementation (e.g., `hupper.polling.PollingFileMonitor`).
Install
-
pip install hupper
Imports
- start_reloader
from hupper import start_reloader
- is_active
from hupper import is_active
- get_reloader
from hupper import get_reloader
Quickstart
import hupper
import os
import datetime
import time
def worker_func():
"""This function contains the main application logic, re-executed on reload."""
now = datetime.datetime.now()
print(f"Worker process active at {now}. Edit 'my_app.py' to trigger reload.")
# In a real application, this would be your main application loop
# (e.g., a web server, a background task). For this example, we just
# sleep briefly to demonstrate the worker's lifecycle.
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Worker received KeyboardInterrupt, shutting down.")
def main():
if os.environ.get('HUPPER_RELOADER') != 'true':
# This code runs in the *monitor* process
print("Monitor: Starting reloader for 'my_app.worker_func'...")
# The first call to start_reloader from the main process
# forks a worker, starts monitoring, and never returns in this process.
reloader = hupper.start_reloader('my_app.worker_func')
# Optional: Watch additional files/directories
# reloader.watch_files(['./config.ini'])
print("Monitor: Reloader stopped.") # This line is usually not reached
else:
# This code runs in the *worker* process, after being forked by the monitor
print("Worker: This is a reloaded worker process.")
worker_func()
if __name__ == '__main__':
main()