Skip to main content

Lightweight task scheduler

Project description

Camora

Ruff pre-commit Checked with mypy CI

Camora is a lightweight task scheduler for Python. It uses Pydantic to validate the tasks' payload and is built with dependency injection in mind.

The main idea behind Camora, was to have something simpler than Celery, that is easy to use, supports async code and allows for customization of all the building blocks.

Warning: this is still work in progress!

Usage

First, you need create an app instance:

import redis.asyncio as redis

from camora.app import Camora
from camora.redis.broker import RedisBroker

redis_broker=RedisBroker(
    "test_stream",
    "test_group",
    "failure_stream",
    client = redis.StrictRedis(host="localhost", port=6379),
)
app = Camora(broker=redis_broker)

The app requires some broker like Redis, but it can take anything that satisfies the Broker interface.

After that, you can define your tasks:

@app.register
class SendEmail(BaseTask):
    recipient: str
    content: str

    async def execute(self) -> None:
        print("sending email to", self.recipient, "with content", self.content)

With that in place, you can dispatch the task in multiple ways:

await app.dispatch(
    SendEmail(
        recipient="kappa",
        content="keppo",
    )
)
await app.dispatch(
    SendEmail,
    recipient="kappa",
    content="keppo",
)
await app.dispatch(
    "SendEmail",
    recipient="kappa",
    content="keppo",
)

Meaning you can either:

  1. Pass a pre-constructed task instance
  2. Pass a task class and kwargs, which will be used to construct the task on the worker side
  3. Pass a task name and kwargs, which will lead to the same result as the previous option

Either way, tasks will be passed to the broker, which will then be consumed by the worker.

To start the worker, you need to simply call the start method:

if __name__ == "__main__":
    asyncio.run(app.start())

Retry policy

Camora supports passing a default retry policy that will be applied to all tasks:

import backoff

app = Camora(
    broker=RedisBroker(
        "test_stream",
        "test_group",
        "failure_stream",
        client = redis.StrictRedis(host="localhost", port=6379),
    ),
    retry_policy=backoff.on_exception(
        backoff.expo,
        Exception,
        max_tries=5,
        max_time=30,
    )
)

Each task can also define its own retry policy:

@app.register
class SendEmail(BaseTask):
    recipient: str
    content: str

    @backoff.on_exception(
        backoff.expo,
        Exception,
        max_tries=3,
    )
    async def execute(self) -> None:
        print("sending email to", self.recipient, "with content", self.content)

Default support was made with backoff in mind, but anything that satisfies the RetryDecorator type will work.

Dependencies

Tasks often call some code that is not a part of the task itself, but is required for it to work. For example, a task that sends an email will need to call some email sending library. Camora supports passing dependencies directly to task execute method. First, you need to define what dependencies will be used in the app:

class EmailClient:
    def send_email(self, recipient: str, content: str) -> None:
        print(f"Sending email to {recipient} with content {content}")


def get_email_client() -> EmailClient:
    return EmailClient()


app = Camora(
    broker=RedisBroker(
        "test_stream",
        "test_group",
        "failure_stream",
        client = redis.StrictRedis(host="localhost", port=6379),
    )
    dependencies=[get_email_client],
)

Now, when defining a task, you can specify what dependencies it requires:

@app.register
class SendEmail(BaseTask):
    recipient: str
    content: str

    async def execute(self, email_client: EmailClient) -> None:
        email_client.send_email(self.recipient, self.content)

Camora will read the types of all the execute dependencies, and will pass them to the task when executing it. On top of that, if multiple tasks are being processed, and they require the same dependency, Camora will only call the dependency factory once, and will pass the same instance to all tasks.

Periodic tasks

Camora allows you to define periodic tasks, that will be executed on a schedule. To do that, you need to use the @app.schedule decorator:

@app.schedule(schedule.every(5).seconds)
def sync_task():
    print("hello every 5 seconds")


@app.schedule(schedule.every(1).day.at("12:00"))
async def async_task():
    print("hello every day at 12:00")

Tasks can be either sync or async, and they will be executed on the worker side.

Work in progress

Here are some things that are still missing:

  • Tests
  • Proper docs
  • Sync code support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

camora-0.1.0.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

camora-0.1.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file camora-0.1.0.tar.gz.

File metadata

  • Download URL: camora-0.1.0.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.7

File hashes

Hashes for camora-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b37380528ed6b0be7d1735862b46ea391371bd506d0b460aa9d7c1dd8e932cae
MD5 3ac3d98db8dbec7c04f15f511b29ca71
BLAKE2b-256 76c24f4c56b7079dd542bb803183eb188a8848330d54337547b9ce8c20212a31

See more details on using hashes here.

File details

Details for the file camora-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: camora-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.7

File hashes

Hashes for camora-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2b31239ee8512b62635033572874949f3184d0e881888ebad48ad2c2c1493420
MD5 3ca254d3ddb38a68ace05d4429aa19f3
BLAKE2b-256 9a0e35f43d4a612dbee9a9f966b455bf42f70803ac6a48caee298e7c88a5653b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page