aiocron: Crontabs for asyncio
aiocron is a Python library that enables scheduling asynchronous functions using crontab-like syntax within an asyncio event loop. It provides a decorator for coroutines, making it straightforward to define recurring tasks. Currently at version 2.1, the library has a moderate release cadence, with notable breaking changes between its 1.x and 2.x major versions.
Common errors
-
ModuleNotFoundError: No module named 'croniter'
cause You are using aiocron version 2.0 or later, which has replaced 'croniter' with 'cronsim' for cron expression parsing. Your code or an indirect dependency might be trying to access 'croniter'.fixIf your code directly imports 'croniter', you need to either remove that import if it's no longer necessary with aiocron 2.x, or adjust to use 'cronsim'. If you are relying on older aiocron behavior, consider pinning your aiocron version to `<2.0`. -
RuntimeError: no running event loop (or: Task exception was never retrieved)
cause This typically occurs because the asyncio event loop required by aiocron was not started, was stopped prematurely, or a scheduled coroutine raised an unhandled exception.fixEnsure your main application entry point starts and keeps the event loop running, commonly with `asyncio.get_event_loop().run_forever()` within a `asyncio.run()` block, or by letting `asyncio.run()` manage the top-level async function that contains your aiocron setups. Also, add `try...except` blocks within your scheduled coroutines to catch and log exceptions, preventing them from silently breaking the loop. -
TypeError: object NoneType can't be used in 'await' expression (or: AttributeError: 'NoneType' object has no attribute 'start')
cause This error often indicates that the function decorated with `@aiocron.crontab` is not defined as an `async def` coroutine, or if `start=False` was used, the `.start()` method was not explicitly called on the returned crontab object.fixVerify that any function decorated with `@aiocron.crontab` is indeed an `async def` function. If you set `start=False` in the decorator (e.g., `@aiocron.crontab('* * * * *', start=False)`), you must manually start the job by calling `.start()` on the returned crontab object: `my_scheduled_job = run_my_task; my_scheduled_job.start()`.
Warnings
- breaking Version 2.0 and later of aiocron switched its underlying cron expression parsing library from 'croniter' to 'cronsim'. This is a breaking change as specific nuances in cron expression interpretation might differ, potentially causing existing schedules to behave unexpectedly or fail validation.
- gotcha aiocron operates within an asyncio event loop. Improper handling or termination of the event loop can lead to scheduled tasks not running, unexpected exits, or unhandled exceptions in background tasks. This is particularly common when integrating with other async libraries (e.g., discord.py) that manage their own event loops or when a top-level `asyncio.run()` exits prematurely.
- deprecated aiocron versions 2.x and above have dropped support for Python 3.7 and 3.8. Attempting to install or run these versions on unsupported Python interpreters will result in errors.
Install
-
pip install aiocron
Imports
- crontab
import CoroCron
from aiocron import crontab
Quickstart
import aiocron
import asyncio
import datetime
async def my_scheduled_task():
"""A simple asynchronous task."""
current_time = datetime.datetime.now().strftime('%H:%M:%S')
print(f"Hello from scheduled task! Current time: {current_time}")
async def main():
# Schedule 'my_scheduled_task' to run every minute using the crontab decorator.
# By default, start=True, so it begins scheduling immediately.
@aiocron.crontab('* * * * *')
async def run_my_task():
await my_scheduled_task()
print("aiocron scheduler started. Task 'run_my_task' will run every minute.")
# Keep the asyncio event loop running indefinitely for scheduled tasks.
await asyncio.get_event_loop().run_forever()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nScheduler stopped by user.")