Skip to main content

tickforge

An advanced task scheduler for Python with persistence, native async support, retries, timeouts, misfire handling and a batteries-included CLI.

Python License

Features

  • Three trigger typesIntervalTrigger, CronTrigger (with timezone support), DateTrigger
  • Durable jobs — SQLite, JSON file, or in-memory stores behind one interface
  • Async firstasync def jobs are awaited; sync jobs run in a thread pool
  • Reliability — per-job retries, retry backoff delay, execution timeouts, misfire grace windows
  • Concurrency control — global semaphore plus per-job overlap protection
  • Run history — every execution recorded with duration, attempts, result and traceback
  • Event hooks — subscribe to job and scheduler lifecycle events
  • Structured logging — human-readable or JSON, with job context on every record
  • CLI — register, inspect, preview, run and serve jobs without writing a driver script

Installation

pip install tickforge

From source:

cd tickforge
pip install -e ".[dev]"

Requires Python 3.8 or newer. Dependencies: click, python-dateutil.

Quick start

Async

import asyncio
from tickforge import AsyncScheduler, CronTrigger, IntervalTrigger, SQLiteJobStore

async def heartbeat():
    print("alive")

def collect_metrics(source: str):
    print("collecting from", source)

async def main():
    scheduler = AsyncScheduler(store=SQLiteJobStore("jobs.db"))
    await scheduler.add_job(heartbeat, IntervalTrigger(seconds=30), name="heartbeat")
    await scheduler.add_job(
        collect_metrics,
        CronTrigger.from_string("*/5 * * * *", timezone="Europe/Paris"),
        args=["edge-01"],
        max_retries=3,
        retry_delay=10,
        timeout=60,
        name="metrics",
    )
    await scheduler.run_forever()

asyncio.run(main())

Synchronous

from tickforge import Scheduler, IntervalTrigger, MemoryJobStore

def report():
    print("report generated")

with Scheduler(store=MemoryJobStore()) as scheduler:
    scheduler.add_job(report, IntervalTrigger(minutes=15), name="report")
    input("press enter to stop\n")

Triggers

Trigger Purpose Example
IntervalTrigger Fixed period, optional jitter and window IntervalTrigger(hours=2, jitter=30)
CronTrigger Calendar schedules, timezone aware CronTrigger(minute="0", hour="9", day_of_week="mon-fri")
DateTrigger One-shot execution DateTrigger(run_at="2026-01-01T00:00:00Z")

Cron fields are minute hour day month day_of_week, supporting *, a-b, a,b, */n, a-b/n, month and weekday names, and the macros @hourly, @daily, @weekly, @monthly, @yearly.

day_of_week uses Python semantics: 0 is Monday through 6 is Sunday. Names (mon, fri) and the legacy 7 for Sunday are also accepted. When both day and day_of_week are restricted, classic cron OR semantics apply.

Stores

from tickforge import MemoryJobStore, JSONFileJobStore, SQLiteJobStore, create_store

MemoryJobStore()                  # volatile, ideal for tests
JSONFileJobStore("jobs.json")     # human-readable, atomic writes
SQLiteJobStore("jobs.db")         # durable, WAL, safe across threads and processes

create_store("sqlite:///var/lib/tickforge/jobs.db")
create_store("json:///tmp/jobs.json")
create_store("memory://")

Because jobs are persisted as module:callable references, the target must be importable from the process running the scheduler. Lambdas and locally defined functions are rejected at registration time.

Reliability options

await scheduler.add_job(
    flaky_task,
    IntervalTrigger(minutes=5),
    max_retries=3,            # four attempts total
    retry_delay=10,           # seconds between attempts
    timeout=120,              # abort a single attempt after two minutes
    misfire_grace_time=300,   # drop slots older than five minutes
    coalesce=True,            # collapse a missed backlog into one run
    allow_concurrent=False,   # skip a slot if the previous run is still going
)

Events

from tickforge import EventType

def on_error(event):
    print("job failed:", event.job.name, event.run.error)

scheduler.add_listener(on_error, EventType.JOB_ERROR)
scheduler.add_listener(lambda e: print(e.to_dict()))   # all events

Available types: SCHEDULER_STARTED, SCHEDULER_STOPPED, SCHEDULER_PAUSED, SCHEDULER_RESUMED, JOB_ADDED, JOB_REMOVED, JOB_MODIFIED, JOB_SUBMITTED, JOB_EXECUTED, JOB_ERROR, JOB_RETRY, JOB_MISSED, JOB_SKIPPED, JOB_FINISHED.

Logging

from tickforge import configure_logging

configure_logging(level="DEBUG", fmt="json", log_file="/var/log/tickforge.log")

Records emitted during a run carry job_id, run_id and job_name automatically.

CLI

# register jobs
tickforge add myapp.tasks:cleanup --interval 1h --name nightly-cleanup
tickforge add myapp.tasks:report --cron "0 9 * * mon-fri" --timezone Europe/Paris
tickforge add myapp.tasks:ping --at "2026-06-01 12:00" --retries 2 --timeout 30

# inspect
tickforge list
tickforge show nightly-cleanup
tickforge next nightly-cleanup --count 10
tickforge history --limit 20

# control
tickforge pause nightly-cleanup
tickforge resume nightly-cleanup
tickforge run-now nightly-cleanup
tickforge remove nightly-cleanup --yes

# serve
tickforge start --poll 1 --concurrency 20

Global options: --db (path or store URI, also read from TICKFORGE_DB), -v/-vv, -q, --log-format text|json, --log-file.

The default store is ~/.tickforge/jobs.db.

Running as a service

[Unit]
Description=tickforge scheduler
After=network.target

[Service]
Environment=TICKFORGE_DB=/var/lib/tickforge/jobs.db
ExecStart=/usr/local/bin/tickforge --log-format json start
Restart=always
User=tickforge

[Install]
WantedBy=multi-user.target

Development

pip install -e ".[dev]"
pytest
ruff check src
black src
mypy src

License

MIT — see LICENSE.

Download files

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

Source Distribution

tickforge-0.1.1.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

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

tickforge-0.1.1-py3-none-any.whl (35.9 kB view details)

Uploaded Python 3

File details

Details for the file tickforge-0.1.1.tar.gz.

File metadata

  • Download URL: tickforge-0.1.1.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.0

File hashes

Hashes for tickforge-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6bf25455a942eb8f8fae62c6c8006b18eb40aa4560a8bb586213ed1692d3c6e4
MD5 0bf9c517a35979d963423b78b2f47caa
BLAKE2b-256 74d8462a24b4a4fc98e8918592cb40bbea20c6ad16d2df2662e04c18841a2e89

See more details on using hashes here.

File details

Details for the file tickforge-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: tickforge-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 35.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.0

File hashes

Hashes for tickforge-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cd20ceab4d646dd94d2fe350b9c6c9d2a685dea3acab716891c2faafa9b8dd55
MD5 810f8a4596425d6f93070f901b263d04
BLAKE2b-256 68380fc753853628fdadd5c6977e9f04a5e441f5010063b8db1ec03f8ca3bfd2

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page