Install & Compatibility
Where this runs
tested against v2.2.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.915 runs
installs and imports cleanly · install 0.0s · import 0.079s · 18.4MB
glibcpy 3.10–3.915 runs
installs and imports cleanly · install 1.9s · import 0.068s · 19MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Service
✓ from cotyledon import Service
ServiceManager
✓ from cotyledon import ServiceManager
This quickstart defines a simple `MyService` that logs its lifecycle events (`init`, `run`, `terminate`, `reload`). The `ServiceManager` then registers two workers for this service. The `run` method simulates work, waiting on a shutdown event. To stop the service, send a `SIGTERM` (e.g., Ctrl+C in a terminal) to the main process; for reload, send `SIGHUP` (e.g., `kill -HUP <pid>`).
import cotyledon
import logging
import threading
import time
import os
LOG = logging.getLogger(__name__)
class MyService(cotyledon.Service):
name = "my_example_service"
def __init__(self, worker_id):
super(MyService, self).__init__(worker_id)
self._shutdown = threading.Event()
LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} init")
def run(self):
LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} running...")
# In a real service, this loop would perform work, e.g., consume from a queue
# and call _shutdown.set() when it needs to stop processing.
while not self._shutdown.is_set():
LOG.debug(f"[{os.getpid()}] {self.name} worker {self.worker_id} working...")
time.sleep(1)
def terminate(self):
LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} terminating...")
self._shutdown.set()
def reload(self):
LOG.info(f"[{os.getpid()}] {self.name} worker {self.worker_id} reloading...")
# Implement logic to reload configuration or re-initialize components
# without stopping the worker if possible.
def main():
# Basic setup for logging to console
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(process)d - %(levelname)s - %(message)s')
LOG.info("Starting Cotyledon Service Manager")
manager = cotyledon.ServiceManager()
# Add MyService with 2 worker processes
manager.add(MyService, workers=2)
# Run the service manager, which will spawn workers and handle signals
manager.run()
LOG.info("Cotyledon Service Manager stopped.")
if __name__ == "__main__":
main()
Debug
Known issues
breakingWhen migrating from `oslo.service`, be aware that Cotyledon does not rely on `eventlet` for greenlet-based concurrency or monkey-patching the standard library. This means applications depending on `eventlet`'s behavior will need significant refactoring.fixRewrite concurrency patterns to use standard Python `threading` or `multiprocessing` modules. For periodic tasks previously handled by `oslo.service`, consider using the `futurist` library.
affects: All versions (design decision)
gotchaCotyledon does not provide built-in facilities for WSGI application creation or socket sharing between parent and child processes. If your `oslo.service` based application relied on these features for HTTP services, Cotyledon is not a direct drop-in replacement.fixIntegrate a dedicated WSGI server (e.g., Gunicorn, uWSGI) and manage its processes separately or within your Cotyledon service, handling socket binding and passing through standard means.
affects: All versions (design decision)
gotchaThe `ServiceManager` includes a 'seatbelt' mechanism to prevent multiple service managers from running concurrently, which can lead to unexpected behavior if multiple instances of your application are launched in the same environment.fixEnsure only one instance of your main application entry point is executed. Check for existing processes before starting new ones, or design your deployment environment to guarantee single instance execution.
affects: All versions
Errors
Common errors & fixes
TypeError: __init__() missing 1 required positional argument: 'worker_id'
Your custom service class (inheriting from `cotyledon.Service`) must define an `__init__` method that accepts `worker_id` as its first argument after `self` and passes it to `super().__init__(worker_id)`.
fixEnsure your service class's `__init__` method signature is `def __init__(self, worker_id, *args, **kwargs):` and calls `super().__init__(worker_id, *args, **kwargs)`.
Service process exited unexpectedly (exit code N)
A worker process terminated prematurely. This can be due to unhandled exceptions within the worker's `run()` method, memory issues, or incorrect termination logic.
fixInspect worker logs for unhandled exceptions or error messages. Ensure your `run()` method properly handles its work loop and that the `terminate()` method gracefully shuts down resources. Increase logging level for the specific service to debug.
SIGHUP received but service does not reload
Your custom service class has not implemented the `reload()` method, or the implementation does not include the desired reload logic.
fixOverride the `reload()` method in your `cotyledon.Service` subclass. This method is called when `SIGHUP` is received, allowing you to implement logic like reloading configuration files or re-initializing state without fully stopping and restarting the worker process. If no `reload()` method is defined, `cotyledon` will still restart the worker for a full reload.
Upgrade
Version history
2.2.0latest on PyPI · released Dec 23, 2025
Audit
Dependencies
futuristoptionalRecommended for periodic tasks if migrating from oslo.service, as cotyledon itself does not provide this functionality.
oslo.configoptionalUsed in some examples for configuration management, particularly when integrating with OpenStack projects, but not a core dependency for basic cotyledon services.