
    ]jT                        U d dl Zd dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	m	Z	m
Z
mZ d dlmZmZmZmZmZmZmZ d dlmZ d dlmZ d dlmZ ddlmZ dd	lmZmZ dd
lmZm Z  ddl!m"Z" ddl#m$Z$ ddl%m&Z& ddl'm(Z( ddl)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/  G d d      Z0 G d d      Z1g a2ee   e3d<   	 	 	 	 	 	 	 	 	 d$dede4dee   dee   dee5   dee4   dee5   de5dee5   dee5   dee6   d efd!Z7d"ed e1fd#Z8y)%    N)datetime	timedeltatimezone)AnyCallableDictListOptionalTupleUnion)croniter)Redis)Pipeline   )cron_scheduler_registry)DEFAULT_LOGGING_DATE_FORMATDEFAULT_LOGGING_FORMAT)SchedulerNotFoundStopRequested)Job)setup_loghandlers)Queue)resolve_serializer)decode_redis_hashnormalize_config_pathnowstr_to_date	utcformatvalidate_absolute_pathc                      e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 ddedee   dee   dee   dee   dee	   d	ee   d
ee	   de	dee	   dee	   dee
   fdZdedefdZdefdZdefdZdeddfdZdeeef   fdZedeeef   dd fd       Zy)CronJobz2Represents a function to be run on a time intervalN
queue_namefunc	func_nameargskwargsintervalcronjob_timeout
result_ttlttlfailure_ttlmetac                 X   |r|rt        d      |s|st        d      |r(|| _        |j                   d|j                   | _        n|rd | _        || _        nt        d      |xs d| _        |xs i | _        || _        || _        || _	        d | _
        d | _        | j                  r8t        | j                  t                     }|j                  t              | _
        ||	|
||d| _        | j                   j#                         D ci c]  \  }}|	|| c}}| _        y c c}}w )Nz0Cannot specify both interval and cron parametersz.Must specify either interval or cron parameter.z)Either func or func_name must be provided )r)   r*   r+   r,   r-   )
ValueErrorr#   
__module____name__r$   r%   r&   r'   r(   r"   next_run_timelatest_run_timer   r   get_nextr   job_optionsitems)selfr"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   	cron_iterkvs                   1/root/env/lib/python3.12/site-packages/rq/cron.py__init__zCronJob.__init__   s    OPPMNN,0DI%)__$5Qt}}o"FDNDI&DNHII:2	"Lb'/#'	)1537 99 CE2I!*!3!3H!=D&$&,
 .2-=-=-C-C-EWTQAqDWWs   
D&D&
connectionreturnc                 z   | j                   st        d      t        | j                  |      } |j                  | j                   g| j
                  i | j                  | j                  }t        j                  t              j                  d| j                   j                   d| j                          |S )z:Enqueue this job to its queue and update the next run timezUCronJob has no function to enqueue. It may have been created for monitoring purposes.r?   zEnqueued job z
 to queue )r#   r1   r   r"   enqueuer%   r&   r7   logging	getLoggerr3   info)r9   r?   queuejobs       r=   rC   zCronJob.enqueueP   s    yytuudoo*=emmDIIU		UT[[UDDTDTU(#((=9K9K8LJW[WfWfVg)hi
    c                 2   | j                   rAt        | j                   | j                  xs
 t                     }|j	                  t
              S | j                  r/| j                  r#| j                  t        | j                        z   S t
        j                  S )z@Calculate the next run time based on interval or cron expression)seconds)	r(   r   r5   r   r6   r   r'   r   max)r9   r:   s     r=   get_next_run_timezCronJob.get_next_run_time[   sj    99 D,@,@,ICEJI%%h//]]t33'')DMM*JJJ||rI   c                 |    | j                   s| j                  sy| j                  rt               | j                  k\  S y)z Check if this job should run nowTF)r5   r(   r4   r   r9   s    r=   
should_runzCronJob.should_rung   s7     ##DII 5D....rI   timec                 n    || _         | j                  | j                  | j                         | _        yy)z<Set latest run time to a given time and update next run timeN)r5   r'   r(   rM   r4   )r9   rQ   s     r=   set_run_timezCronJob.set_run_timet   s5    # ==$		(=!%!7!7!9D )>rI   c                     | j                   | j                  | j                  | j                  d}|j	                  | j
                  j                         D ci c]  \  }}|	|| c}}       |S c c}}w )z@Convert CronJob instance to a dictionary for monitoring purposes)r$   r"   r'   r(   )r$   r"   r'   r(   updater7   r8   )r9   objr;   r<   s       r=   to_dictzCronJob.to_dict|   sf     //II	
 	

