
    AHjf              
         U d dl m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
Z
d dlZd dlmZmZ d dl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 d	dlmZ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/m0Z0m1Z1m2Z2m3Z3 d	dl4m5Z5  G d d      Z6 G d d      Z7g a8de9d<   dddddeddddf
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ:ddZ;y)    )annotationsN)CallableSequence)datetime	timedeltatimezone)Any)croniter)Redis)Pipeline   )cron_scheduler_registry)DEFAULT_LOGGING_DATE_FORMATDEFAULT_LOGGING_FORMATDEFAULT_RESULT_TTL)SchedulerNotFoundStopRequested)Job)setup_loghandlers)Queue)resolve_serializer)	NOT_JSON_SERIALIZABLEdecode_redis_hashnormalize_config_pathnowsafe_json_dumpsstr_to_date	utcformatutcparsevalidate_absolute_path)Webhookc                      e Zd ZdZdddddddeddddf	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d
dZddZddZddZddZ	ddZ
edd	       Zy)CronJobz2Represents a function to be run on a time intervalNc                   |r|rt        d      |s|st        d      |r-t        |t              rt        d |D              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&              | _        ||	|
|||rt)        |      nd 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 parameterc              3  <   K   | ]  }t        |t                y wN)
isinstancer!   ).0webhooks     ?/root/tools/cai/cai_env/lib/python3.12/site-packages/rq/cron.py	<genexpr>z#CronJob.__init__.<locals>.<genexpr>B   s     9o[b*Wg:V9os   z0webhooks must be a sequence of Webhook instances.z)Either func or func_name must be provided )job_timeout
result_ttlttlfailure_ttlmetawebhooks)
ValueErrorr'   r   all	TypeErrorfunc
__module____name__	func_nameargskwargsintervalcron
queue_namenext_enqueue_timelatest_enqueue_timer
   r   get_nextr   listjob_optionsitems)selfr?   r7   r:   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   	cron_iterkvs                    r*   __init__zCronJob.__init__,   sQ     OPPMNN8X.c9ofn9o6oNOO)-DI%)__$5Qt}}o"FDNDI&DNHII:2	"Lb$, $	)2648  99 CE2I%.%7%7%AD"&$& +3X	,
 .2-=-=-C-C-EWTQAqDWWs   	
E#E#c                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.
connectionzEnqueued job z
 to queue )r7   r4   r   r?   enqueuer;   r<   rD   logging	getLoggerr9   info)rF   rM   queuejobs       r*   rN   zCronJob.enqueueh   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
   rA   r   rB   r   r=   r   max)rF   rG   s     r*   get_next_enqueue_timezCronJob.get_next_enqueue_times   sj    99 D,D,D,MNI%%h//]]t77++i.NNN||rT   c                |    | j                   s| j                  sy| j                  rt               | j                  k\  S y)z Check if this job should run nowTF)rA   r>   r@   r   rF   s    r*   
should_runzCronJob.should_run   s7     ''		 !!5D2222rT   c                n    || _         | j                  | j                  | j                         | _        yy)z<Set latest run time to a given time and update next run timeN)rA   r=   r>   rX   r@   )rF   times     r*   set_enqueue_timezCronJob.set_enqueue_time   s5    #'  ==$		(=%)%?%?%AD" )>rT   c           
     n   | j                   | j                  | j                  rt        | j                        nd| j                  rt        | j                        nd| j
                  | j                  | j                  rt        | j                        nd| j                  rt        | j                        ndd}| j                  j                         D ][  \  }}|	|dk(  rt        |      ||<   |dk(  r5t        j                  |D cg c]  }|j                          c}      ||<   W|||<   ] |S c c}w )z@Convert CronJob instance to a dictionary for monitoring purposesN)r:   r?   r;   r<   r=   r>   rA   r@   r2   r3   )r:   r?   r;   r   r<   r=   r>   rA   r   r@   rD   rE   jsondumpsto_dict)rF   objrH   rI   r)   s        r*   rb   zCronJob.to_dict   s     //26))ODII.6:kkodkk2tIIJNJbJb9T-E-E#FhlFJF\F\4+A+A!Bbf	
 $$**, 	DAq};,Q/CF*_!ZZ!(Lw):(LMCFCF	 
 )Ms   D2
