Of course! "Autorunner" in Python typically refers to a tool or script that automatically detects changes in your source code and re-runs your application. This is an incredibly common and useful workflow for development, especially for web applications, data analysis scripts, and automation tasks.

The most popular and standard tool for this in the Python ecosystem is watchdog.
Let's break down how to create an autorunner, from the basic concept to practical examples.
The Core Concept: How an Autorunner Works
An autorunner script performs these main steps in a loop:
- Watch a Directory: It monitors a specific folder (and its subfolders) for file system events.
- Detect Changes: It listens for events like file modification (
on_modified), creation (on_created), or deletion (on_deleted). - Trigger Action: When a relevant change is detected (e.g., a
.pyfile is saved), it executes a command, such aspython your_script.py. - Handle State: To prevent running the script multiple times for a single save, it often needs a mechanism (like a timer) to debounce the event.
Method 1: The Manual Approach with watchdog
This is the best way to understand the underlying mechanics. We'll write a small script that uses the watchdog library to watch a file and run it when it changes.

Step 1: Install watchdog
pip install watchdog
Step 2: Create a Sample Application
Let's say we have a simple web server that we want to auto-reload.
app.py
import time
import random
print("Server starting...")
# Simulate some state that changes
counter = 0
try:
while True:
counter += 1
print(f"Server is running... Counter: {counter}")
# Simulate some work
time.sleep(2)
# Simulate a random "error" to see how the autorunner handles it
if random.random() < 0.1:
print("Simulating an error by raising an exception!")
raise ValueError("A random error occurred!")
except KeyboardInterrupt:
print("Server shutting down.")
except Exception as e:
print(f"Server crashed with error: {e}")
# In a real app, you might want to log this and restart
Step 3: Create the Autorunner Script
This script will watch app.py and run it whenever it's modified.
autorunner.py

import time
import subprocess
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# --- Configuration ---
# The file or directory to watch
WATCHED_FILE = "app.py"
# The command to run
RUN_COMMAND = "python app.py"
class ChangeHandler(FileSystemEventHandler):
"""Triggers the run command on file modification."""
def __init__(self):
# This will hold the process of our running script
self.process = None
# Flag to prevent running the command multiple times for a single save
self.last_triggered = 0
self.debounce_interval = 1 # seconds
def on_modified(self, event):
# Check if the modified file is the one we're watching
if event.src_path.endswith(WATCHED_FILE):
current_time = time.time()
# Debounce: only run if enough time has passed since the last trigger
if current_time - self.last_triggered > self.debounce_interval:
print(f"\n--- Detected changes in {WATCHED_FILE} ---")
self.last_triggered = current_time
self.restart_process()
def restart_process(self):
"""Stops the old process and starts a new one."""
if self.process:
print("Stopping old process...")
self.process.terminate() # Send SIGTERM
try:
self.process.wait(timeout=5) # Wait for graceful shutdown
except subprocess.TimeoutExpired:
print("Old process did not terminate gracefully, killing it.")
self.process.kill() # Send SIGKILL
print(f"Starting new process with command: {RUN_COMMAND}")
try:
# Use sys.executable to ensure we're using the same Python interpreter
self.process = subprocess.Popen(RUN_COMMAND, shell=True)
except Exception as e:
print(f"Failed to start process: {e}")
def main():
print(f"Watching for changes in {WATCHED_FILE}...")
print(f"Run command: {RUN_COMMAND}")
print("Press Ctrl+C to stop the autorunner.")
event_handler = ChangeHandler()
observer = Observer()
observer.schedule(event_handler, path='.', recursive=False) # Watch only the current directory
observer.start()
try:
# Start the initial process
event_handler.restart_process()
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nStopping observer...")
observer.stop()
if event_handler.process:
event_handler.process.terminate()
observer.join()
if __name__ == "__main__":
main()
Step 4: Run the Autorunner
Open your terminal and run the autorunner script:
python autorunner.py
Now, open app.py in your editor, make a small change (like adding a print statement or changing a number), and save it. You will see the autorunner detect the change, stop the old app.py process, and start a new one.
Method 2: The Easiest Way with watchfiles
For many use cases, a newer library called watchfiles is simpler and more robust. It's often faster and has a cleaner API.
Step 1: Install watchfiles
pip install watchfiles
Step 2: Create a Simple Autorunner Script
watchfiles handles the process management for you, making the code much shorter.
autorunner_simple.py
import watchfiles
import subprocess
import time
# --- Configuration ---
WATCHED_DIR = "." # Watch the current directory
RUN_COMMAND = "python app.py"
def run_command_on_change(changes):
"""This function is called whenever a change is detected."""
# We only care about modifications to .py files
if any(change[1].endswith('.py') for change in changes):
print(f"\n--- Detected {len(changes)} change(s). Restarting... ---")
try:
# The 'shell=True' is important for commands like 'python app.py'
subprocess.run(RUN_COMMAND, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Process exited with error: {e}")
except KeyboardInterrupt:
print("\nStopping the process.")
# This will be caught by the main loop's KeyboardInterrupt handler
def main():
print(f"Watching directory: {WATCHED_DIR}")
print(f"Run command: {RUN_COMMAND}")
print("Press Ctrl+C to stop.")
# The watchfiles.awatch() function runs forever and calls our callback
# when changes are detected.
for changes in watchfiles.awatch(WATCHED_DIR, watch_filter=watchfiles.filters.PythonFilter()):
run_command_on_change(changes)
if __name__ == "__main__":
main()
This version is more concise because watchfiles handles the complexity of debouncing and process management. The PythonFilter is a convenient way to only watch Python files.
Method 3: The Industry Standard for Web Development (Flask / Django)
If you are building a web application, you should use the built-in development server of your framework. They have excellent, integrated autorunners.
For Flask
Flask's run command has a --reload flag (which is on by default in development).
# Install Flask if you haven't pip install Flask # Run the Flask app with auto-reloader python -m flask --app your_app_name run --reload
When you save a file in your project, the server will automatically restart.
For Django
Django's runserver command has a built-in autoreloader.
# Make sure you are in your Django project's root directory python manage.py runserver
Django's autoreloader is very sophisticated and can even detect changes in imported modules.
Summary: Which Method to Choose?
| Method | Best For | Pros | Cons |
|---|---|---|---|
Manual watchdog |
Learning the fundamentals, custom automation tasks. | Full control, no extra dependencies beyond watchdog. |
More complex code, you have to manage processes yourself. |
watchfiles |
General-purpose scripts, simple web apps, data pipelines. | Easiest to use, faster, robust, handles debouncing well. | One more dependency, but a very good one. |
| Framework Runner | Web Development (Flask, Django, FastAPI, etc.) | The standard, highly optimized, integrated with the framework's debugger. | Only works for that specific framework. |
Recommendation:
- For any web project, use your framework's built-in runner.
- For general-purpose scripts or automation, use the
watchfilesmethod. It's the sweet spot of simplicity and power. - If you want to understand how it works from the ground up, the manual
watchdogapproach is a great exercise.
