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