c                r   |j                  d      }|r'|t        k7  rt        t        j                  |            }|j                  d      }|r|t        k7  rt        j                  |      }|j                  d      }|r|t        k7  rt        j                  |      }|j                  d      }|r?|t        k7  r6t        j                  |      D cg c]  }t        j                  |       }}nd} | |d   |d   |||j                  d      |j                  d	      |j                  d
      |j                  dt              |j                  d      |j                  d      ||      }|j                  d      rt        |d         |_	        |j                  d      rt        |d         |_
        |S c c}w )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.
        r;   r<   r2   r3   Nr?   r:   r=   r>   r.   r/   r0   r1   )r?   r:   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   rA   r@   )getr   tupler`   loadsr!   	from_dictr   r   rA   r@   )clsdatar;   r<   r2   r3   r)   rS   s           r*   rh   zCronJob.from_dict   s    xxD11D)*D (#f 55ZZ'F xxD11::d#D 88J'$99BF**XBVWw))'2WHWHL);'XXj)&!/xx.@A/
  88)*&.t4I/J&KC#88'($,T2E-F$GC!
5 Xs   F4)r?   strr7   zCallable | Noner:   
str | Noner;   tuple | Noner<   dict | Noner=   
int | Noner>   rl   r.   ro   r/   intr0   ro   r1   ro   r2   rn   r3   Sequence[Webhook] | None)rM   r   returnr   )rr   r   rr   bool)r]   r   rr   None)rr   dict[str, Any])rj   rv   rr   r#   )r9   r8   __qualname____doc__r   rJ   rN   rX   r[   r^   rb   classmethodrh   r-   rT   r*   r#   r#   )   s    <
 !% $!"#"&,"& -1:X:X :X 	:X
 :X :X :X :X  :X :X :X  :X :X +:Xx	
B0 3 3rT   r#   c            
      T   e Zd ZdZej
                  df	 	 	 	 	 ddZddZddZddddde	ddddf
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ
ddZdd	Zdd
Zd Zd Zd Zd dZed!d       Zd"dZd#d$dZd%dZd&dZed'd       Zed(d)d       Zd%dZd#d$dZd%dZed*d       Zy)+CronSchedulerz*Simple interval-based job scheduler for RQ c                $   || _         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:   r|   )levelname
log_formatdate_formatF)rM   
_cron_jobssocketgethostnamehostnameosgetpidpiduuiduuid4hexr   config_filer   
created_atr   
serializerrO   rP   r9   loghasHandlersr   r   r   	propagate)rF   rM   logging_levelr   s       r*   rJ   zCronScheduler.__init__   s     ",)+#//1		UDMM?!DHH:Qtzz|?O?OPRQR?S>T!U	 "$'E,.#*#4#4X#>xx##%#17	 "'DHH &rT   c                b    t        || j                        sy| j                  |j                  k(  S )z;Equality does not take the database/connection into accountF)r'   	__class__r   )rF   others     r*   __eq__zCronScheduler.__eq__   s&    %0yyEJJ&&rT   c                ,    t        | j                        S )z;The hash does not take the database/connection into account)hashr   rZ   s    r*   __hash__zCronScheduler.__hash__  s    DIIrT   Nc                @   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?   r7   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   r,   zRegistered 'z' to run on z every z secondsz with cron schedule '')r#   r   appendr8   r9   r   rQ   )rF   r7   r?   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   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rT   c                    | j                   S )zGet all registered cron jobs)r   rZ   s    r*   get_jobszCronScheduler.get_jobs2  s    rT   c                    t               }g }| j                  D ]P  }|j                         s|j                  | j                         |j                  |       |j                  |       R |S )z$Enqueue all jobs that are due to run)r   r   r[   rN   rM   r^   r   )rF   enqueue_timeenqueued_jobsrS   s       r*   enqueue_jobszCronScheduler.enqueue_jobs6  s_    u')?? 	*C~~DOO,$$\2$$S)		*
 rT   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   r   r@   mintotal_seconds)rF   current_timerS   next_job_timesclosest_timeseconds_until_nexts         r*   calculate_sleep_intervalz&CronScheduler.calculate_sleep_intervalA  s~     u <@??dCcNcNc#//dd >* +\9HHJ " %r**! e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SIGTERMrZ   s    r*   _install_signal_handlersz&CronScheduler._install_signal_handlers\  s2    fmmT%7%78fnnd&8&89rT   c                d    | j                   j                  d| j                  |       t               )z#Handle shutdown signals gracefully.z-CronScheduler %s: received shutdown signal %s)r   rQ   r   r   )rF   signumframes      r*   r   zCronScheduler._request_stopa  s$    EtyyRXYorT   c                8   | j                   j                  d| j                         | j                          | j	                          	 	 | j                         }|r| j                          | j                          | j                         }|dkD  r4| j                   j                  d| d       t        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   rQ   r   r   register_birthr   save_jobs_data	heartbeatr   debugr]   sleepKeyboardInterruptr   register_death)rF   enqueued
sleep_times      r*   startzCronScheduler.startf  s8   5tyyA 	%%'	L,,.'') !::<
>HHNN]:,k#JKJJz*  ! 	UHHMMH$))T 	IHHMM<diiH	I !HHMM?K !HHMM?Ks1   A=C /D'4E! 6.D'$E! &D''E! !8Fc                b   || _         | 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 t        j7                          | 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: r7   r   zFailed to register job z from config: T)exc_infozSuccessfully registered z cron jobs from 'r   r-   )r   r   rQ   _job_data_registryr   pathisabsr   r    basenamereplace	importlibutilspec_from_file_locationloadererrorImportErrormodule_from_specsysmodulesexec_module	Exceptionr   reloadimport_moduler9   r   clear)
rF   config_pathmodule_namespec	error_msgmoduleenormalized_path	job_countrj   s
             r*   load_config_from_filez#CronScheduler.load_config_from_file  sZ    '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J A,K) 6M*	K&AK!!K&)	M'22L$$M'02M""M'*	N.30N))N.c                     d| j                    S )z)Redis key for this CronScheduler instancerq:cron_scheduler:)r   rZ   s    r*   keyzCronScheduler.key  s     $DII;//rT   c                $   | j                   t        | j                        | j                  t	        | j
                        | j                  xs dt        j                  | j                  D cg c]  }|j                          c}      d}|S c c}w )z@Convert CronScheduler instance to a dictionary for Redis storager|   )r   r   r   r   r   	cron_jobs)r   rk   r   r   r   r   r   r`   ra   r   rb   )rF   rS   rc   s      r*   rb   zCronScheduler.to_dict  sl     txx=II#DOO4++1rdoo$NsS[[]$NO
 
 %Os   +Bc                    ||n| j                   }|j                  | j                  | j                                |j	                  | j                  d       y)z2Save CronScheduler instance to Redis hash with TTLN)mappingr   )rM   hsetr   rb   expire)rF   pipelinerM   s      r*   savezCronScheduler.save  s@    !)!5X4??
