Tasks¶
future.interfaces.ITask is the base for scheduled work — extend it, implement async def run, and pass instances into Lifespan (startup, shutdown, or cron_tasks).
from future.interfaces.ITask import ITask
from future.scheduling import Unit, Weekday, WorkingHours
ITask fields¶
| Attribute | Role |
|---|---|
name |
Label in logs / scheduler |
interval |
How often (required for cron_tasks) |
unit |
Unit.SECONDS / MINUTES / HOURS / DAYS |
start_time |
First run time (datetime); default is “now” when registered |
jitter |
Optional extra delay 0 … jitter seconds on each next-run calculation |
working_hours |
Optional WorkingHours constraint for interval tasks |
Cron tasks need name, interval, and unit. Startup / shutdown tasks only need name and run (interval is ignored).
Define a task¶
from future.interfaces.ITask import ITask
from future.scheduling import Unit
class ScrapeTask(ITask):
name = "scrape"
interval = 1
unit = Unit.HOURS
async def run(self) -> None:
...
class BootTask(ITask):
name = "boot"
async def run(self) -> None:
...
Working hours¶
An interval task can be restricted to a local-time window. The default weekdays are Monday through Friday:
from datetime import time
from future.interfaces.ITask import ITask
from future.scheduling import Unit, WorkingHours
class SyncCustomers(ITask):
name = "sync-customers"
interval = 15
unit = Unit.MINUTES
working_hours = WorkingHours(
start=time(8),
end=time(17),
timezone="Europe/Oslo",
)
async def run(self) -> None:
...
Pass weekdays to customize the opening days:
working_hours = WorkingHours(
start=time(22),
end=time(6),
timezone="Europe/Oslo",
weekdays=frozenset({Weekday.FRIDAY, Weekday.SATURDAY}),
)
The start is inclusive and the end is exclusive. Overnight windows belong to the day on which they open, so Friday 22:00–06:00 includes early Saturday morning. If an interval becomes due while the window is closed, it runs once when the next window opens; missed intervals are not replayed. Time-zone and daylight-saving transitions are handled with the standard-library zone database.
Wire into Lifespan¶
from datetime import datetime, timedelta
from future.application import Future
from future.lifespan import Lifespan
from app.tasks.ScrapeTask import ScrapeTask
from app.tasks.CleanupTask import CleanupTask
from future.tasks.CheckDNSTask import CheckDNSTask
startup_tasks = [
BootTask(),
]
shutdown_tasks = [
CleanupTask(),
]
cron_tasks = [
ScrapeTask(),
CheckDNSTask(domain="example.com"),
]
lifespan = Lifespan(
startup_tasks=startup_tasks,
shutdown_tasks=shutdown_tasks,
cron_tasks=cron_tasks,
)
app = Future(lifespan=lifespan, config=config)
Startup and shutdown¶
On ASGI lifespan enter, Future runs each startup_tasks entry in order (await task.run()), then starts the scheduler and registers cron_tasks.
On exit, the scheduler stops, then shutdown_tasks run the same way.
Interval (cron) tasks¶
Fixed intervals with optional working-hour constraints — not crontab expressions. future.taskscheduler.CronScheduler checks about once per second and runs due tasks concurrently (asyncio.create_task). Errors are logged; last_run is not updated on failure so the task retries on the next eligible cycle.
Each uvicorn worker runs its own scheduler (no cross-worker lock).
Generate a task stub¶
future make:task Cleanup
Creates app/tasks/CleanupTask.py extending ITask — add it to a Lifespan list:
from app.tasks.CleanupTask import CleanupTask
cron_tasks = [CleanupTask()]
Built-in examples¶
future.tasks.CheckDNSTask, future.tasks.CheckHttpStatusTask, future.tasks.DailyBackupTask, and similar modules provide small sample tasks. Prefer app-specific modules under app/tasks/ for real work.
See Lifespan for the ASGI wrapper that runs these lists.