Circus Process & Socket Manager
Circus is a robust Python program designed to run, monitor, and manage multiple processes and sockets. It provides a flexible way to supervise long-running services, akin to tools like Supervisor, but with a focus on programmatic control and extensibility. The current version is 0.19.0. Releases are somewhat irregular, often tied to Python version support or dependency updates.
Common errors
-
ImportError: No module named 'circus.arbiter'
cause The `circus` library is not installed or not available in the current Python environment.fixRun `pip install circus` to install the library. -
circusd: error: The current Tornado version (X.Y) is too old.
cause Your installed `tornado` package is older than the minimum version required by Circus (typically >=5.0 for recent Circus versions).fixUpgrade your Tornado installation: `pip install --upgrade tornado` or ensure `pip install circus` resolves compatible versions. -
ConfigParser.NoOptionError: No option '...' in section: '...'
cause Configuration file parsing issue, potentially due to the `RawConfigParser` change in Circus 0.17.0+, or malformed INI syntax.fixCheck your `circus.ini` file for correct syntax, missing sections/options, or unintended interpolation. Ensure options are explicitly defined. -
circusctl: error: Command 'mycommand' failed (status: error_message_here)
cause A `circusctl` command failed, and the utility returned a non-zero exit code, potentially breaking scripts that expect a zero exit code on all `circusctl` operations.fixExamine the error message from `circusctl` for the specific failure reason. Update calling scripts to handle non-zero exit codes from `circusctl`.
Warnings
- breaking Circus versions 0.17.0+ dropped support for Python 2.7, 3.3, and 3.4. Version 0.16.0+ dropped support for Python 2.6 and 3.3. Ensure your Python environment meets the `>=3.7` requirement for recent versions.
- breaking Dependency requirements for Tornado and PyZMQ changed significantly. Circus 0.15.0 explicitly forbid Tornado >= 5 and PyZMQ >= 17, while 0.16.0+ requires PyZMQ >= 17 and 0.17.0+ requires Tornado >= 5. This can lead to dependency conflicts or runtime errors if versions are mismatched.
- gotcha Starting with version 0.17.0, Circus uses `RawConfigParser` instead of `ConfigParser` for its `.ini` configuration files. If your configuration heavily relies on `ConfigParser`'s interpolation features (e.g., `${section:option}`), these might no longer work as expected and could lead to parsing errors.
- gotcha From version 0.15.0, the `circusctl` command-line utility returns a non-zero exit code when a command fails. This is a behavioral change from previous versions which might have always returned zero. Scripts relying on `circusctl`'s exit code for success/failure detection will need to be updated.
Install
-
pip install circus
Imports
- Arbiter
from circus.arbiter import Arbiter
- Watcher
from circus.watcher import Watcher
- CircusClient
from circus.client import CircusClient
Quickstart
from circus.watcher import Watcher
from circus.arbiter import Arbiter
import time
import sys
# Define a simple command to run
# Using sys.executable ensures the current Python environment's interpreter is used.
command = f"{sys.executable} -c \"import time; print('Hello from Circus!'); time.sleep(2); print('Process exiting.')\""
# Create a watcher for our process
# 'stop_signal' 15 (SIGTERM) is a common graceful shutdown signal
watcher = Watcher(name="my_test_process", cmd=command, numprocesses=1, stop_signal=15)
# Create an arbiter and register the watcher
arbiter = Arbiter([watcher])
print("Starting Circus Arbiter...")
try:
# Start the arbiter, which will launch and manage the watcher's processes
arbiter.start()
print("Arbiter started. Processes are running. Press Ctrl+C to stop.")
# Keep the main thread alive to allow the arbiter to run in the background
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nCtrl+C detected. Stopping Arbiter...")
# Cleanly stop the arbiter and its managed processes
arbiter.stop()
print("Arbiter stopped. Exiting.")
except Exception as e:
print(f"An error occurred: {e}")
arbiter.stop()
sys.exit(1)