ominfra 0.0.0.dev121__py3-none-any.whl → 0.0.0.dev123__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ominfra/deploy/_executor.py +10 -1
- ominfra/journald/messages.py +1 -1
- ominfra/pyremote/_runcommands.py +10 -1
- ominfra/scripts/journald2aws.py +10 -2
- ominfra/scripts/supervisor.py +2227 -2043
- 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.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/RECORD +23 -21
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev121.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
|