Metadata-Version: 2.4
Name: pyncette
Version: 1.1.0
Summary: A reliable distributed scheduler with pluggable storage backends
Project-URL: Documentation, https://tibordp.github.io/pyncette/
Project-URL: Changelog, https://tibordp.github.io/pyncette/changelog/
Project-URL: Issue Tracker, https://github.com/tibordp/pyncette/issues
Project-URL: Homepage, https://github.com/tibordp/pyncette
Author-email: Tibor Djurica Potpara <tibor.djurica@ojdip.net>
License: MIT
License-File: LICENSE
Keywords: async,asyncio,cron,distributed,scheduler,task-queue
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Operating System :: Unix
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: aiohttp>=3.8.4
Requires-Dist: aiosqlite>=0.19.0
Requires-Dist: coloredlogs
Requires-Dist: croniter>=1.3.14
Requires-Dist: python-dateutil>=2.8.2
Provides-Extra: all
Requires-Dist: aioboto3>=11.1.0; extra == 'all'
Requires-Dist: aiomysql>=0.1.1; extra == 'all'
Requires-Dist: asyncpg>=0.27.0; extra == 'all'
Requires-Dist: cryptography>=40.0.2; extra == 'all'
Requires-Dist: motor>=3.1.1; extra == 'all'
Requires-Dist: prometheus-client>=0.16.0; extra == 'all'
Requires-Dist: redis>=4.5.4; extra == 'all'
Requires-Dist: uvloop>=0.22.1; extra == 'all'
Provides-Extra: dynamodb
Requires-Dist: aioboto3>=11.1.0; extra == 'dynamodb'
Provides-Extra: mongodb
Requires-Dist: motor>=3.1.1; extra == 'mongodb'
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.1.1; extra == 'mysql'
Requires-Dist: cryptography>=40.0.2; extra == 'mysql'
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.27.0; extra == 'postgres'
Provides-Extra: prometheus
Requires-Dist: prometheus-client>=0.16.0; extra == 'prometheus'
Provides-Extra: redis
Requires-Dist: redis>=4.5.4; extra == 'redis'
Provides-Extra: uvloop
Requires-Dist: uvloop>=0.16.0; extra == 'uvloop'
Description-Content-Type: text/markdown

# Overview

[![Documentation](https://img.shields.io/badge/docs-github%20pages-blue)](https://tibordp.github.io/pyncette/)
[![Github Actions Build Status](https://github.com/tibordp/pyncette/workflows/Python%20package/badge.svg?branch=master)](https://github.com/tibordp/pyncette/actions?query=branch%3Amaster+workflow%3A%22Python+package%22)
[![Coverage Status](https://codecov.io/gh/tibordp/pyncette/branch/master/graphs/badge.svg?branch=master)](https://codecov.io/github/tibordp/pyncette)
[![PyPI Package latest release](https://img.shields.io/pypi/v/pyncette.svg)](https://pypi.org/project/pyncette)
[![PyPI Wheel](https://img.shields.io/pypi/wheel/pyncette.svg)](https://pypi.org/project/pyncette)
[![Supported versions](https://img.shields.io/pypi/pyversions/pyncette.svg)](https://pypi.org/project/pyncette)
[![Supported implementations](https://img.shields.io/pypi/implementation/pyncette.svg)](https://pypi.org/project/pyncette)
[![Commits since latest release](https://img.shields.io/github/commits-since/tibordp/pyncette/v1.1.0.svg)](https://github.com/tibordp/pyncette/compare/v1.1.0...master)

A reliable distributed scheduler with pluggable storage backends for Async Python.

- Free software: MIT license

## Installation

Minimal installation (just SQLite persistence):

```bash
pip install pyncette
```

Full installation (all the backends and Prometheus metrics exporter):

```bash
pip install pyncette[all]
```

You can also install the in-development version with:

```bash
pip install https://github.com/tibordp/pyncette/archive/master.zip
```

## Documentation

https://tibordp.github.io/pyncette/

## Usage example

Simple in-memory scheduler (does not persist state)

```python
from pyncette import Pyncette, Context

app = Pyncette()


@app.task(schedule="* * * * *")
async def foo(context: Context):
    print("This will run every minute")


if __name__ == "__main__":
    app.main()
```

Persistent distributed cron using Redis (coordinates execution with parallel instances and survives restarts)

```python
from pyncette import Pyncette, Context
from pyncette.redis import redis_repository

app = Pyncette(repository_factory=redis_repository, redis_url="redis://localhost")


@app.task(schedule="* * * * * */10")
async def foo(context: Context):
    print("This will run every 10 seconds")


if __name__ == "__main__":
    app.main()
```

See the `examples` directory for more examples of usage.

## Use cases

Pyncette is designed for reliable (at-least-once or at-most-once) execution of recurring tasks (think cronjobs) whose
lifecycles are managed dynamically, but can work effectively for non-reccuring tasks too.

Example use cases:

- You want to perform a database backup every day at noon
- You want a report to be generated daily for your 10M users at the time of their choosing
- You want currency conversion rates to be refreshed every 10 seconds
- You want to allow your users to schedule non-recurring emails to be sent at an arbitrary time in the future

Pyncette might not be a good fit if:

- You want your tasks to be scheduled to run (ideally) once as soon as possible. It is doable, but you will be better served by a general purpose reliable queue like RabbitMQ or Amazon SQS.
- You need tasks to execute at sub-second intervals with low jitter. Pyncette coordinates execution on a per task-instance basis and this corrdination can add overhead and jitter.

## Supported backends

Pyncette comes with an implementation for the following backends (used for persistence and coordination) out-of-the-box:

- SQLite (included)
- Redis (`pip install pyncette[redis]`)
- PostgreSQL (`pip install pyncette[postgres]`)
- MySQL 8.0+ (`pip install pyncette[mysql]`)
- MongoDB (`pip install pyncette[mongodb]`)
- Amazon DynamoDB (`pip install pyncette[dynamodb]`)

Pyncette imposes few requirements on the underlying datastores, so it can be extended to support other databases or
custom storage formats / integrations with existing systems. For best results, the backend needs to provide:

- Some sort of serialization mechanism, e.g. traditional transactions, atomic stored procedures or compare-and-swap
- Efficient range queries over a secondary index, which can be eventually consistent

## Development

### Prerequisites

Install [uv](https://docs.astral.sh/uv/) for fast package management:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

### Setup Development Environment

Sync dependencies and install the package in editable mode:

```bash
uv sync --extra all
```

### Running Tests

**Unit tests** (fast, no external dependencies):

```bash
uv run pytest -m "not integration" tests
```

**Integration tests** (requires Redis, PostgreSQL, MySQL, MongoDB, DynamoDB):

Using Docker Compose to set up all backends:

```bash
docker-compose up -d
docker-compose run --rm shell
uv run pytest tests
```

Or manually with services running locally:

```bash
uv run pytest tests
```

**Test on specific Python version**:

```bash
uv venv --python 3.11
uv sync --extra all
uv run pytest tests
```

### Code Quality

Run linting and type checking:

```bash
uv run pre-commit run --all-files
uv run ty check src examples
```

### Building Documentation

```bash
uv run mkdocs build
# Or serve locally with live reload
uv run mkdocs serve
```

### Building the Package

```bash
uv build
```
