Registry / workflow / cotyledon

cotyledon

JSON →
library2.2.0pypypi✓ verified 86d ago

Cotyledon is a Python framework (version 2.2.0, actively maintained) designed for defining and managing long-running services. It provides robust handling of Unix signals, efficient spawning and supervision of worker processes, daemon reloading capabilities, `sd-notify` integration, and rate limiting for worker restarts. It sees significant use in OpenStack Telemetry projects as a lightweight replacement for `oslo.service`, which carried heavy `eventlet` dependencies. The library aims for a consistent code path for single and multiple worker configurations and offers advanced reload and termination APIs.

pip install cotyledon
INSTALL
IMPORT
SIG · COTYLEDON
C
cotyledon
workflowpythonv2.2.0
Install
1.9s avg
Import
92ms
Disk
16MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
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
musl
py 3.103.915 runs
installs and imports cleanly · install 0.0s · import 0.079s · 18.4MB
glibc
py 3.103.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.
fix
Rewrite 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.
fix
Integrate 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.
fix
Ensure 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)`.
fix
Ensure 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.
fix
Inspect 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.
fix
Override 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.
Agent activity
32 hits · last 30 days
node
30
OpenAI (training)
1
Resources
cotyledon — pip install cotyledon · libregistry