1
1
# Builtin modules
2
2
from __future__ import annotations
3
- import traceback , signal as _signal
4
- from threading import Event
3
+ import os , traceback , unittest , signal as _signal
4
+ from threading import Timer , Event
5
5
from time import monotonic , sleep
6
6
from typing import Callable , Dict , Any , Iterator , Iterable , Optional , Union
7
- from types import FrameType
8
- # Third party modules
9
7
# Local modules
10
8
# Program
11
- Signals = _signal .Signals
12
-
13
9
class KillSignal (Exception ): pass
14
10
15
- class SignalIterator (Iterator [ Any ] ):
11
+ class SignalIterator (Iterator ):
16
12
__slots__ = ("event" , "it" , "checkDelay" , "lastCheck" )
17
- event :Event
18
- it :Iterator [Any ]
19
- checkDelay :float
20
- lastCheck :float
21
- def __init__ (self , event :Event , it :Iterable [Any ], checkDelay :float = 1.0 ):
22
- self .event = event
23
- self .it = it .__iter__ ()
24
- self .checkDelay = checkDelay
25
- self .lastCheck = monotonic ()
26
- def __iter__ (self ) -> Iterator [Any ]:
13
+ def __init__ (self , event :Event , it :Iterable , checkDelay :float = 1.0 ):
14
+ self .event :Event = event
15
+ self .it :Iterator = it .__iter__ ()
16
+ self .checkDelay :float = checkDelay
17
+ self .lastCheck :float = monotonic ()
18
+ def __iter__ (self ) -> Iterator :
27
19
return self
28
20
def __next__ (self ) -> Any :
29
- m = monotonic ()
21
+ m : float = monotonic ()
30
22
if m - self .lastCheck > self .checkDelay :
31
23
self .lastCheck = m
32
24
if self .event .is_set ():
@@ -36,16 +28,11 @@ def __next__(self) -> Any:
36
28
class BaseSignal :
37
29
_force :bool
38
30
@classmethod
39
- def get (self ) -> bool :
31
+ def check (self ) -> bool :
40
32
if not isinstance (Signal ._handler , Signal ):
41
33
return False
42
34
return Signal ._handler ._check (self ._force )
43
35
@classmethod
44
- def check (self ) -> None :
45
- if not isinstance (Signal ._handler , Signal ):
46
- return
47
- return Signal ._handler .get (self ._force )
48
- @classmethod
49
36
def checkSoft (self ) -> bool :
50
37
if not isinstance (Signal ._handler , Signal ):
51
38
return False
@@ -61,15 +48,15 @@ def sleep(self, seconds:Union[int, float], raiseOnKill:bool=False) -> None:
61
48
return sleep (seconds )
62
49
return Signal ._handler ._sleep (seconds , raiseOnKill , self ._force )
63
50
@classmethod
64
- def signalSoftKill (self , * args : Any , ** kwargs : Any ) -> None :
51
+ def signalSoftKill (self , * args , ** kwargs ) -> None :
65
52
if isinstance (Signal ._handler , Signal ):
66
53
return Signal ._handler ._signalSoftKill (* args , ** kwargs )
67
54
@classmethod
68
- def signalHardKill (self , * args : Any , ** kwargs : Any ) -> None :
55
+ def signalHardKill (self , * args , ** kwargs ) -> None :
69
56
if isinstance (Signal ._handler , Signal ):
70
57
return Signal ._handler ._signalHardKill (* args , ** kwargs )
71
58
@classmethod
72
- def iter (self , it :Iterable [ Any ] , checkDelay :float = 1.0 ) -> Iterable [ Any ] :
59
+ def iter (self , it :Iterable , checkDelay :float = 1.0 ) -> Iterable :
73
60
if not isinstance (Signal ._handler , Signal ):
74
61
return it
75
62
return Signal ._handler ._iter (it , checkDelay , self ._force )
@@ -102,22 +89,16 @@ class HardSignal(BaseSignal):
102
89
_force :bool = True
103
90
104
91
class Signal (HardSignal ):
105
- _handler :Optional [Signal ] = None
106
- softKillFn :Optional [Callable [[Signals , FrameType ], Any ]]
107
- hardKillFn :Optional [Callable [[Signals , FrameType ], Any ]]
108
- forceKillCounterFn :Optional [Callable [[int , int ], Any ]]
109
- counter :int
110
- forceCounter :int
111
92
eSoft :Event
112
93
eHard :Event
113
- def __init__ ( self , softKillFn :Optional [Callable [[ Signals , FrameType ], Any ]] = None ,
114
- hardKillFn :Optional [Callable [[ Signals , FrameType ], Any ] ]= None ,
115
- forceKillCounterFn :Optional [Callable [[ int , int ], Any ] ]= None , forceCounter :int = 10 ):
116
- self .softKillFn = softKillFn
117
- self .hardKillFn = hardKillFn
118
- self .forceKillCounterFn = forceKillCounterFn
119
- self .counter = 0
120
- self .forceCounter = forceCounter
94
+ _handler :Optional [Signal ] = None
95
+ def __init__ ( self , softKillFn :Optional [Callable ] = None , hardKillFn : Optional [ Callable ]= None ,
96
+ forceKillCounterFn :Optional [Callable ]= None , forceCounter :int = 10 ):
97
+ self .softKillFn : Optional [ Callable ] = softKillFn
98
+ self .hardKillFn : Optional [ Callable ] = hardKillFn
99
+ self .forceKillCounterFn : Optional [ Callable ] = forceKillCounterFn
100
+ self .counter : int = 0
101
+ self .forceCounter : int = forceCounter
121
102
self .eSoft = Event ()
122
103
self .eHard = Event ()
123
104
Signal ._handler = self
@@ -142,21 +123,16 @@ def __setstate__(self, states:Dict[str, Any]) -> None:
142
123
def _activate (self ) -> None :
143
124
_signal .signal (_signal .SIGINT , Signal .signalSoftKill )
144
125
_signal .signal (_signal .SIGTERM , Signal .signalHardKill )
145
- def _get (self , force :bool = True ) -> bool :
126
+ def _check (self , force :bool = True ) -> bool :
146
127
if force :
147
128
return self .eHard .is_set ()
148
129
return self .eSoft .is_set ()
149
- def _check (self , force :bool = True ) -> None :
150
- if (force and self .eHard .is_set ()) or (not force and self .eSoft .is_set ()):
151
- raise KillSignal
152
- return None
153
130
def _sleep (self , seconds :Union [int , float ], raiseOnKill :bool = False , force :bool = True ) -> None :
154
131
if (self .eHard if force else self .eSoft ).wait (float (seconds )) and raiseOnKill :
155
132
raise KillSignal
156
- return None
157
- def _iter (self , it :Iterable [Any ], checkDelay :float = 1.0 , force :bool = True ) -> Iterator [Any ]:
133
+ def _iter (self , it :Iterable , checkDelay :float = 1.0 , force :bool = True ) -> Iterator :
158
134
return SignalIterator (self .eHard if force else self .eSoft , it , checkDelay )
159
- def _signalSoftKill (self , * args : Any , ** kwargs : Any ) -> None :
135
+ def _signalSoftKill (self , * args , ** kwargs ) -> None :
160
136
self ._softKill ()
161
137
if not self .eHard .is_set ():
162
138
self .counter += 1
@@ -167,26 +143,67 @@ def _signalSoftKill(self, *args:Any, **kwargs:Any) -> None:
167
143
traceback .print_exc ()
168
144
if self .counter >= self .forceCounter :
169
145
self ._hardKill ()
170
- def _signalHardKill (self , * args : Any , ** kwargs : Any ) -> None :
146
+ def _signalHardKill (self , * args , ** kwargs ) -> None :
171
147
self ._softKill ()
172
148
self ._hardKill ()
173
149
def _softKill (self ) -> None :
174
150
if not self .eSoft .is_set ():
175
151
self .eSoft .set ()
176
152
if callable (self .softKillFn ):
177
153
try :
178
- self .softKillFn () # type: ignore
154
+ self .softKillFn ()
179
155
except :
180
156
traceback .print_exc ()
181
157
def _hardKill (self ) -> None :
182
158
if not self .eHard .is_set ():
183
159
self .eHard .set ()
184
160
if callable (self .hardKillFn ):
185
161
try :
186
- self .hardKillFn () # type: ignore
162
+ self .hardKillFn ()
187
163
except :
188
164
traceback .print_exc ()
189
165
def _reset (self ) -> None :
190
166
self .eSoft .clear ()
191
167
self .eHard .clear ()
192
168
self .counter = 0
169
+
170
+ class SignalTest (unittest .TestCase ):
171
+ rootSignal :Signal
172
+ @classmethod
173
+ def setUpClass (self ) -> None :
174
+ self .rootSignal = Signal ()
175
+ def tearDown (self ) -> None :
176
+ self .rootSignal .reset ()
177
+ def killmeTimer (self ) -> None :
178
+ def suicide ():
179
+ os .kill (os .getpid (), _signal .SIGINT )
180
+ Timer (1 , suicide ).start ()
181
+ def test_sleep (self ) -> None :
182
+ t :float = monotonic ()
183
+ self .rootSignal .sleep (2 )
184
+ self .assertGreater (monotonic ()- t , 2.0 )
185
+ def test_sleepRaise (self ) -> None :
186
+ self .killmeTimer ()
187
+ with self .assertRaises (KillSignal ):
188
+ self .rootSignal .getSoftSignal ().sleep (2 , raiseOnKill = True )
189
+ def test_iter (self ) -> None :
190
+ s :list = list (range (5 ))
191
+ d :list = []
192
+ i :int
193
+ signal :SoftSignal = self .rootSignal .getSoftSignal ()
194
+ self .killmeTimer ()
195
+ with self .assertRaises (KillSignal ):
196
+ for i in s :
197
+ signal .sleep (0.5 , raiseOnKill = True )
198
+ d .append (i )
199
+ def test_hardkill (self ) -> None :
200
+ self .killmeTimer ()
201
+ sleep (0.1 )
202
+ self .killmeTimer ()
203
+ sleep (0.1 )
204
+ self .killmeTimer ()
205
+ sleep (0.1 )
206
+ self .rootSignal .forceCounter = 3
207
+ with self .assertRaises (KillSignal ):
208
+ self .rootSignal .sleep (10 , raiseOnKill = True )
209
+ self .rootSignal .forceCounter = 10
0 commit comments