T%5%5%;%;%=OTQAqDOP
 Ps   
A2
#A2
datac                      | di |S )zCreate a CronJob instance from dictionary data for monitoring purposes.

        Note: The returned CronJob will not have a func attribute and cannot be executed,
        but contains all the metadata for monitoring.
        r0   r0   )clsrX   s     r=   	from_dictzCronJob.from_dict   s     {T{rI   )NNNNNNN  NNN)r3   r2   __qualname____doc__strr
   r   r   r   intdictr>   r   r   rC   r   rM   boolrP   rS   r   rW   classmethodr[   r0   rI   r=   r!   r!      sV   <
 $(#' $!%"&"%)!%)#1X1X x 1X C=	1X
 uo1X 1X 3-1X sm1X c]1X 1X c]1X c]1X tn1Xf	% 	C 	
8 
D : :d :
c3h 
 T#s(^ 	  rI   r!   c                      e Zd ZdZej
                  dfdedeee	f   defdZ
defdZde	fd	Z	 	 	 	 	 	 	 	 	 d,dededee   dee   dee	   dee   dee	   de	dee	   dee	   dee   defdZdee   fdZdee   fdZdefdZd Zd Zd ZdefdZedefd       Zdefd Z d-d!ee!   dd
fd"Z"d#edd
fd$Z#e$dededd fd%       Z%e$d.ded&eded    fd'       Z&d/d(Z'd-d!ee!   dd
fd)Z(d/d*Z)edee*   fd+       Z+y
)0CronSchedulerz*Simple interval-based job scheduler for RQ r?   logging_levelnamec                 $   || _         g | _        t        j                         | _        t        j                         | _        |xs= | j                   d| j                   dt        j                         j                  d d  | _        d| _        t               | _        t               | _        t#        j$                  t&              | _        | j(                  j+                         s-t-        |t&        t.        t0               d| j(                  _        y y )N:   rf   )levelrh   
log_formatdate_formatF)r?   
_cron_jobssocketgethostnamehostnameosgetpidpiduuiduuid4hexrh   config_filer   
created_atr   
serializerrD   rE   r3   loghasHandlersr   r   r   	propagate)r9   r?   rg   rh   s       r=   r>   zCronScheduler.__init__   s     ",)+#//1		UDMM?!DHH:Qtzz|?O?OPRQR?S>T!U	 "$'E,.#*#4#4X#>xx##%#17	 "'DHH &rI   r@   c                 b    t        || j                        sy| j                  |j                  k(  S )z;Equality does not take the database/connection into accountF)
isinstance	__class__rh   )r9   others     r=   __eq__zCronScheduler.__eq__   s&    %0yyEJJ&&rI   c                 ,    t        | j                        S )z;The hash does not take the database/connection into account)hashrh   rO   s    r=   __hash__zCronScheduler.__hash__   s    DIIrI   Nr#   r"   r%   r&   r'   r(   r)   r*   r+   r,   r-   c                 >   t        |||||||||	|
|      }| j                  j                  |       |j                   d|j                   }|r'| j
                  j                  d| d| d| d       |S |r%| j
                  j                  d| d| d| d       |S )	z2Register a function to be run at regular intervals)r"   r#   r%   r&   r'   r(   r)   r*   r+   r,   r-   r/   zRegistered 'z' to run on z every z secondsz with cron schedule '')r!   ro   appendr2   r3   r|   rF   )r9   r#   r"   r%   r&   r'   r(   r)   r*   r+   r,   r-   cron_jobjob_keys                 r=   registerzCronScheduler.register   s     !#!#
 	x(__%Qt}}o6HHMML	j\QYPZZbcd  HHMML	j\I^_c^ddefgrI   c                     | j                   S )zGet all registered cron jobs)ro   rO   s    r=   get_jobszCronScheduler.get_jobs   s    rI   c                     t               }g }| j                  D ]P  }|j                         s|j                  | j                         |j                  |       |j                  |       R |S )z$Enqueue all jobs that are due to run)r   ro   rP   rC   r?   rS   r   )r9   enqueue_timeenqueued_jobsrH   s       r=   enqueue_jobszCronScheduler.enqueue_jobs   s_    u')?? 	*C~~DOO,  .$$S)		*
 rI   c                     t               }| j                  D cg c]  }|j                  s|j                   }}|syt        |      }||z
  j	                         }|dk  ryt        |d      S c c}w )zCalculate how long to sleep until the next job is due.

        Returns the number of seconds to sleep, with a maximum of 60 seconds
        to ensure we check regularly.
        <   r   )r   ro   r4   mintotal_seconds)r9   current_timerH   next_job_timesclosest_timeseconds_until_nexts         r=   calculate_sleep_intervalz&CronScheduler.calculate_sleep_interval   s}     u 8<\#J[J[#++\\ >* +\9HHJ " %r**! ]s
   A.A.c                     t        j                   t         j                  | j                         t        j                   t         j                  | j                         y)z.Install signal handlers for graceful shutdown.N)signalSIGINT_request_stopSIGTERMrO   s    r=   _install_signal_handlersz&CronScheduler._install_signal_handlers	  s2    fmmT%7%78fnnd&8&89rI   c                 d    | j                   j                  d| j                  |       t               )z#Handle shutdown signals gracefully.z-CronScheduler %s: received shutdown signal %s)r|   rF   rh   r   )r9   signumframes      r=   r   zCronScheduler._request_stop  s$    EtyyRXYorI   c                    | j                   j                  d| j                         | j                          | j	                          	 	 | j                          | j                          | j                         }|dkD  r4| j                   j                  d| d       t        j                  |       j# t        $ r) | j                   j                  d| j                         Y n4t        $ r) | j                   j                  d| j                         Y nw xY w| j                          | j                   j                  d| j                         y# | j                          | j                   j                  d| j                         w xY w)	zStart the cron schedulerzCronScheduler %s: starting...r   zSleeping for z seconds...z,CronScheduler %s: received KeyboardInterruptz CronScheduler %s: stop requestedz#CronScheduler %s: shutdown completeN)r|   rF   rh   r   register_birthr   	heartbeatr   debugrQ   sleepKeyboardInterruptr   register_death)r9   
