ominfra 0.0.0.dev122__py3-none-any.whl → 0.0.0.dev123__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/deploy/_executor.py +1 -1
- ominfra/journald/messages.py +1 -1
- ominfra/pyremote/_runcommands.py +1 -1
- ominfra/scripts/journald2aws.py +2 -2
- ominfra/scripts/supervisor.py +2216 -2044
- ominfra/supervisor/context.py +16 -41
- ominfra/supervisor/dispatchers.py +88 -58
- ominfra/supervisor/events.py +59 -70
- ominfra/supervisor/groups.py +162 -0
- ominfra/supervisor/main.py +27 -11
- ominfra/supervisor/poller.py +51 -53
- ominfra/supervisor/process.py +267 -352
- ominfra/supervisor/signals.py +52 -0
- ominfra/supervisor/states.py +26 -47
- ominfra/supervisor/supervisor.py +105 -81
- ominfra/supervisor/types.py +57 -6
- ominfra/supervisor/{compat.py → utils.py} +34 -53
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/RECORD +23 -21
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev122.dist-info → ominfra-0.0.0.dev123.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import signal
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
_SIG_NAMES: ta.Optional[ta.Mapping[int, str]] = None
|
10
|
+
|
11
|
+
|
12
|
+
def sig_name(sig: int) -> str:
|
13
|
+
global _SIG_NAMES
|
14
|
+
if _SIG_NAMES is None:
|
15
|
+
_SIG_NAMES = _init_sig_names()
|
16
|
+
return _SIG_NAMES.get(sig) or 'signal %d' % sig
|
17
|
+
|
18
|
+
|
19
|
+
def _init_sig_names() -> ta.Dict[int, str]:
|
20
|
+
d = {}
|
21
|
+
for k, v in signal.__dict__.items(): # noqa
|
22
|
+
k_startswith = getattr(k, 'startswith', None)
|
23
|
+
if k_startswith is None:
|
24
|
+
continue
|
25
|
+
if k_startswith('SIG') and not k_startswith('SIG_'):
|
26
|
+
d[v] = k
|
27
|
+
return d
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
|
32
|
+
|
33
|
+
class SignalReceiver:
|
34
|
+
def __init__(self) -> None:
|
35
|
+
super().__init__()
|
36
|
+
|
37
|
+
self._signals_recvd: ta.List[int] = []
|
38
|
+
|
39
|
+
def receive(self, sig: int, frame: ta.Any) -> None:
|
40
|
+
if sig not in self._signals_recvd:
|
41
|
+
self._signals_recvd.append(sig)
|
42
|
+
|
43
|
+
def install(self, *sigs: int) -> None:
|
44
|
+
for sig in sigs:
|
45
|
+
signal.signal(sig, self.receive)
|
46
|
+
|
47
|
+
def get_signal(self) -> ta.Optional[int]:
|
48
|
+
if self._signals_recvd:
|
49
|
+
sig = self._signals_recvd.pop(0)
|
50
|
+
else:
|
51
|
+
sig = None
|
52
|
+
return sig
|
ominfra/supervisor/states.py
CHANGED
@@ -1,29 +1,10 @@
|
|
1
|
-
|
2
|
-
import typing as ta
|
3
|
-
|
4
|
-
from omlish.lite.check import check_not_none
|
5
|
-
|
6
|
-
|
7
|
-
ProcessState = int # ta.TypeAlias
|
8
|
-
SupervisorState = int # ta.TypeAlias
|
9
|
-
|
10
|
-
|
11
|
-
##
|
12
|
-
|
13
|
-
|
14
|
-
def _names_by_code(states: ta.Any) -> ta.Dict[int, str]:
|
15
|
-
d = {}
|
16
|
-
for name in states.__dict__:
|
17
|
-
if not name.startswith('__'):
|
18
|
-
code = getattr(states, name)
|
19
|
-
d[code] = name
|
20
|
-
return d
|
1
|
+
import enum
|
21
2
|
|
22
3
|
|
23
4
|
##
|
24
5
|
|
25
6
|
|
26
|
-
class
|
7
|
+
class ProcessState(enum.IntEnum):
|
27
8
|
STOPPED = 0
|
28
9
|
STARTING = 10
|
29
10
|
RUNNING = 20
|
@@ -33,46 +14,44 @@ class ProcessStates:
|
|
33
14
|
FATAL = 200
|
34
15
|
UNKNOWN = 1000
|
35
16
|
|
17
|
+
@property
|
18
|
+
def stopped(self) -> bool:
|
19
|
+
return self in STOPPED_STATES
|
20
|
+
|
21
|
+
@property
|
22
|
+
def running(self) -> bool:
|
23
|
+
return self in RUNNING_STATES
|
24
|
+
|
25
|
+
@property
|
26
|
+
def signalable(self) -> bool:
|
27
|
+
return self in SIGNALABLE_STATES
|
28
|
+
|
36
29
|
|
37
30
|
STOPPED_STATES = (
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
ProcessState.STOPPED,
|
32
|
+
ProcessState.EXITED,
|
33
|
+
ProcessState.FATAL,
|
34
|
+
ProcessState.UNKNOWN,
|
42
35
|
)
|
43
36
|
|
44
37
|
RUNNING_STATES = (
|
45
|
-
|
46
|
-
|
47
|
-
|
38
|
+
ProcessState.RUNNING,
|
39
|
+
ProcessState.BACKOFF,
|
40
|
+
ProcessState.STARTING,
|
48
41
|
)
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
SIGNALABLE_STATES = (
|
44
|
+
ProcessState.RUNNING,
|
45
|
+
ProcessState.STARTING,
|
46
|
+
ProcessState.STOPPING,
|
54
47
|
)
|
55
48
|
|
56
49
|
|
57
|
-
_process_states_by_code = _names_by_code(ProcessStates)
|
58
|
-
|
59
|
-
|
60
|
-
def get_process_state_description(code: ProcessState) -> str:
|
61
|
-
return check_not_none(_process_states_by_code.get(code))
|
62
|
-
|
63
|
-
|
64
50
|
##
|
65
51
|
|
66
52
|
|
67
|
-
class
|
53
|
+
class SupervisorState(enum.IntEnum):
|
68
54
|
FATAL = 2
|
69
55
|
RUNNING = 1
|
70
56
|
RESTARTING = 0
|
71
57
|
SHUTDOWN = -1
|
72
|
-
|
73
|
-
|
74
|
-
_supervisor_states_by_code = _names_by_code(SupervisorStates)
|
75
|
-
|
76
|
-
|
77
|
-
def get_supervisor_state_description(code: SupervisorState) -> str:
|
78
|
-
return check_not_none(_supervisor_states_by_code.get(code))
|
ominfra/supervisor/supervisor.py
CHANGED
@@ -8,28 +8,83 @@ from omlish.lite.cached import cached_nullary
|
|
8
8
|
from omlish.lite.check import check_not_none
|
9
9
|
from omlish.lite.logs import log
|
10
10
|
|
11
|
-
from .compat import ExitNow
|
12
|
-
from .compat import as_string
|
13
|
-
from .compat import decode_wait_status
|
14
|
-
from .compat import signame
|
15
11
|
from .configs import ProcessGroupConfig
|
16
12
|
from .context import ServerContext
|
17
13
|
from .dispatchers import Dispatcher
|
18
|
-
from .events import EVENT_CALLBACKS
|
19
14
|
from .events import TICK_EVENTS
|
20
|
-
from .events import
|
21
|
-
from .events import ProcessGroupRemovedEvent
|
15
|
+
from .events import EventCallbacks
|
22
16
|
from .events import SupervisorRunningEvent
|
23
17
|
from .events import SupervisorStoppingEvent
|
24
|
-
from .
|
18
|
+
from .groups import ProcessGroup
|
19
|
+
from .groups import ProcessGroups
|
20
|
+
from .poller import Poller
|
25
21
|
from .process import Subprocess
|
22
|
+
from .signals import SignalReceiver
|
23
|
+
from .signals import sig_name
|
26
24
|
from .states import SupervisorState
|
27
|
-
from .
|
28
|
-
from .
|
25
|
+
from .utils import ExitNow
|
26
|
+
from .utils import as_string
|
27
|
+
from .utils import decode_wait_status
|
28
|
+
from .utils import timeslice
|
29
29
|
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
##
|
32
|
+
|
33
|
+
|
34
|
+
class SignalHandler:
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
*,
|
38
|
+
context: ServerContext,
|
39
|
+
signal_receiver: SignalReceiver,
|
40
|
+
process_groups: ProcessGroups,
|
41
|
+
) -> None:
|
42
|
+
super().__init__()
|
43
|
+
|
44
|
+
self._context = context
|
45
|
+
self._signal_receiver = signal_receiver
|
46
|
+
self._process_groups = process_groups
|
47
|
+
|
48
|
+
def set_signals(self) -> None:
|
49
|
+
self._signal_receiver.install(
|
50
|
+
signal.SIGTERM,
|
51
|
+
signal.SIGINT,
|
52
|
+
signal.SIGQUIT,
|
53
|
+
signal.SIGHUP,
|
54
|
+
signal.SIGCHLD,
|
55
|
+
signal.SIGUSR2,
|
56
|
+
)
|
57
|
+
|
58
|
+
def handle_signals(self) -> None:
|
59
|
+
sig = self._signal_receiver.get_signal()
|
60
|
+
if not sig:
|
61
|
+
return
|
62
|
+
|
63
|
+
if sig in (signal.SIGTERM, signal.SIGINT, signal.SIGQUIT):
|
64
|
+
log.warning('received %s indicating exit request', sig_name(sig))
|
65
|
+
self._context.set_state(SupervisorState.SHUTDOWN)
|
66
|
+
|
67
|
+
elif sig == signal.SIGHUP:
|
68
|
+
if self._context.state == SupervisorState.SHUTDOWN:
|
69
|
+
log.warning('ignored %s indicating restart request (shutdown in progress)', sig_name(sig)) # noqa
|
70
|
+
else:
|
71
|
+
log.warning('received %s indicating restart request', sig_name(sig)) # noqa
|
72
|
+
self._context.set_state(SupervisorState.RESTARTING)
|
73
|
+
|
74
|
+
elif sig == signal.SIGCHLD:
|
75
|
+
log.debug('received %s indicating a child quit', sig_name(sig))
|
76
|
+
|
77
|
+
elif sig == signal.SIGUSR2:
|
78
|
+
log.info('received %s indicating log reopen request', sig_name(sig))
|
79
|
+
|
80
|
+
for group in self._process_groups:
|
81
|
+
group.reopen_logs()
|
82
|
+
|
83
|
+
else:
|
84
|
+
log.debug('received %s indicating nothing', sig_name(sig))
|
85
|
+
|
86
|
+
|
87
|
+
##
|
33
88
|
|
34
89
|
|
35
90
|
@dc.dataclass(frozen=True)
|
@@ -41,25 +96,26 @@ class ProcessGroupFactory:
|
|
41
96
|
|
42
97
|
|
43
98
|
class Supervisor:
|
44
|
-
|
45
99
|
def __init__(
|
46
100
|
self,
|
47
|
-
context: ServerContext,
|
48
101
|
*,
|
49
|
-
|
102
|
+
context: ServerContext,
|
103
|
+
poller: Poller,
|
104
|
+
process_groups: ProcessGroups,
|
105
|
+
signal_handler: SignalHandler,
|
106
|
+
event_callbacks: EventCallbacks,
|
107
|
+
process_group_factory: ProcessGroupFactory,
|
50
108
|
) -> None:
|
51
109
|
super().__init__()
|
52
110
|
|
53
111
|
self._context = context
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
process_group_factory = ProcessGroupFactory(make_process_group)
|
112
|
+
self._poller = poller
|
113
|
+
self._process_groups = process_groups
|
114
|
+
self._signal_handler = signal_handler
|
115
|
+
self._event_callbacks = event_callbacks
|
59
116
|
self._process_group_factory = process_group_factory
|
60
117
|
|
61
118
|
self._ticks: ta.Dict[int, float] = {}
|
62
|
-
self._process_groups: ta.Dict[str, ProcessGroup] = {} # map of process group name to process group object
|
63
119
|
self._stop_groups: ta.Optional[ta.List[ProcessGroup]] = None # list used for priority ordered shutdown
|
64
120
|
self._stopping = False # set after we detect that we are handling a stop request
|
65
121
|
self._last_shutdown_report = 0. # throttle for delayed process error reports at stop
|
@@ -82,7 +138,7 @@ class Supervisor:
|
|
82
138
|
|
83
139
|
def diff_to_active(self) -> DiffToActive:
|
84
140
|
new = self._context.config.groups or []
|
85
|
-
cur = [group.config for group in self._process_groups
|
141
|
+
cur = [group.config for group in self._process_groups]
|
86
142
|
|
87
143
|
curdict = dict(zip([cfg.name for cfg in cur], cur))
|
88
144
|
newdict = dict(zip([cfg.name for cfg in new], new))
|
@@ -95,38 +151,35 @@ class Supervisor:
|
|
95
151
|
return Supervisor.DiffToActive(added, changed, removed)
|
96
152
|
|
97
153
|
def add_process_group(self, config: ProcessGroupConfig) -> bool:
|
98
|
-
|
99
|
-
if name in self._process_groups:
|
154
|
+
if self._process_groups.get(config.name) is not None:
|
100
155
|
return False
|
101
156
|
|
102
|
-
group = self.
|
157
|
+
group = self._process_group_factory(config)
|
103
158
|
group.after_setuid()
|
104
159
|
|
105
|
-
|
160
|
+
self._process_groups.add(group)
|
161
|
+
|
106
162
|
return True
|
107
163
|
|
108
164
|
def remove_process_group(self, name: str) -> bool:
|
109
165
|
if self._process_groups[name].get_unstopped_processes():
|
110
166
|
return False
|
111
167
|
|
112
|
-
self._process_groups
|
113
|
-
|
114
|
-
del self._process_groups[name]
|
168
|
+
self._process_groups.remove(name)
|
115
169
|
|
116
|
-
EVENT_CALLBACKS.notify(ProcessGroupRemovedEvent(name))
|
117
170
|
return True
|
118
171
|
|
119
172
|
def get_process_map(self) -> ta.Dict[int, Dispatcher]:
|
120
173
|
process_map = {}
|
121
|
-
for group in self._process_groups
|
174
|
+
for group in self._process_groups:
|
122
175
|
process_map.update(group.get_dispatchers())
|
123
176
|
return process_map
|
124
177
|
|
125
178
|
def shutdown_report(self) -> ta.List[Subprocess]:
|
126
179
|
unstopped: ta.List[Subprocess] = []
|
127
180
|
|
128
|
-
for group in self._process_groups
|
129
|
-
unstopped.extend(group.get_unstopped_processes())
|
181
|
+
for group in self._process_groups:
|
182
|
+
unstopped.extend(group.get_unstopped_processes()) # type: ignore
|
130
183
|
|
131
184
|
if unstopped:
|
132
185
|
# throttle 'waiting for x to die' reports
|
@@ -137,8 +190,7 @@ class Supervisor:
|
|
137
190
|
log.info('waiting for %s to die', namestr)
|
138
191
|
self._last_shutdown_report = now
|
139
192
|
for proc in unstopped:
|
140
|
-
state
|
141
|
-
log.debug('%s state: %s', proc.config.name, state)
|
193
|
+
log.debug('%s state: %s', proc.config.name, proc.get_state().name)
|
142
194
|
|
143
195
|
return unstopped
|
144
196
|
|
@@ -169,16 +221,16 @@ class Supervisor:
|
|
169
221
|
*,
|
170
222
|
callback: ta.Optional[ta.Callable[['Supervisor'], bool]] = None,
|
171
223
|
) -> None:
|
172
|
-
self._process_groups
|
224
|
+
self._process_groups.clear()
|
173
225
|
self._stop_groups = None # clear
|
174
226
|
|
175
|
-
|
227
|
+
self._event_callbacks.clear()
|
176
228
|
|
177
229
|
try:
|
178
230
|
for config in self._context.config.groups or []:
|
179
231
|
self.add_process_group(config)
|
180
232
|
|
181
|
-
self.
|
233
|
+
self._signal_handler.set_signals()
|
182
234
|
|
183
235
|
if not self._context.config.nodaemon and self._context.first:
|
184
236
|
self._context.daemonize()
|
@@ -186,7 +238,7 @@ class Supervisor:
|
|
186
238
|
# writing pid file needs to come *after* daemonizing or pid will be wrong
|
187
239
|
self._context.write_pidfile()
|
188
240
|
|
189
|
-
|
241
|
+
self._event_callbacks.notify(SupervisorRunningEvent())
|
190
242
|
|
191
243
|
while True:
|
192
244
|
if callback is not None and not callback(self):
|
@@ -202,10 +254,10 @@ class Supervisor:
|
|
202
254
|
def _run_once(self) -> None:
|
203
255
|
self._poll()
|
204
256
|
self._reap()
|
205
|
-
self.
|
257
|
+
self._signal_handler.handle_signals()
|
206
258
|
self._tick()
|
207
259
|
|
208
|
-
if self._context.state <
|
260
|
+
if self._context.state < SupervisorState.RUNNING:
|
209
261
|
self._ordered_stop_groups_phase_2()
|
210
262
|
|
211
263
|
def _ordered_stop_groups_phase_1(self) -> None:
|
@@ -228,15 +280,15 @@ class Supervisor:
|
|
228
280
|
combined_map = {}
|
229
281
|
combined_map.update(self.get_process_map())
|
230
282
|
|
231
|
-
pgroups = list(self._process_groups
|
283
|
+
pgroups = list(self._process_groups)
|
232
284
|
pgroups.sort()
|
233
285
|
|
234
|
-
if self._context.state <
|
286
|
+
if self._context.state < SupervisorState.RUNNING:
|
235
287
|
if not self._stopping:
|
236
288
|
# first time, set the stopping flag, do a notification and set stop_groups
|
237
289
|
self._stopping = True
|
238
290
|
self._stop_groups = pgroups[:]
|
239
|
-
|
291
|
+
self._event_callbacks.notify(SupervisorStoppingEvent())
|
240
292
|
|
241
293
|
self._ordered_stop_groups_phase_1()
|
242
294
|
|
@@ -246,12 +298,12 @@ class Supervisor:
|
|
246
298
|
|
247
299
|
for fd, dispatcher in combined_map.items():
|
248
300
|
if dispatcher.readable():
|
249
|
-
self.
|
301
|
+
self._poller.register_readable(fd)
|
250
302
|
if dispatcher.writable():
|
251
|
-
self.
|
303
|
+
self._poller.register_writable(fd)
|
252
304
|
|
253
305
|
timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
|
254
|
-
r, w = self.
|
306
|
+
r, w = self._poller.poll(timeout)
|
255
307
|
|
256
308
|
for fd in r:
|
257
309
|
if fd in combined_map:
|
@@ -260,7 +312,7 @@ class Supervisor:
|
|
260
312
|
log.debug('read event caused by %r', dispatcher)
|
261
313
|
dispatcher.handle_read_event()
|
262
314
|
if not dispatcher.readable():
|
263
|
-
self.
|
315
|
+
self._poller.unregister_readable(fd)
|
264
316
|
except ExitNow:
|
265
317
|
raise
|
266
318
|
except Exception: # noqa
|
@@ -270,7 +322,7 @@ class Supervisor:
|
|
270
322
|
# time, which may cause 100% cpu usage
|
271
323
|
log.debug('unexpected read event from fd %r', fd)
|
272
324
|
try:
|
273
|
-
self.
|
325
|
+
self._poller.unregister_readable(fd)
|
274
326
|
except Exception: # noqa
|
275
327
|
pass
|
276
328
|
|
@@ -281,7 +333,7 @@ class Supervisor:
|
|
281
333
|
log.debug('write event caused by %r', dispatcher)
|
282
334
|
dispatcher.handle_write_event()
|
283
335
|
if not dispatcher.writable():
|
284
|
-
self.
|
336
|
+
self._poller.unregister_writable(fd)
|
285
337
|
except ExitNow:
|
286
338
|
raise
|
287
339
|
except Exception: # noqa
|
@@ -289,7 +341,7 @@ class Supervisor:
|
|
289
341
|
else:
|
290
342
|
log.debug('unexpected write event from fd %r', fd)
|
291
343
|
try:
|
292
|
-
self.
|
344
|
+
self._poller.unregister_writable(fd)
|
293
345
|
except Exception: # noqa
|
294
346
|
pass
|
295
347
|
|
@@ -316,34 +368,6 @@ class Supervisor:
|
|
316
368
|
# keep reaping until no more kids to reap, but don't recurse infinitely
|
317
369
|
self._reap(once=False, depth=depth + 1)
|
318
370
|
|
319
|
-
def _handle_signal(self) -> None:
|
320
|
-
sig = self._context.get_signal()
|
321
|
-
if not sig:
|
322
|
-
return
|
323
|
-
|
324
|
-
if sig in (signal.SIGTERM, signal.SIGINT, signal.SIGQUIT):
|
325
|
-
log.warning('received %s indicating exit request', signame(sig))
|
326
|
-
self._context.set_state(SupervisorStates.SHUTDOWN)
|
327
|
-
|
328
|
-
elif sig == signal.SIGHUP:
|
329
|
-
if self._context.state == SupervisorStates.SHUTDOWN:
|
330
|
-
log.warning('ignored %s indicating restart request (shutdown in progress)', signame(sig)) # noqa
|
331
|
-
else:
|
332
|
-
log.warning('received %s indicating restart request', signame(sig)) # noqa
|
333
|
-
self._context.set_state(SupervisorStates.RESTARTING)
|
334
|
-
|
335
|
-
elif sig == signal.SIGCHLD:
|
336
|
-
log.debug('received %s indicating a child quit', signame(sig))
|
337
|
-
|
338
|
-
elif sig == signal.SIGUSR2:
|
339
|
-
log.info('received %s indicating log reopen request', signame(sig))
|
340
|
-
# self._context.reopen_logs()
|
341
|
-
for group in self._process_groups.values():
|
342
|
-
group.reopen_logs()
|
343
|
-
|
344
|
-
else:
|
345
|
-
log.debug('received %s indicating nothing', signame(sig))
|
346
|
-
|
347
371
|
def _tick(self, now: ta.Optional[float] = None) -> None:
|
348
372
|
"""Send one or more 'tick' events when the timeslice related to the period for the event type rolls over"""
|
349
373
|
|
@@ -352,7 +376,7 @@ class Supervisor:
|
|
352
376
|
now = time.time()
|
353
377
|
|
354
378
|
for event in TICK_EVENTS:
|
355
|
-
period = event.period
|
379
|
+
period = event.period
|
356
380
|
|
357
381
|
last_tick = self._ticks.get(period)
|
358
382
|
if last_tick is None:
|
@@ -362,4 +386,4 @@ class Supervisor:
|
|
362
386
|
this_tick = timeslice(period, now)
|
363
387
|
if this_tick != last_tick:
|
364
388
|
self._ticks[period] = this_tick
|
365
|
-
|
389
|
+
self._event_callbacks.notify(event(this_tick, self))
|
ominfra/supervisor/types.py
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
# ruff: noqa: UP006
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
2
|
import abc
|
3
|
+
import functools
|
3
4
|
import typing as ta
|
4
5
|
|
5
6
|
from .configs import ProcessConfig
|
7
|
+
from .configs import ProcessGroupConfig
|
6
8
|
from .configs import ServerConfig
|
9
|
+
from .states import ProcessState
|
7
10
|
from .states import SupervisorState
|
8
11
|
|
9
12
|
|
@@ -27,12 +30,8 @@ class AbstractServerContext(abc.ABC):
|
|
27
30
|
def pid_history(self) -> ta.Dict[int, 'AbstractSubprocess']:
|
28
31
|
raise NotImplementedError
|
29
32
|
|
30
|
-
@property
|
31
|
-
@abc.abstractmethod
|
32
|
-
def inherited_fds(self) -> ta.FrozenSet[int]:
|
33
|
-
raise NotImplementedError
|
34
|
-
|
35
33
|
|
34
|
+
@functools.total_ordering
|
36
35
|
class AbstractSubprocess(abc.ABC):
|
37
36
|
@property
|
38
37
|
@abc.abstractmethod
|
@@ -44,6 +43,12 @@ class AbstractSubprocess(abc.ABC):
|
|
44
43
|
def config(self) -> ProcessConfig:
|
45
44
|
raise NotImplementedError
|
46
45
|
|
46
|
+
def __lt__(self, other):
|
47
|
+
return self.config.priority < other.config.priority
|
48
|
+
|
49
|
+
def __eq__(self, other):
|
50
|
+
return self.config.priority == other.config.priority
|
51
|
+
|
47
52
|
@property
|
48
53
|
@abc.abstractmethod
|
49
54
|
def context(self) -> AbstractServerContext:
|
@@ -52,3 +57,49 @@ class AbstractSubprocess(abc.ABC):
|
|
52
57
|
@abc.abstractmethod
|
53
58
|
def finish(self, sts: int) -> None:
|
54
59
|
raise NotImplementedError
|
60
|
+
|
61
|
+
@abc.abstractmethod
|
62
|
+
def remove_logs(self) -> None:
|
63
|
+
raise NotImplementedError
|
64
|
+
|
65
|
+
@abc.abstractmethod
|
66
|
+
def reopen_logs(self) -> None:
|
67
|
+
raise NotImplementedError
|
68
|
+
|
69
|
+
@abc.abstractmethod
|
70
|
+
def stop(self) -> ta.Optional[str]:
|
71
|
+
raise NotImplementedError
|
72
|
+
|
73
|
+
@abc.abstractmethod
|
74
|
+
def give_up(self) -> None:
|
75
|
+
raise NotImplementedError
|
76
|
+
|
77
|
+
@abc.abstractmethod
|
78
|
+
def transition(self) -> None:
|
79
|
+
raise NotImplementedError
|
80
|
+
|
81
|
+
@abc.abstractmethod
|
82
|
+
def get_state(self) -> ProcessState:
|
83
|
+
raise NotImplementedError
|
84
|
+
|
85
|
+
@abc.abstractmethod
|
86
|
+
def create_auto_child_logs(self) -> None:
|
87
|
+
raise NotImplementedError
|
88
|
+
|
89
|
+
@abc.abstractmethod
|
90
|
+
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: # dict[int, Dispatcher]
|
91
|
+
raise NotImplementedError
|
92
|
+
|
93
|
+
|
94
|
+
@functools.total_ordering
|
95
|
+
class AbstractProcessGroup(abc.ABC):
|
96
|
+
@property
|
97
|
+
@abc.abstractmethod
|
98
|
+
def config(self) -> ProcessGroupConfig:
|
99
|
+
raise NotImplementedError
|
100
|
+
|
101
|
+
def __lt__(self, other):
|
102
|
+
return self.config.priority < other.config.priority
|
103
|
+
|
104
|
+
def __eq__(self, other):
|
105
|
+
return self.config.priority == other.config.priority
|