$,,.9$((B'rT   c                    t        j                  | j                  D cg c]  }|j                          c}      }| j                  j                  | j                  d|       yc c}w )zSave cron jobs data to Redis.r   N)r`   ra   r   rb   rM   r   r   )rF   rS   rj   s      r*   r   zCronScheduler.save_jobs_data  sE    zzDOODS3;;=DETXX{D9 Es   A$c                   t        |d      }|d   | _        t        |j                  dd            | _        |d   | _        t        |d         | _        |d   | _        |j                  d	      rB	 t        j                  |d	         }|D cg c]  }t        j                  |       c}| _        yg | _        yc c}w # t        j                  t        t         f$ r/}| j"                  j%                  d
|        g | _        Y d}~yd}~ww xY w)z4Restore CronScheduler instance from Redis hash data.T)decode_valuesr   r   r   r   r   r   r   zFailed to restore cron jobs: N)r   r   rp   re   r   r   r   r   r   r`   rg   r#   rh   r   JSONDecodeErrorKeyErrorr6   r   warning)rF   raw_datarc   	jobs_datajob_datar   s         r*   restorezCronScheduler.restore  s    =Jswwua()K	%c,&78}- 77;% JJs;'78	OX"Y87#4#4X#>"Y !DO #Z(((I> %  #@!DE"$%s*   1B? B:)B? :B? ?D%DDc                    d| }|j                  |      }|st        d| d       | ||      }|j                  |       |S )z2Fetch a CronScheduler instance from Redis by name.r   zCronScheduler with name 'z' not found)rM   r   )hgetallr   r   )ri   r   rM   r   r   	schedulers         r*   fetchzCronScheduler.fetch	  sX     #4&)%%c*#&?v[$QRR:D9	(#rT   c                   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   cleanupget_keysr   r   r   )ri   rM   r  r   scheduler_names
schedulersr   r   s           r*   r5   z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   rQ   r   rM   r   r   r   r   executerF   r   s     r*   r   zCronScheduler.register_birth0  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   rQ   r   r   
unregisterr  s     r*   r   zCronScheduler.register_death9  s0    tyyk1GHI**4:rT   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)rM   r   zaddr   get_registry_keyr   r]   r   r   r  r   r   r   )rF   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)	rM   zscorer   r  r   r   fromtimestampr   utc)rF   scores     r*   last_heartbeatzCronScheduler.last_heartbeatN  sL     &&'>'O'O'QSWS\S\]= %%e==rT   )rM   r   r   z	str | intr   rk   rs   )rr   rp   )r7   r   r?   rk   r;   rm   r<   rn   r=   ro   r>   rl   r.   ro   r/   rp   r0   ro   r1   ro   r2   rn   r3   rq   rr   r#   )rr   zlist[CronJob])rr   float)r   rk   )rr   rk   )rr   dictr&   )r   zPipeline | Nonerr   ru   )rr   ru   )r   r  rr   ru   )r   rk   rM   r   rr   r{   )T)rM   r   r  rt   rr   zlist[CronScheduler])rr   zdatetime | None)r9   r8   rw   rx   rO   INFOrJ   r   r   r   r   r   r   r   r   r   r   r   propertyr   rb   r   r   r   ry   r   r5   r   r   r   r  r-   rT   r*   r{   r{      s}   4
 $+<<	'' !' 	'2' ""#"&,"& -1'' ' 	'
 ' ' '  ' ' '  ' ' +' 
'R	+6:

L8R]j 0 0
(:
!, 
 
  2;
s  > >rT   r{   z
list[dict]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.
    )r7   r?   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   r,   zCron config: Adding job 'z' to registry for queue )r   r   rO   rP   r9   r8   r   )r7   r?   r;   r<   r=   r>   r.   r/   r0   r1   r2   r3   r   loggerr   s                  r*   r   r   b  s    @  " "H h' x(F!4==/2G
LL,WI5Mj\Z[OrT   c                    t        |       }t        D ]9  }t        j                  d|d   j                           |j
                  di | ; |S )z8Create a CronScheduler instance with all registered jobsrL   zRegistering job: r7   r-   )r{   r   rO   r   r9   r   )rM   cron_instancerj   s      r*   create_cronr#    sW    !Z8M # ')$v,*?*?)@AB&&' rT   )r7   r   r?   rk   r;   rm   r<   rn   r=   ro   r>   rl   r.   ro   r/   rp   r0   ro   r1   ro   r2   rn   r3   rq   rr   r  )rM   r   rr   r{   )<
__future__r   importlib.utilr   r`   rO   r   r   r   r   r]   r   collections.abcr   r   r   r   r   typingr	   r
   redisr   redis.clientr   r|   r   defaultsr   r   r   
exceptionsr   r   rS   r   logutilsr   rR   r   serializersr   utilsr   r   r   r   r   r   r   r   r    r)   r!   r#   r{   r   __annotations__r   r#  r-   rT   r*   <module>r0     s4   "    	   
   . 2 2    ! % ] ] 8  '  +
 
 
 w wtx> x>x "$ J # "(")-5
55 5 	5
 5 5 5 5 
5 5 5 '5 
5p	rT   