sleep_times     r=   startzCronScheduler.start  s'   5tyyA 	%%'	L!!# !::<
>HHNN]:,k#JKJJz*  ! 	UHHMMH$))T 	IHHMM<diiH	I !HHMM?K !HHMM?Ks1   A+B3 3/D"E $.DE DE 8Fconfig_pathc                 >   || _         | j                  j                  d|        g at        j
                  j                  |      r!| j                  j                  d|        t        |       dt        j
                  j                  |      j                  dd       }	 t        j                  j                  ||      }||j                  +d| }| j                  j                  |       t!        |      t        j                  j#                  |      }|t$        j&                  |<   |j                  j)                  |       | j                  j                  d|        nt-        |      }| j                  j                  d|        	 |t$        j&                  v r't        j.                  t$        j&                  |          nt        j0                  |       | j                  j                  d|        d}t        D ]E  }	| j                  j                  d|	d   j2                          	  | j4                  di |	 |dz  }G g a| j                  j                  d| d| d       y# t*        $ rW}|t$        j&                  v rt$        j&                  |= d	| d
| }| j                  j                  |       t!        |      |d}~ww xY w# t         $ r7}d| d| d| }| j                  j                  |       t!        |      |d}~wt*        $ r7}d| d| d| }| j                  j                  |       t+        |      |d}~ww xY w# t*        $ r;}| j                  j                  d|	d   j2                   d| d       Y d}~d}~ww xY w)a  
        Dynamically load a cron config file and register all jobs with this Cron instance.

        Supports both dotted import paths (e.g. 'app.cron_config') and file paths
        (e.g. '/path/to/app/cron_config.py', 'app/cron_config.py'). The .py
        extension is recommended for file paths for clarity.

        Jobs defined in the config file must use the global `rq.cron.register` function.

        Args:
            config_path: Path to the cron_config.py file or module path.
        z Loading cron configuration from zLoading absolute file path: rq_cron_config_r/   _Nz!Could not create module spec for z&Successfully loaded config from file: z#Failed to load configuration file 'z': zNormalized path: z(Successfully loaded config from module: z'Failed to import configuration module 'z	' (from 'z'): z#An error occurred while importing 'r   zRegistering job from config: r#   r   zFailed to register job z from config: T)exc_infozSuccessfully registered z cron jobs from 'r   r0   )ry   r|   rF   _job_data_registryrs   pathisabsr   r   basenamereplace	importlibutilspec_from_file_locationloadererrorImportErrormodule_from_specsysmodulesexec_module	Exceptionr   reloadimport_moduler3   r   )
