1
1
# Builtin modules
2
2
from __future__ import annotations
3
- import os , traceback , unittest , signal as _signal
4
- from threading import Timer , Event
3
+ import traceback , signal as _signal
4
+ from threading import 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
7
9
# Local modules
8
10
# Program
11
+ Signals = _signal .Signals
12
+
9
13
class KillSignal (Exception ): pass
10
14
11
- class SignalIterator (Iterator ):
15
+ class SignalIterator (Iterator [ Any ] ):
12
16
__slots__ = ("event" , "it" , "checkDelay" , "lastCheck" )
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 :
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 ]:
19
27
return self
20
28
def __next__ (self ) -> Any :
21
- m : float = monotonic ()
29
+ m = monotonic ()
22
30
if m - self .lastCheck > self .checkDelay :
23
31
self .lastCheck = m
24
32
if self .event .is_set ():
@@ -28,38 +36,50 @@ def __next__(self) -> Any:
28
36
class BaseSignal :
29
37
_force :bool
30
38
@classmethod
31
- def check (self ) -> bool :
32
- if not isinstance (Signal ._handler , Signal ):
33
- return False
34
- return Signal . _handler . _check ( self . _force )
39
+ def get (self ) -> bool :
40
+ if isinstance (Signal ._handler , Signal ):
41
+ return Signal . _handler . _get ( self . _force )
42
+ return False
35
43
@classmethod
36
- def checkSoft (self ) -> bool :
37
- if not isinstance (Signal ._handler , Signal ):
38
- return False
39
- return Signal . _handler . _check ( False )
44
+ def getSoft (self ) -> bool :
45
+ if isinstance (Signal ._handler , Signal ):
46
+ return Signal . _handler . _get ( False )
47
+ return False
40
48
@classmethod
41
- def checkHard (self ) -> bool :
42
- if not isinstance (Signal ._handler , Signal ):
43
- return False
44
- return Signal ._handler ._check (True )
49
+ def getHard (self ) -> bool :
50
+ if isinstance (Signal ._handler , Signal ):
51
+ return Signal ._handler ._get (True )
52
+ return False
53
+ @classmethod
54
+ def check (self ) -> None :
55
+ if isinstance (Signal ._handler , Signal ):
56
+ return Signal ._handler ._check (self ._force )
57
+ @classmethod
58
+ def checkSoft (self ) -> None :
59
+ if isinstance (Signal ._handler , Signal ):
60
+ return Signal ._handler ._check (False )
61
+ @classmethod
62
+ def checkHard (self ) -> None :
63
+ if isinstance (Signal ._handler , Signal ):
64
+ return Signal ._handler ._check (True )
45
65
@classmethod
46
66
def sleep (self , seconds :Union [int , float ], raiseOnKill :bool = False ) -> None :
47
- if not isinstance (Signal ._handler , Signal ):
48
- return sleep (seconds )
49
- return Signal . _handler . _sleep (seconds , raiseOnKill , self . _force )
67
+ if isinstance (Signal ._handler , Signal ):
68
+ return Signal . _handler . _sleep (seconds , raiseOnKill , self . _force )
69
+ return sleep (seconds )
50
70
@classmethod
51
- def signalSoftKill (self , * args , ** kwargs ) -> None :
71
+ def signalSoftKill (self , * args : Any , ** kwargs : Any ) -> None :
52
72
if isinstance (Signal ._handler , Signal ):
53
73
return Signal ._handler ._signalSoftKill (* args , ** kwargs )
54
74
@classmethod
55
- def signalHardKill (self , * args , ** kwargs ) -> None :
75
+ def signalHardKill (self , * args : Any , ** kwargs : Any ) -> None :
56
76
if isinstance (Signal ._handler , Signal ):
57
77
return Signal ._handler ._signalHardKill (* args , ** kwargs )
58
78
@classmethod
59
- def iter (self , it :Iterable , checkDelay :float = 1.0 ) -> Iterable :
60
- if not isinstance (Signal ._handler , Signal ):
61
- return it
62
- return Signal . _handler . _iter ( it , checkDelay , self . _force )
79
+ def iter (self , it :Iterable [ Any ] , checkDelay :float = 1.0 ) -> Iterable [ Any ] :
80
+ if isinstance (Signal ._handler , Signal ):
81
+ return Signal . _handler . _iter ( it , checkDelay , self . _force )
82
+ return it
63
83
@classmethod
64
84
def softKill (self ) -> None :
65
85
if isinstance (Signal ._handler , Signal ):
@@ -89,16 +109,22 @@ class HardSignal(BaseSignal):
89
109
_force :bool = True
90
110
91
111
class Signal (HardSignal ):
112
+ _handler :Optional [Signal ] = None
113
+ softKillFn :Optional [Callable [[Signals , FrameType ], Any ]]
114
+ hardKillFn :Optional [Callable [[Signals , FrameType ], Any ]]
115
+ forceKillCounterFn :Optional [Callable [[int , int ], Any ]]
116
+ counter :int
117
+ forceCounter :int
92
118
eSoft :Event
93
119
eHard :Event
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
120
+ def __init__ ( self , softKillFn :Optional [Callable [[ Signals , FrameType ], Any ]] = None ,
121
+ hardKillFn :Optional [Callable [[ Signals , FrameType ], Any ] ]= None ,
122
+ forceKillCounterFn :Optional [Callable [[ int , int ], Any ] ]= None , forceCounter :int = 10 ):
123
+ self .softKillFn = softKillFn
124
+ self .hardKillFn = hardKillFn
125
+ self .forceKillCounterFn = forceKillCounterFn
126
+ self .counter = 0
127
+ self .forceCounter = forceCounter
102
128
self .eSoft = Event ()
103
129
self .eHard = Event ()
104
130
Signal ._handler = self
@@ -123,16 +149,21 @@ def __setstate__(self, states:Dict[str, Any]) -> None:
123
149
def _activate (self ) -> None :
124
150
_signal .signal (_signal .SIGINT , Signal .signalSoftKill )
125
151
_signal .signal (_signal .SIGTERM , Signal .signalHardKill )
126
- def _check (self , force :bool = True ) -> bool :
152
+ def _get (self , force :bool = True ) -> bool :
127
153
if force :
128
154
return self .eHard .is_set ()
129
155
return self .eSoft .is_set ()
156
+ def _check (self , force :bool = True ) -> None :
157
+ if (force and self .eHard .is_set ()) or (not force and self .eSoft .is_set ()):
158
+ raise KillSignal
159
+ return None
130
160
def _sleep (self , seconds :Union [int , float ], raiseOnKill :bool = False , force :bool = True ) -> None :
131
161
if (self .eHard if force else self .eSoft ).wait (float (seconds )) and raiseOnKill :
132
162
raise KillSignal
133
- def _iter (self , it :Iterable , checkDelay :float = 1.0 , force :bool = True ) -> Iterator :
163
+ return None
164
+ def _iter (self , it :Iterable [Any ], checkDelay :float = 1.0 , force :bool = True ) -> Iterator [Any ]:
134
165
return SignalIterator (self .eHard if force else self .eSoft , it , checkDelay )
135
- def _signalSoftKill (self , * args , ** kwargs ) -> None :
166
+ def _signalSoftKill (self , * args : Any , ** kwargs : Any ) -> None :
136
167
self ._softKill ()
137
168
if not self .eHard .is_set ():
138
169
self .counter += 1
@@ -143,67 +174,26 @@ def _signalSoftKill(self, *args, **kwargs) -> None:
143
174
traceback .print_exc ()
144
175
if self .counter >= self .forceCounter :
145
176
self ._hardKill ()
146
- def _signalHardKill (self , * args , ** kwargs ) -> None :
177
+ def _signalHardKill (self , * args : Any , ** kwargs : Any ) -> None :
147
178
self ._softKill ()
148
179
self ._hardKill ()
149
180
def _softKill (self ) -> None :
150
181
if not self .eSoft .is_set ():
151
182
self .eSoft .set ()
152
183
if callable (self .softKillFn ):
153
184
try :
154
- self .softKillFn ()
185
+ self .softKillFn () # type: ignore
155
186
except :
156
187
traceback .print_exc ()
157
188
def _hardKill (self ) -> None :
158
189
if not self .eHard .is_set ():
159
190
self .eHard .set ()
160
191
if callable (self .hardKillFn ):
161
192
try :
162
- self .hardKillFn ()
193
+ self .hardKillFn () # type: ignore
163
194
except :
164
195
traceback .print_exc ()
165
196
def _reset (self ) -> None :
166
197
self .eSoft .clear ()
167
198
self .eHard .clear ()
168
199
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