杰瑞科技汇

autorunner Python如何实现自动化运行?

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.

autorunner Python如何实现自动化运行?-图1
(图片来源网络,侵删)

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:

  1. Watch a Directory: It monitors a specific folder (and its subfolders) for file system events.
  2. Detect Changes: It listens for events like file modification (on_modified), creation (on_created), or deletion (on_deleted).
  3. Trigger Action: When a relevant change is detected (e.g., a .py file is saved), it executes a command, such as python your_script.py.
  4. 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.

autorunner Python如何实现自动化运行?-图2
(图片来源网络,侵删)

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

autorunner Python如何实现自动化运行?-图3
(图片来源网络,侵删)
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 watchfiles method. It's the sweet spot of simplicity and power.
  • If you want to understand how it works from the ground up, the manual watchdog approach is a great exercise.
分享:
扫描分享到社交APP
上一篇
下一篇