scietex.service
Async worker framework for building background daemon services in Python.
Provides a hierarchy of workers — from basic signal-handling daemons to concurrent task processors with Valkey-backed distributed queues.
Python ≥ 3.10 · License: MIT
Documentation
- Overview — Core components and architecture
- BasicAsyncWorker — Signal handling, logging, heartbeat & watchdog managers
- AsyncTaskProcessor — Concurrent task processing, handler dispatch, timeout monitoring
- ValkeyWorker — Valkey stream-based task distribution
- Task Handler — Pluggable handler architecture, typed schemas
Installation
# Core package (no Valkey)
pip install scietex.service
# With Valkey (Redis-compatible) support
pip install "scietex.service[valkey]"
Dependencies: msgspec>=0.20.0, pyaml>=26.2.1, scietex.logging>=0.2.0
Quick Start
Basic Async Worker
A minimal daemon with signal handling, heartbeat, and watchdog. See the full BasicAsyncWorker docs for lifecycle, manager system, and configuration details.
import asyncio
import logging
from scietex.service import BasicAsyncWorker
class MyWorker(BasicAsyncWorker):
async def heartbeat(self) -> None:
self.logger.info("Worker is alive")
async def watchdog(self) -> None:
self.logger.debug("Running watchdog checks")
async def cleanup(self) -> None:
self.logger.info("Shutting down gracefully")
async def main() -> None:
worker = MyWorker(
service_name="my_service",
version="1.0.0",
logging_level=logging.DEBUG,
heartbeat_interval=10,
watchdog_interval=1,
)
await worker.start()
await worker.events["exit"].wait()
if __name__ == "__main__":
asyncio.run(main())
Send SIGINT (Ctrl+C) or SIGTERM to trigger graceful shutdown.
Task Processor
Register handlers for different task types and process them concurrently. See the full AsyncTaskProcessor docs for architecture, task processing flow, and best practices.
import asyncio
import logging
from scietex.service import AsyncTaskProcessor
from scietex.service.task_handler import TaskData, TaskHandler, TaskResult
class EmailHandler(TaskHandler):
@property
def supported_tasks(self) -> list[str]:
return ["send_email"]
async def initialize(self) -> bool:
# Connect to email service, etc.
self.logger.info("Email handler initialized")
return True
async def handle(self, task_data: TaskData) -> TaskResult:
try:
# Process task_data.payload
self.logger.info("Sending email…")
return TaskResult(status="success", error="")
except Exception as exc:
return TaskResult(status="error", error=str(exc))
class MyProcessor(AsyncTaskProcessor):
async def fetch_tasks(self) -> None:
# Pull tasks from your source (DB, API, queue, etc.)
# and put them into self.task_queue:
# await self.task_queue.put((task_id, task_data))
pass
async def main() -> None:
processor = MyProcessor(
service_name="email_worker",
version="1.0.0",
queue_size=100,
max_concurrent_tasks=5,
)
processor.add_task_handler("send_email", EmailHandler)
await processor.start()
await processor.events["exit"].wait()
if __name__ == "__main__":
asyncio.run(main())
Valkey Worker
Distributed task processing backed by a Valkey (Redis-compatible) stream. See the full ValkeyWorker docs for architecture, key naming, configuration reference, and PubSub broadcasting.
import asyncio
import logging
from scietex.service import (
ValkeyAdvancedConfig,
ValkeyBaseConfig,
ValkeyConfig,
ValkeyNode,
ValkeyWorker,
)
async def main() -> None:
config = ValkeyConfig(
base_config=ValkeyBaseConfig(
nodes=[ValkeyNode(host="localhost", port=6379)],
request_timeout=10_000,
),
advanced_config=ValkeyAdvancedConfig(
connection_timeout=10_000,
tcp_nodelay=True,
),
)
worker = ValkeyWorker(
service_name="distributed_worker",
version="1.0.0",
worker_id=1,
logging_level=logging.DEBUG,
heartbeat_interval=10,
valkey_config=config,
queue_size=100,
max_concurrent_tasks=10,
)
await worker.start()
await worker.events["exit"].wait()
if __name__ == "__main__":
asyncio.run(main())
Tasks are stored in a Valkey stream named
scietex:{service_name}:{worker_id}:tasks and consumed via a consumer
group scietex:{service_name}:{worker_id}:task_group.
Architecture
Worker Hierarchy
See BasicAsyncWorker, AsyncTaskProcessor, and ValkeyWorker for detailed architecture diagrams.
BasicAsyncWorker — Signal handling, async logging, heartbeat &
watchdog managers, graceful shutdown
└── AsyncTaskProcessor — Task queue, concurrent processing, handler
dispatch, timeout watchdog
└── ValkeyWorker — Valkey stream integration, connection
management, stream-based task fetching
Manager Lifecycle
Managers are async methods decorated with @Manager. The worker
discovers them via the class MRO and runs each as an asyncio.Task:
- Start — Manager loop runs the decorated method in a
while Trueloop until cancelled. - Error — On any exception (except
CancelledError), the error is recorded and the manager is automatically restarted. - Stop — On shutdown, managers are cancelled and their optional
cleanupcallbacks are invoked.
Task Handler System
See the Task Handler docs for the full handler lifecycle, schema details, and best practices.
- Register:
processor.add_task_handler("type", HandlerClass)— Registers a handler class under a name. The processor creates handler instances on start. - Declare support:
Handler.supported_tasksproperty must return a list of task type strings this handler can process. - Dispatch: When a task arrives, the processor calls
handler.supports(task_type)on each registered handler. The first handler returningTruereceives the task. - Initialize:
handler.start()callshandler.initialize()and setshandler.is_ready = True. - Handle:
await handler.handle(task_data)returns aTaskResultwithstatus("success"/"error"), optionalerrormessage, and optionalpayload. - Timeout: Tasks exceeding their
timeout(default 3s) are canceled and either re-queued or discarded perTaskTimeout.timeout_action.
Task Schemas
All schemas are frozen msgspec.Struct instances (immutable).
| Type | Description |
|---|---|
TaskData |
Immutable task payload: task (type string), payload (bytes), timeout (TaskTimeout), canceled_action ("requeue"/"discard") |
TaskResult |
Handler result: status ("success"/"error"), error (message), processed_at (UTC datetime), payload (bytes) |
TaskTimeout |
Timeout config: timeout (seconds, None for default 3s), timeout_action ("requeue"/"discard") |
TaskTracker |
Internal: tracks running asyncio.Task, associated TaskData, and monotonic start time |
Configuration
Config Directory Precedence
The worker searches for a config directory in this order:
conf_dirargument (if provided and is a directory)~/.config/scietex//etc/scietex//usr/local/etc/scietex/./config/(current working directory)
The first existing directory is used. If none exist, ~/.config/scietex/
is created.
Valkey Configuration
ValkeyWorker reads valkey.yml from the config directory:
base_config:
nodes:
- host: localhost
port: 6379
user_credentials: null
use_tls: false
request_timeout: 5000
database_id: null
client_name: null
inflight_requests_limit: null
client_az: null
lazy_connect: null
read_from: PRIMARY
backoff_strategy: null
protocol: RESP3
advanced_config:
connection_timeout: 10000
tcp_nodelay: null
tls_config:
use_insecure_tls: false
root_pem_cacerts: null
If the file is missing or invalid, defaults are used and the file is created with default values.
API Reference
Exported from scietex.service
| Symbol | Description |
|---|---|
BasicAsyncWorker |
Base async daemon worker |
AsyncTaskProcessor |
Concurrent task processor |
Manager |
Decorator for creating managed async loop methods |
ValkeyWorker |
Valkey-backed distributed worker |
__version__ |
Package version string |
Exported from scietex.service.task_handler
| Symbol | Description |
|---|---|
TaskHandler |
Abstract base class for task handlers |
TaskData |
Task payload schema |
TaskResult |
Task result schema |
TaskTimeout |
Timeout configuration schema |
TaskTracker |
Internal task tracker schema |
Exported from scietex.service.valkey
| Symbol | Description |
|---|---|
ValkeyConfig |
Top-level Valkey configuration |
ValkeyBaseConfig |
Basic connection settings |
ValkeyAdvancedConfig |
Advanced connection settings |
ValkeyNode |
Server node address |
ValkeyUserCredentials |
Authentication credentials |
ValkeyBackoffStrategy |
Reconnection backoff config |
ValkeyTlsAdvancedConfiguration |
TLS settings |
Development
Setup
# Clone the repository and install all dependencies
uv sync --all-extras
# Or install specific extras
uv sync --extra dev --extra test --extra lint
Commands
| Command | Description |
|---|---|
uv run ruff check src/ |
Lint (auto-fix: ruff check --fix) |
uv run ty check src/ |
Type check |
uv run ruff format src/ |
Format code |
uv run pytest tests/ |
Run tests |
tox |
Run tests with coverage |
Running Examples
python -m examples.async_service
python -m examples.async_task_processor
python -m examples.valkey_async_service # requires valkey-glide
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scietex_service-2.0.0.tar.gz.
File metadata
- Download URL: scietex_service-2.0.0.tar.gz
- Upload date:
- Size: 36.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05a15a492c5a4643c1a6a266dbf148da47c0f2367ffbeab5522fcdbe543021f8
|
|
| MD5 |
fc1bbc07e1c75e025c171233a51e0919
|
|
| BLAKE2b-256 |
6a085fefda272d93972fedef732a8b8815d0f61a03c62b877a42a583ee4fb6d4
|
Provenance
The following attestation bundles were made for scietex_service-2.0.0.tar.gz:
Publisher:
python-publish.yml on bond-anton/scietex.service
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scietex_service-2.0.0.tar.gz -
Subject digest:
05a15a492c5a4643c1a6a266dbf148da47c0f2367ffbeab5522fcdbe543021f8 - Sigstore transparency entry: 2686467767
- Sigstore integration time:
-
Permalink:
bond-anton/scietex.service@eed7dca1b0ce9bcb11cd4f7f3be6fd15c0e51292 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/bond-anton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@eed7dca1b0ce9bcb11cd4f7f3be6fd15c0e51292 -
Trigger Event:
release
-
Statement type:
File details
Details for the file scietex_service-2.0.0-py3-none-any.whl.
File metadata
- Download URL: scietex_service-2.0.0-py3-none-any.whl
- Upload date:
- Size: 37.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a1b0a010539d9e2ab2253b1a2d7b3bf318e6b707567f3911be72ffe589d1a95
|
|
| MD5 |
f611b8ea80d475dc400315ff3016f838
|
|
| BLAKE2b-256 |
2accc52f822e839d4d2caa53edf7991bb60988828d392db8e50c1fc965cdc39d
|
Provenance
The following attestation bundles were made for scietex_service-2.0.0-py3-none-any.whl:
Publisher:
python-publish.yml on bond-anton/scietex.service
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scietex_service-2.0.0-py3-none-any.whl -
Subject digest:
3a1b0a010539d9e2ab2253b1a2d7b3bf318e6b707567f3911be72ffe589d1a95 - Sigstore transparency entry: 2686467839
- Sigstore integration time:
-
Permalink:
bond-anton/scietex.service@eed7dca1b0ce9bcb11cd4f7f3be6fd15c0e51292 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/bond-anton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@eed7dca1b0ce9bcb11cd4f7f3be6fd15c0e51292 -
Trigger Event:
release
-
Statement type: