Metadata-Version: 2.5
Name: taskferry-scheduler
Version: 0.2.0
Summary: Portable schedule triggering (cron, rate, one-shot) alongside Taskferry.
Project-URL: Homepage, https://github.com/xiidigital/taskferry
Project-URL: Documentation, https://taskferry.dev/scheduler
Project-URL: Source, https://github.com/xiidigital/taskferry
Author: Taskferry authors
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: cloud-scheduler,cron,eventbridge-scheduler,scheduler,taskferry
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: taskferry<0.3,>=0.2
Provides-Extra: aws
Requires-Dist: boto3>=1.34; extra == 'aws'
Provides-Extra: gcp
Requires-Dist: google-cloud-scheduler>=2.13; extra == 'gcp'
Provides-Extra: kubernetes
Requires-Dist: kubernetes>=29.0; extra == 'kubernetes'
Description-Content-Type: text/markdown

# taskferry-scheduler

Portable **"when to fire"** triggering. Ships alongside [Taskferry](https://github.com/xiidigital/taskferry),
the portable execution layer. Plain Python, no Django required.

A **Schedule** decides *when*; it never runs business logic itself. It triggers
another capability — a Task, a Job, or an Event — by composition.

```bash
pip install taskferry-scheduler            # in-process scheduler
pip install taskferry-scheduler[gcp]       # + Google Cloud Scheduler
```

## Usage

```python
from taskferry_scheduler import Schedule, CronTrigger, HttpTarget, schedulers

schedulers.configure(
    {
        "default": {
            "factory": "taskferry_scheduler.adapters.gcp:make_cloud_scheduler",
            "project": "my-proj",
            "location": "us-central1",
        },
    }
)

schedulers["default"].create(
    Schedule(
        name="nightly-report",
        trigger=CronTrigger("0 2 * * *", timezone="Europe/Madrid"),
        # Fire a Task by hitting your Django task webhook (composition):
        target=HttpTarget("https://my-service.run.app/_taskferry/execute"),
    )
)
```

Local development / testing (in-process, deterministic):

```python
from taskferry_scheduler import LocalScheduler, Schedule, IntervalTrigger, CallableTarget
from datetime import datetime, timedelta, UTC

s = LocalScheduler()
s.create(Schedule("beat", IntervalTrigger(seconds=60), CallableTarget(lambda: print("tick"))))
s.run_pending(datetime.now(UTC) + timedelta(minutes=2))  # deterministic firing
# or s.start()  # background thread for real-time firing
```

## Triggers, targets, capabilities

- **Triggers:** `CronTrigger`, `IntervalTrigger`, `OneShotTrigger`.
- **Targets:** `HttpTarget` (fire a Task/service), `PubSubTarget` (fire an Event),
  `CallableTarget` (in-process, local only).

Schedulers differ, and the capability set says so honestly:

| Adapter                       | Provider   | Capabilities                                      |
| ----------------------------- | ---------- | ------------------------------------------------- |
| `LocalScheduler`              | local      | one_shot, interval, pause, resume, update, delete |
| `CloudSchedulerScheduler`     | gcp        | cron, timezone, pause, resume, update, delete     |
| `EventBridgeScheduler`        | aws        | cron, interval, one_shot, timezone, pause, resume, update, delete |
| `KubernetesCronJobScheduler`  | kubernetes | cron, timezone, pause, resume, update, delete     |

`LocalScheduler` rejects a `CronTrigger` and Cloud Scheduler / CronJob reject an
`IntervalTrigger` with `UnsupportedCapabilityError` — no silent misbehavior.

Roadmap: Azure scheduling (no single equivalent — Logic Apps / Functions timer).

## Composition, not orchestration

A schedule fires *one* capability. Taskferry does not provide DAG workflows or
state machines — that is out of scope (see the family composition docs).

## License

Apache-2.0.