r9   r   module_namespec	error_msgmoduleenormalized_path	job_countrX   s
             r=   load_config_from_filez#CronScheduler.load_config_from_file,  sT    '8FG  77==%HHNN9+GH #;/ ,BGG,<,<[,I,Q,QRUWZ,[+\]K4 ~~==k;W<4;;#6"CK= QIHHNN9-%i00"88>+1K(''/!G}UV 4K@OHHNN..?@A2"ckk1$$S[[%AB++O<!I/IZ[\ 	& 	rDHHNN:4<;P;P:QRSr%%Q			r  0;L[MYZ[\W  4#++-K0A+cRSQTU	y)!),!34$  4EoEVV_`k_llpqrpst	y)!),!3 2A/ARR[\g[hhlmnlop	y)	*12  r!8f9N9N8O~^_]`alpqqrsW   $CI4 A,K 6M4	K=AKK	M 2LM2MM	N!0NNc                      d| j                    S )z)Redis key for this CronScheduler instancerq:cron_scheduler:)rh   rO   s    r=   keyzCronScheduler.key  s     $DII;//rI   c                     | j                   t        | j                        | j                  t	        | j
                        | j                  xs dd}|S )z@Convert CronScheduler instance to a dictionary for Redis storagerf   )rr   ru   rh   rz   ry   )rr   r_   ru   rh   r   rz   ry   )r9   rV   s     r=   rW   zCronScheduler.to_dict  sE     txx=II#DOO4++1r
 
rI   pipelinec                     ||n| j                   }|j                  | j                  | j                                |j	                  | j                  d       y)z2Save CronScheduler instance to Redis hash with TTLN)mappingr   )r?   hsetr   rW   expire)r9   r   r?   s      r=   savezCronScheduler.save  s@    !)!5X4??
$,,.9$((B'rI   raw_datac                     t        |d      }|d   | _        t        |j                  dd            | _        |d   | _        t        |d         | _        |d   | _        y	)
z3Restore CronScheduler instance from Redis hash dataT)decode_valuesrr   ru   r   rh   rz   ry   N)	r   rr   r`   getru   rh   r   rz   ry   )r9   r   rV   s      r=   restorezCronScheduler.restore  sY    =Jswwua()K	%c,&78}-rI   c                     d| }|j                  |      }|st        d| d       | ||      }|j                  |       |S )z1Fetch a CronScheduler instance from Redis by namer   zCronScheduler with name 'z' not found)r?   rh   )hgetallr   r   )rZ   rh   r?   r   r   	schedulers         r=   fetchzCronScheduler.fetch  sX     #4&)%%c*#&?v[$QRR:D9	(#rI   cleanupc                    ddl m} |rt        j                  |       t        j                  |      }g }|D ]:  } |t
              5  | j                  ||      }|j                  |       ddd       < |S # 1 sw Y   HxY w)a  Returns all CronScheduler instances from the registry

        Args:
            connection: Redis connection to use
            cleanup: If True, removes stale entries from registry before fetching schedulers

        Returns:
            List of CronScheduler instances
        r   )suppressN)
contextlibr   r   r   get_keysr   r   r   )rZ   r?   r   r   scheduler_names
schedulersrh   r   s           r=   allzCronScheduler.all  s     	(#++J71:::F
# 	-D+, -IIdJ7	!!),- -	-
 	- -s   $A66A?	c                 "   | j                   j                  d| j                   d       | j                  j	                         5 }t        j                  | |       | j                  |       |j                          ddd       y# 1 sw Y   yxY w)zURegister this scheduler's birth in the scheduler registry and save data to Redis hashCronScheduler z: registering birth...N)	r|   rF   rh   r?   r   r   r   r   executer9   r   s     r=   r   zCronScheduler.register_birth  so    tyyk1GHI__%%' 	8#,,T8<IIh	 	 	s   8BBc                     | j                   j                  d| j                   d       t        j                  | |       y)zJRegister this scheduler's death by removing it from the scheduler registryr   z: registering death...N)r|   rF   rh   r   
unregisterr   s     r=   r   zCronScheduler.register_death  s0    tyyk1GHI**4:rI   c                    | j                   j                         5 }|j                  t        j                         | j
                  t        j                         idd       |j                  | j                  d       |j                         }|d   }|r*| j                  j                  d| j
                   d       n)| j                  j                  d| j
                   d       ddd       y# 1 sw Y   yxY w)	zSend a heartbeat to update this scheduler's last seen timestamp in the registry
        and extend the scheduler's Redis hash TTL.
        T)xxchx   r   r   z: heartbeat sent successfullyz4: heartbeat failed - scheduler not found in registryN)r?   r   zaddr   get_registry_keyrh   rQ   r   r   r   r|   r   warning)r9   piperesultszadd_results       r=   r   zCronScheduler.heartbeat  s     __%%' 
	s4II->>@499diikBZ_chlImKK#&llnG "!*K		{:WXY  >$))<p!qr
	s 
	s 
	ss   CC00C9c                     | j                   j                  t        j                         | j                        }|yt        j                  |t        j                        S )zReturn the UTC datetime of the last heartbeat, or None if no heartbeat recorded

        Returns:
            datetime: UTC datetime of the last heartbeat, or None if scheduler not found in registry
        N)tz)	r?   zscorer   r   rh   r   fromtimestampr   utc)r9   scores     r=   last_heartbeatzCronScheduler.last_heartbeat  sL     &&'>'O'O'QSWS\S\]= %%e==rI   	NNNNNr\   NNN)N)T)r@   N),r3   r2   r]   r^   rD   INFOr   r   r_   r`   r>   rb   r   r   r   r
   r   r   ra   r!   r   r	   r   r   floatr   r   r   r   r   propertyr   rW   r   r   r   rc   r   r   r   r   r   r   r   r0   rI   r=   re   re      sD   4
 *1	'' S#X' 	'2't '#  !%!%"&"%)!%)#%% % uo	%
 % 3-% sm% c]% % c]% c]% tn% 
