3
3
from datetime import datetime
4
4
from functools import wraps , partial
5
5
from tzlocal import get_localzone
6
- from uuid import uuid4
6
+ from uuid import uuid4 , UUID
7
7
import time
8
8
import asyncio
9
9
import sys
10
10
import inspect
11
+ import typing as tp
12
+ import zoneinfo
11
13
12
14
13
- async def null_callback (* args ) :
15
+ async def null_callback (* args : tp . Any ) -> tp . Tuple [ tp . Any , ...] :
14
16
return args
15
17
16
18
17
- def wrap_func (func ) :
19
+ def wrap_func (func : tp . Callable [..., tp . Union [ tp . Any , tp . Awaitable [ tp . Any ]]]) -> tp . Callable [..., tp . Awaitable [ tp . Any ]] :
18
20
"""wrap in a coroutine"""
19
21
20
22
@wraps (func )
21
- async def wrapper (* args , ** kwargs ) :
23
+ async def wrapper (* args : tp . Any , ** kwargs : tp . Any ) -> tp . Any :
22
24
result = func (* args , ** kwargs )
23
25
if inspect .isawaitable (result ):
24
26
result = await result
@@ -30,70 +32,72 @@ async def wrapper(*args, **kwargs):
30
32
class Cron (object ):
31
33
def __init__ (
32
34
self ,
33
- spec ,
34
- func = None ,
35
- args = (),
36
- kwargs = None ,
37
- start = False ,
38
- uuid = None ,
39
- loop = None ,
40
- tz = None ,
41
- ):
35
+ spec : str ,
36
+ func : tp . Optional [ tp . Callable [..., tp . Union [ tp . Any , tp . Awaitable [ tp . Any ]]]] = None ,
37
+ args : tp . Tuple [ tp . Any , ...] = (),
38
+ kwargs : tp . Optional [ tp . Mapping [ str , tp . Any ]] = None ,
39
+ start : bool = False ,
40
+ uuid : tp . Optional [ UUID ] = None ,
41
+ loop : tp . Optional [ asyncio . AbstractEventLoop ] = None ,
42
+ tz : tp . Optional [ zoneinfo . ZoneInfo ] = None ,
43
+ ) -> None :
42
44
self .spec = spec
43
45
if func is not None :
44
46
kwargs = kwargs or {}
45
47
self .func = func if not (args or kwargs ) else partial (func , * args , ** kwargs )
46
48
else :
47
49
self .func = null_callback
48
50
self .tz = get_localzone () if tz is None else tz
49
- self .cron = wrap_func (self .func )
51
+ self .cron : tp . Callable [..., tp . Awaitable [ tp . Any ]] = wrap_func (self .func )
50
52
self .auto_start = start
51
53
self .uuid = uuid if uuid is not None else uuid4 ()
52
- self .handle = self .future = self .cronsim = None
54
+ self .handle = None
55
+ self .future : tp .Optional [asyncio .Future ] = None
56
+ self .cronsim : tp .Optional [CronSim ] = None
53
57
self .loop = loop if loop is not None else asyncio .get_event_loop ()
54
58
if start and self .func is not null_callback :
55
59
self .handle = self .loop .call_soon_threadsafe (self .start )
56
60
57
- def start (self ):
61
+ def start (self ) -> None :
58
62
"""Start scheduling"""
59
63
self .stop ()
60
64
self .initialize ()
61
65
self .handle = self .loop .call_at (self .get_next (), self .call_next )
62
66
63
- def stop (self ):
67
+ def stop (self ) -> None :
64
68
"""Stop scheduling"""
65
69
if self .handle is not None :
66
70
self .handle .cancel ()
67
71
self .handle = self .future = self .cronsim = None
68
72
69
- async def next (self , * args ) :
73
+ async def next (self , * args : tp . Any ) -> tp . Any :
70
74
"""yield from .next()"""
71
75
self .initialize ()
72
76
self .future = asyncio .Future (loop = self .loop )
73
77
self .handle = self .loop .call_at (self .get_next (), self .call_func , * args )
74
78
return await self .future
75
79
76
- def initialize (self ):
80
+ def initialize (self ) -> None :
77
81
"""Initialize cronsim and related times"""
78
82
if self .cronsim is None :
79
83
self .time = time .time ()
80
84
self .datetime = datetime .now (self .tz )
81
85
self .loop_time = self .loop .time ()
82
86
self .cronsim = CronSim (self .spec , self .datetime )
83
87
84
- def get_next (self ):
88
+ def get_next (self ) -> float :
85
89
"""Return next iteration time related to loop time"""
86
90
return self .loop_time + (next (self .cronsim ).timestamp () - self .time )
87
91
88
- def call_next (self ):
92
+ def call_next (self ) -> None :
89
93
"""Set next hop in the loop. Call task"""
90
94
if self .handle is not None :
91
95
self .handle .cancel ()
92
96
next_time = self .get_next ()
93
97
self .handle = self .loop .call_at (next_time , self .call_next )
94
98
self .call_func ()
95
99
96
- def call_func (self , * args , ** kwargs ) :
100
+ def call_func (self , * args : tp . Any , ** kwargs : tp . Any ) -> None :
97
101
"""Called. Take care of exceptions using gather"""
98
102
"""Check the version of python installed"""
99
103
if sys .version_info [0 :2 ] >= (3 , 10 ):
@@ -105,7 +109,7 @@ def call_func(self, *args, **kwargs):
105
109
self .cron (* args , ** kwargs ), loop = self .loop , return_exceptions = True
106
110
).add_done_callback (self .set_result )
107
111
108
- def set_result (self , result ) :
112
+ def set_result (self , result : asyncio . Future ) -> None :
109
113
"""Set future's result if needed (can be an exception).
110
114
Else raise if needed."""
111
115
result = result .result ()[0 ]
@@ -118,22 +122,30 @@ def set_result(self, result):
118
122
elif isinstance (result , Exception ):
119
123
raise result
120
124
121
- def __call__ (self , func ) :
125
+ def __call__ (self , func : tp . Callable [..., tp . Awaitable [ tp . Any ]]) -> 'Cron' :
122
126
"""Used as a decorator"""
123
127
self .func = func
124
128
self .cron = wrap_func (func )
125
129
if self .auto_start :
126
130
self .loop .call_soon_threadsafe (self .start )
127
131
return self
128
132
129
- def __str__ (self ):
133
+ def __str__ (self ) -> str :
130
134
return "{0.spec} {0.func}" .format (self )
131
135
132
- def __repr__ (self ):
136
+ def __repr__ (self ) -> str :
133
137
return "<Cron {0.spec} {0.func}>" .format (self )
134
138
135
139
136
- def crontab (spec , func = None , args = (), kwargs = None , start = True , loop = None , tz = None ):
140
+ def crontab (
141
+ spec : str ,
142
+ func : tp .Optional [tp .Callable [..., tp .Union [tp .Any , tp .Awaitable [tp .Any ]]]] = None ,
143
+ args : tp .Tuple [tp .Any , ...] = (),
144
+ kwargs : tp .Optional [tp .Mapping [str , tp .Any ]] = None ,
145
+ start : bool = False ,
146
+ loop : tp .Optional [asyncio .AbstractEventLoop ] = None ,
147
+ tz : tp .Optional [zoneinfo .ZoneInfo ] = None ,
148
+ ) -> Cron :
137
149
return Cron (
138
150
spec , func = func , args = args , kwargs = kwargs , start = start , loop = loop , tz = tz
139
151
)
0 commit comments