کتابخانه schedule در پایتون
کتابخانه schedule یک ابزار سبک و انسانپسند برای زمانبندی وظایف در پایتون است. این کتابخانه امکان تعریف تسکهای تکرارشونده (مثلاً هر ۵ دقیقه یا هر روز ساعت ۰۸:۰۰) را با سینتکس خوانا و ساده فراهم میکند. بر خلاف کرون که مبتنی بر رشتههای متنی است، schedule با توابع و اشیاء کار میکند و مناسب اسکریپتهای کوچک تا متوسط است.
ویژگیهای اصلی
- سینتکس ساده: every().minute.do(func)
- حمایت از واحدهای زمانی رایج: seconds, minutes, hours, days, weeks
- امکان تعیین روزهای هفته و زمان مشخص: every().monday.at(“10:30”)
- قابلیت تگگذاری و لغو گروهی تسکها
- قابلیت مشاهده next_run برای هر Job
محدودیتهای مهم
- خودِ schedule حلقه اجرایی دائمی ندارد؛ باید با run_pending() آن را در حلقه یا ترد اجرا کنید.
- پشتیبانی از timezone داخلی ندارد؛ زمانها بر اساس زمان محلی سیستم هستند.
- برای کارهای بسیار پیچیده، تراکنشمحور یا با نیاز به تضمین اجرا یکبار، بهتر است از ابزارهای قویتری مثل Celery یا APScheduler استفاده کنید.
نمونههای پایه و کاربردی
نمونه ساده: اجرای تابع هر ۱۰ ثانیه
import schedule
import time
def job():
print("Hello, world!")
schedule.every(10).seconds.do(job)
while True:
schedule.run_pending()
time.sleep(1)
در این مثال یک تابع ساده تعریف شده که هر ۱۰ ثانیه اجرا میشود. تابع run_pending() وظیفه بررسی و اجرای تسکهای زمانبندیشده را بر عهده دارد و معمولاً داخل یک حلقهٔ نامحدود قرار میگیرد. افزودن time.sleep(1) باعث مصرف کمتر CPU میشود.
اجرای روزانه در ساعت مشخص
import schedule
import time
from datetime import datetime
def backup():
print("Running backup at", datetime.now())
schedule.every().day.at("02:30").do(backup)
while True:
schedule.run_pending()
time.sleep(30)
با every().day.at("HH:MM") میتوان تسکهای روزانه را در ساعت مشخص اجرا کرد. توجه کنید که فرمت ساعت باید “HH:MM” باشد و بر اساس زمان محلی سیستم عمل میکند.
API کلیدی: جدول مرجع
| متد | توضیح |
|---|---|
| every(interval) | شروع تنظیم بازه (مثلاً every(5) یا every()) |
| .seconds/.minutes/.hours/.days/.weeks | واحد زمانی |
| .at(“HH:MM”) | زمان دقیق برای روزها یا هفته |
| .do(func, *args) | تخصیص تابع جهت اجرا |
| run_pending() | اجرا کردن تسکهای آماده |
| run_all() | اجرای همهٔ تسکها فوراً |
| cancel_job(job) | لغو یک Job |
| clear(tag=None) | حذف همه یا بر اساس تگ |
| jobs | لیست Jobها |
اجرای schedule در پسزمینه با Thread
import schedule
import time
import threading
def job():
print("Background job")
schedule.every(1).minutes.do(job)
stop_event = threading.Event()
def run_scheduler():
while not stop_event.is_set():
schedule.run_pending()
time.sleep(1)
t = threading.Thread(target=run_scheduler, daemon=True)
t.start()
# برنامهٔ اصلی ادامه پیدا میکند
try:
while True:
time.sleep(10)
except KeyboardInterrupt:
stop_event.set()
t.join()
این الگو به شما اجازه میدهد تا زمانبندی در یک ترد جداگانه اجرا شود و برنامهٔ اصلی کارهای دیگری انجام دهد. از Event برای متوقفسازی ایمن استفاده شده است. همچنین ترد با daemon=True میتواند با خروج برنامه بسته شود؛ اما برای پاکسازی بهتر از Event و join استفاده میکنیم.
ادغام با asyncio
کتابخانه schedule بهصورت بُردی async نیست، اما میتوان آن را با asyncio ترکیب کرد. نمونه زیر اجرای run_pending() را در حلقهٔ asyncio زمانبندی میکند:
import schedule
import asyncio
def job():
print("Async-friendly job")
schedule.every(5).seconds.do(job)
async def scheduler_loop():
while True:
schedule.run_pending()
await asyncio.sleep(1)
async def main():
task = asyncio.create_task(scheduler_loop())
await asyncio.sleep(20) # شبیهسازی کار اصلی
task.cancel()
asyncio.run(main())
در این مثال از asyncio.sleep استفاده شده تا حلقهٔ asyncio مسدود نشود. اگر وظایف شما خودشان async هستند، میتوانید درون یک تابع wrapper از asyncio.create_task برای اجرای تابع غیرهمزمان استفاده کنید.
مدیریت خطاها و تکرار اجرا
- هر تابعی که به
do()داده میشود باید استحکام لازم را داشته باشد یا درون try/except باشد تا خطاها باعث توقف scheduler نشوند. - برای جلوگیری از اجرای همزمان یک تسک (race) در تردهای متعدد، از قفل (Lock) استفاده کنید یا طراحی تسک را idempotent کنید.
- وقتی نیاز به اجرای یک بار (one-shot) دارید، میتوانید پس از اجرا job را لغو کنید یا شرطی در تابع قرار دهید.
نمونه: اجرای یک بار و حذف خودکار
import schedule
import time
def one_shot():
print("Run once")
return schedule.CancelJob
schedule.every().day.at("23:59").do(one_shot)
while True:
schedule.run_pending()
time.sleep(10)
اگر تابع مقدار schedule.CancelJob را برگرداند، آن Job پس از اجرا حذف میشود. این روش برای اجرای یک بار مناسب است.
نکات پیشرفته و بهترین شیوهها
- برای مدیریت timezone از کتابخانههایی مثل pytz یا pendulum استفاده کنید و زمانهای محلی را قبل از ارسال به schedule تنظیم کنید.
- برای بارگذاری/ذخیرهٔ تنظیمات (persistence) jobها باید خودتان وضعیت را ذخیره کنید؛ schedule این قابلیت را ندارد.
- برای بارگذاری متغیرهای پیکربندی (مثلاً بازهها یا زمانها) از فایل YAML یا دیتابیس استفاده کنید تا تغییر زمانبندی بدون تغییر کد ممکن شود.
- اگر نیاز به سرور زمانبندی توزیعشده یا تضمین اجرای once-only دارید، از سیستمهایی مانند Celery، APScheduler یا Airflow بهره ببرید.
جمعبندی و توصیهها
کتابخانه schedule ابزار ساده و مناسبی برای بسیاری از اسکریپتها و سرویسهای سبک است که نیاز به اجرای دورهای وظایف دارند. برای استفاده بهینه:
- وظایف را کوچک، مقاوم و idempotent بنویسید.
- اجرا در پسزمینه را با Thread یا asyncio مدیریت کنید.
- برای نیازهای پیچیدهتر یا توزیعشده به ابزارهای حرفهایتر فکر کنید.
در صورتی که مایل باشید، میتوانم نمونههای عملی بیشتری شامل حفظ وضعیت jobها، مدیریت خطاهای شبکه در تسکها و الگوهای طراحی برای زمانبندی در سرویسهای واقعی ارائه دهم.
آیا این مطلب برای شما مفید بود ؟