%N$w- 	d7m 	+% +6:

L2R] R]j 0S 0 0	 	(Xh/ (4 (. . . 
 
% 
O 
 
 U T T/=R  2;x'9 ;T ;
s  > 2 > >rI   re   r   r#   r"   r%   r&   r'   r(   r)   r*   r+   r,   r-   r@   c                     | |||||||||	|
d}t         j                  |       t        j                  t              }| j
                   d| j                   }|j                  d| d|        |S )a  
    Register a function to be run as a cron job by adding its definition
    to a temporary global registry.

    This function should typically be called from within a cron configuration file
    that will be loaded using `CronScheduler.load_config_from_file()`.

    Example (in your cron_config.py):
        from rq import cron
        from my_app.tasks import my_func

        cron.register(my_func, 'default', interval=60)  # Run every 60 seconds

    Returns:
        dict: The job data dictionary added to the registry.
    )r#   r"   r%   r&   r'   r(   r)   r*   r+   r,   r-   r/   zCron config: Adding job 'z' to registry for queue )r   r   rD   rE   r3   r2   r   )r#   r"   r%   r&   r'   r(   r)   r*   r+   r,   r-   job_dataloggerr   s                 r=   r   r     s    >  " "H h' x(F!4==/2G
LL,WI5Mj\Z[OrI   r?   c                     t        |       }t        D ]9  }t        j                  d|d   j                           |j
                  di | ; |S )z8Create a CronScheduler instance with all registered jobsrB   zRegistering job: r#   r0   )re   r   rD   r   r3   r   )r?   cron_instancerX   s      r=   create_cronr  0  sW    !Z8M # ')$v,*?*?)@AB&&' rI   r   )9importlib.utilr   rD   rs   r   rp   r   rQ   rv   r   r   r   typingr   r   r   r	   r
   r   r   r   redisr   redis.clientr   rf   r   defaultsr   r   
exceptionsr   r   rH   r   logutilsr   rG   r   serializersr   utilsr   r   r   r   r   r   r!   re   r   __annotations__r_   r`   ra   r   r  r0   rI   r=   <module>r     sE     	   
   2 2 D D D   ! % I 8  '  + p pu upa> a>J "$ DJ # !!"!%!%3
33 5/3 TN	3
 sm3 3-3 #3 3 
#3 #3 4.3 
3l	E 	m 	rI   