HH/config/celery_scheduler.py
2026-03-28 14:03:56 +03:00

71 lines
2.4 KiB
Python

"""
Custom Celery Beat scheduler to fix Python 3.12 zoneinfo compatibility issue.
This patches the TzAwareCrontab class to work with zoneinfo.ZoneInfo instead of pytz.
The error occurs because django-celery-beat assumes pytz timezone objects
which have a `normalize()` method that zoneinfo.ZoneInfo doesn't have.
"""
import functools
# Flag to track if patch has been applied
_patch_applied = False
def apply_tzcrontab_patch():
"""
Apply monkey-patch to django_celery_beat.tzcrontab.TzAwareCrontab
to fix zoneinfo.ZoneInfo compatibility.
This should be called at Celery app initialization.
"""
from django_celery_beat import tzcrontab
original_init = tzcrontab.TzAwareCrontab.__init__
@functools.wraps(original_init)
def patched_init(self, *args, **kwargs):
# Get the tz argument, default to None
tz = kwargs.get('tz', None)
# Check if it's a zoneinfo.ZoneInfo (no 'normalize' attribute)
if tz is not None and not hasattr(tz, 'normalize'):
# Replace with a patched nowfun that works with zoneinfo
def zoneinfo_aware_nowfunc():
"""Get current time in the scheduler's timezone using Django's timezone utility."""
from django.utils import timezone as django_timezone
now = django_timezone.now()
return now.astimezone(self.tz)
# Store the zoneinfo-compatible nowfun
self._zoneinfo_nowfun = zoneinfo_aware_nowfunc
# Call original init
original_init(self, *args, **kwargs)
# If we detected zoneinfo, override the nowfun
if hasattr(self, '_zoneinfo_nowfun'):
self.nowfun = self._zoneinfo_nowfun
tzcrontab.TzAwareCrontab.__init__ = patched_init
class PatchedDatabaseScheduler:
"""
Wrapper class that applies the zoneinfo patch before importing the real scheduler.
This class is referenced in CELERY_BEAT_SCHEDULER setting and lazily imports
the actual DatabaseScheduler after applying the patch.
"""
def __new__(cls, *args, **kwargs):
"""Apply patch and return the actual DatabaseScheduler instance."""
global _patch_applied
if not _patch_applied:
apply_tzcrontab_patch()
_patch_applied = True
from django_celery_beat.schedulers import DatabaseScheduler
return DatabaseScheduler(*args, **kwargs)