33from datetime import datetime
44from functools import wraps , partial
55from tzlocal import get_localzone
6- from uuid import uuid4
6+ from uuid import uuid4 , UUID
77import time
88import asyncio
99import sys
1010import inspect
11+ import typing as tp
1112
1213
13- async def null_callback (* args ) :
14+ async def null_callback (* args : tp . Any ) -> tuple [ tp . Any , ...] :
1415 return args
1516
1617
17- def wrap_func (func ) :
18+ def wrap_func (func : tp . Callable [..., tp . Union [ tp . Any , tp . Awaitable [ tp . Any ]]]) -> tp . Callable [..., tp . Awaitable [ tp . Any ]] :
1819 """wrap in a coroutine"""
1920
2021 @wraps (func )
21- async def wrapper (* args , ** kwargs ) :
22+ async def wrapper (* args : tp . Any , ** kwargs : tp . Any ) -> tp . Any :
2223 result = func (* args , ** kwargs )
2324 if inspect .isawaitable (result ):
2425 result = await result
@@ -30,70 +31,72 @@ async def wrapper(*args, **kwargs):
3031class Cron (object ):
3132 def __init__ (
3233 self ,
33- spec ,
34- func = None ,
35- args = (),
36- kwargs = None ,
37- start = False ,
38- uuid = None ,
39- loop = None ,
40- tz = None ,
41- ):
34+ spec : str ,
35+ func : tp . Optional [ tp . Callable [..., tp . Union [ tp . Any , tp . Awaitable [ tp . Any ]]]] = None ,
36+ args : tuple [ tp . Any , ...] = (),
37+ kwargs : tp . Optional [ tp . Mapping [ str , tp . Any ]] = None ,
38+ start : bool = False ,
39+ uuid : tp . Optional [ UUID ] = None ,
40+ loop : tp . Optional [ asyncio . AbstractEventLoop ] = None ,
41+ tz : tp . Optional [ zoneinfo . ZoneInfo ] = None ,
42+ ) -> None :
4243 self .spec = spec
4344 if func is not None :
4445 kwargs = kwargs or {}
4546 self .func = func if not (args or kwargs ) else partial (func , * args , ** kwargs )
4647 else :
4748 self .func = null_callback
4849 self .tz = get_localzone () if tz is None else tz
49- self .cron = wrap_func (self .func )
50+ self .cron : tp . Callable [..., tp . Awaitable [ tp . Any ]] = wrap_func (self .func )
5051 self .auto_start = start
5152 self .uuid = uuid if uuid is not None else uuid4 ()
52- self .handle = self .future = self .cronsim = None
53+ self .handle = None
54+ self .future : tp .Optional [asyncio .Future ] = None
55+ self .cronsim : tp .Optional [CronSim ] = None
5356 self .loop = loop if loop is not None else asyncio .get_event_loop ()
5457 if start and self .func is not null_callback :
5558 self .handle = self .loop .call_soon_threadsafe (self .start )
5659
57- def start (self ):
60+ def start (self ) -> None :
5861 """Start scheduling"""
5962 self .stop ()
6063 self .initialize ()
6164 self .handle = self .loop .call_at (self .get_next (), self .call_next )
6265
63- def stop (self ):
66+ def stop (self ) -> None :
6467 """Stop scheduling"""
6568 if self .handle is not None :
6669 self .handle .cancel ()
6770 self .handle = self .future = self .cronsim = None
6871
69- async def next (self , * args ) :
72+ async def next (self , * args : tp . Any ) -> tp . Any :
7073 """yield from .next()"""
7174 self .initialize ()
7275 self .future = asyncio .Future (loop = self .loop )
7376 self .handle = self .loop .call_at (self .get_next (), self .call_func , * args )
7477 return await self .future
7578
76- def initialize (self ):
79+ def initialize (self ) -> None :
7780 """Initialize cronsim and related times"""
7881 if self .cronsim is None :
7982 self .time = time .time ()
8083 self .datetime = datetime .now (self .tz )
8184 self .loop_time = self .loop .time ()
8285 self .cronsim = CronSim (self .spec , self .datetime )
8386
84- def get_next (self ):
87+ def get_next (self ) -> float :
8588 """Return next iteration time related to loop time"""
8689 return self .loop_time + (next (self .cronsim ).timestamp () - self .time )
8790
88- def call_next (self ):
91+ def call_next (self ) -> None :
8992 """Set next hop in the loop. Call task"""
9093 if self .handle is not None :
9194 self .handle .cancel ()
9295 next_time = self .get_next ()
9396 self .handle = self .loop .call_at (next_time , self .call_next )
9497 self .call_func ()
9598
96- def call_func (self , * args , ** kwargs ) :
99+ def call_func (self , * args : tp . Any , ** kwargs : tp . Any ) -> None :
97100 """Called. Take care of exceptions using gather"""
98101 """Check the version of python installed"""
99102 if sys .version_info [0 :2 ] >= (3 , 10 ):
@@ -105,7 +108,7 @@ def call_func(self, *args, **kwargs):
105108 self .cron (* args , ** kwargs ), loop = self .loop , return_exceptions = True
106109 ).add_done_callback (self .set_result )
107110
108- def set_result (self , result ) :
111+ def set_result (self , result : asyncio . Future ) -> None :
109112 """Set future's result if needed (can be an exception).
110113 Else raise if needed."""
111114 result = result .result ()[0 ]
@@ -118,22 +121,30 @@ def set_result(self, result):
118121 elif isinstance (result , Exception ):
119122 raise result
120123
121- def __call__ (self , func ) :
124+ def __call__ (self , func : tp . Callable [..., tp . Awaitable [ tp . Any ]]) -> 'Cron' :
122125 """Used as a decorator"""
123126 self .func = func
124127 self .cron = wrap_func (func )
125128 if self .auto_start :
126129 self .loop .call_soon_threadsafe (self .start )
127130 return self
128131
129- def __str__ (self ):
132+ def __str__ (self ) -> str :
130133 return "{0.spec} {0.func}" .format (self )
131134
132- def __repr__ (self ):
135+ def __repr__ (self ) -> str :
133136 return "<Cron {0.spec} {0.func}>" .format (self )
134137
135138
136- def crontab (spec , func = None , args = (), kwargs = None , start = True , loop = None , tz = None ):
139+ def crontab (
140+ spec : str ,
141+ func : tp .Optional [tp .Callable [..., tp .Union [tp .Any , tp .Awaitable [tp .Any ]]]] = None ,
142+ args : tuple [tp .Any , ...] = (),
143+ kwargs : tp .Optional [tp .Mapping [str , tp .Any ]] = None ,
144+ start : bool = False ,
145+ loop : tp .Optional [asyncio .AbstractEventLoop ] = None ,
146+ tz : tp .Optional [zoneinfo .ZoneInfo ] = None ,
147+ ) -> Cron :
137148 return Cron (
138149 spec , func = func , args = args , kwargs = kwargs , start = start , loop = loop , tz = tz
139150 )
0 commit comments