ominfra 0.0.0.dev126__py3-none-any.whl → 0.0.0.dev127__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.
Files changed (34) hide show
  1. ominfra/clouds/aws/auth.py +1 -1
  2. ominfra/deploy/_executor.py +1 -1
  3. ominfra/deploy/poly/_main.py +1 -1
  4. ominfra/pyremote/_runcommands.py +1 -1
  5. ominfra/scripts/journald2aws.py +2 -2
  6. ominfra/scripts/supervisor.py +1796 -1218
  7. ominfra/supervisor/collections.py +52 -0
  8. ominfra/supervisor/context.py +2 -336
  9. ominfra/supervisor/datatypes.py +1 -63
  10. ominfra/supervisor/dispatchers.py +20 -324
  11. ominfra/supervisor/dispatchersimpl.py +342 -0
  12. ominfra/supervisor/groups.py +33 -111
  13. ominfra/supervisor/groupsimpl.py +86 -0
  14. ominfra/supervisor/inject.py +44 -19
  15. ominfra/supervisor/main.py +1 -1
  16. ominfra/supervisor/pipes.py +83 -0
  17. ominfra/supervisor/poller.py +6 -3
  18. ominfra/supervisor/privileges.py +65 -0
  19. ominfra/supervisor/processes.py +18 -0
  20. ominfra/supervisor/{process.py → processesimpl.py} +96 -330
  21. ominfra/supervisor/setup.py +38 -0
  22. ominfra/supervisor/setupimpl.py +261 -0
  23. ominfra/supervisor/signals.py +24 -16
  24. ominfra/supervisor/spawning.py +31 -0
  25. ominfra/supervisor/spawningimpl.py +347 -0
  26. ominfra/supervisor/supervisor.py +52 -77
  27. ominfra/supervisor/types.py +101 -45
  28. ominfra/supervisor/users.py +64 -0
  29. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/METADATA +3 -3
  30. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/RECORD +34 -23
  31. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/LICENSE +0 -0
  32. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/WHEEL +0 -0
  33. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/entry_points.txt +0 -0
  34. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev127.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import errno
3
3
  import os.path
4
- import shlex
5
4
  import signal
6
5
  import time
7
6
  import traceback
@@ -9,47 +8,31 @@ import typing as ta
9
8
 
10
9
  from omlish.lite.check import check_isinstance
11
10
  from omlish.lite.logs import log
12
- from omlish.lite.typing import Func
11
+ from omlish.lite.typing import Func1
13
12
 
14
13
  from .configs import ProcessConfig
15
- from .context import check_execv_args
16
- from .context import close_child_pipes
17
- from .context import close_parent_pipes
18
- from .context import drop_privileges
19
- from .context import make_pipes
20
14
  from .datatypes import RestartUnconditionally
15
+ from .dispatchers import Dispatchers
21
16
  from .events import PROCESS_STATE_EVENT_MAP
22
17
  from .events import EventCallbacks
23
- from .events import ProcessCommunicationEvent
24
- from .events import ProcessCommunicationStderrEvent
25
- from .events import ProcessCommunicationStdoutEvent
26
- from .exceptions import BadCommandError
27
- from .exceptions import ProcessError
18
+ from .pipes import ProcessPipes
19
+ from .pipes import close_parent_pipes
20
+ from .processes import ProcessStateError
28
21
  from .signals import sig_name
22
+ from .spawning import ProcessSpawnError
23
+ from .spawning import ProcessSpawning
29
24
  from .states import ProcessState
30
25
  from .states import SupervisorState
31
- from .types import Dispatcher
32
26
  from .types import InputDispatcher
33
- from .types import OutputDispatcher
34
27
  from .types import Process
35
28
  from .types import ProcessGroup
36
29
  from .types import ServerContext
37
- from .utils import as_bytes
38
30
  from .utils import as_string
39
- from .utils import close_fd
40
- from .utils import compact_traceback
41
31
  from .utils import decode_wait_status
42
- from .utils import get_path
43
- from .utils import real_exit
44
32
 
45
33
 
46
- # (process: Process, event_type: ta.Type[ProcessCommunicationEvent], fd: int)
47
- OutputDispatcherFactory = ta.NewType('OutputDispatcherFactory', Func[OutputDispatcher])
48
-
49
- # (process: Process, event_type: ta.Type[ProcessCommunicationEvent], fd: int)
50
- InputDispatcherFactory = ta.NewType('InputDispatcherFactory', Func[InputDispatcher])
51
-
52
- InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
34
+ class ProcessSpawningFactory(Func1[Process, ProcessSpawning]):
35
+ pass
53
36
 
54
37
 
55
38
  ##
@@ -65,12 +48,7 @@ class ProcessImpl(Process):
65
48
  *,
66
49
  context: ServerContext,
67
50
  event_callbacks: EventCallbacks,
68
-
69
- output_dispatcher_factory: OutputDispatcherFactory,
70
- input_dispatcher_factory: InputDispatcherFactory,
71
-
72
- inherited_fds: ta.Optional[InheritedFds] = None,
73
-
51
+ process_spawning_factory: ProcessSpawningFactory,
74
52
  ) -> None:
75
53
  super().__init__()
76
54
 
@@ -80,13 +58,12 @@ class ProcessImpl(Process):
80
58
  self._context = context
81
59
  self._event_callbacks = event_callbacks
82
60
 
83
- self._output_dispatcher_factory = output_dispatcher_factory
84
- self._input_dispatcher_factory = input_dispatcher_factory
61
+ self._spawning = process_spawning_factory(self)
85
62
 
86
- self._inherited_fds = InheritedFds(frozenset(inherited_fds or []))
63
+ #
87
64
 
88
- self._dispatchers: ta.Dict[int, Dispatcher] = {}
89
- self._pipes: ta.Dict[str, int] = {}
65
+ self._dispatchers = Dispatchers([])
66
+ self._pipes = ProcessPipes()
90
67
 
91
68
  self._state = ProcessState.STOPPED
92
69
  self._pid = 0 # 0 when not running
@@ -106,17 +83,30 @@ class ProcessImpl(Process):
106
83
  self._exitstatus: ta.Optional[int] = None # status attached to dead process by finish()
107
84
  self._spawn_err: ta.Optional[str] = None # error message attached by spawn() if any
108
85
 
86
+ #
87
+
88
+ def __repr__(self) -> str:
89
+ return f'<Subprocess at {id(self)} with name {self._config.name} in state {self.get_state().name}>'
90
+
91
+ #
92
+
109
93
  @property
110
- def pid(self) -> int:
111
- return self._pid
94
+ def name(self) -> str:
95
+ return self._config.name
96
+
97
+ @property
98
+ def config(self) -> ProcessConfig:
99
+ return self._config
112
100
 
113
101
  @property
114
102
  def group(self) -> ProcessGroup:
115
103
  return self._group
116
104
 
117
105
  @property
118
- def config(self) -> ProcessConfig:
119
- return self._config
106
+ def pid(self) -> int:
107
+ return self._pid
108
+
109
+ #
120
110
 
121
111
  @property
122
112
  def context(self) -> ServerContext:
@@ -130,33 +120,59 @@ class ProcessImpl(Process):
130
120
  def backoff(self) -> int:
131
121
  return self._backoff
132
122
 
133
- def get_dispatchers(self) -> ta.Mapping[int, Dispatcher]:
134
- return self._dispatchers
123
+ #
124
+
125
+ def spawn(self) -> ta.Optional[int]:
126
+ process_name = as_string(self._config.name)
135
127
 
136
- def remove_logs(self) -> None:
137
- for dispatcher in self._dispatchers.values():
138
- if hasattr(dispatcher, 'remove_logs'):
139
- dispatcher.remove_logs()
140
-
141
- def reopen_logs(self) -> None:
142
- for dispatcher in self._dispatchers.values():
143
- if hasattr(dispatcher, 'reopen_logs'):
144
- dispatcher.reopen_logs()
145
-
146
- def drain(self) -> None:
147
- for dispatcher in self._dispatchers.values():
148
- # note that we *must* call readable() for every dispatcher, as it may have side effects for a given
149
- # dispatcher (eg. call handle_listener_state_change for event listener processes)
150
- if dispatcher.readable():
151
- dispatcher.handle_read_event()
152
- if dispatcher.writable():
153
- dispatcher.handle_write_event()
128
+ if self.pid:
129
+ log.warning('process \'%s\' already running', process_name)
130
+ return None
131
+
132
+ self.check_in_state(
133
+ ProcessState.EXITED,
134
+ ProcessState.FATAL,
135
+ ProcessState.BACKOFF,
136
+ ProcessState.STOPPED,
137
+ )
138
+
139
+ self._killing = False
140
+ self._spawn_err = None
141
+ self._exitstatus = None
142
+ self._system_stop = False
143
+ self._administrative_stop = False
144
+
145
+ self._last_start = time.time()
146
+
147
+ self.change_state(ProcessState.STARTING)
148
+
149
+ try:
150
+ sp = self._spawning.spawn()
151
+ except ProcessSpawnError as err:
152
+ log.exception('Spawn error')
153
+ self._spawn_err = err.args[0]
154
+ self.check_in_state(ProcessState.STARTING)
155
+ self.change_state(ProcessState.BACKOFF)
156
+ return None
157
+
158
+ log.info("Spawned: '%s' with pid %s", self.name, sp.pid)
159
+
160
+ self._pid = sp.pid
161
+ self._pipes = sp.pipes
162
+ self._dispatchers = sp.dispatchers
163
+
164
+ self._delay = time.time() + self.config.startsecs
165
+
166
+ return sp.pid
167
+
168
+ def get_dispatchers(self) -> Dispatchers:
169
+ return self._dispatchers
154
170
 
155
171
  def write(self, chars: ta.Union[bytes, str]) -> None:
156
172
  if not self.pid or self._killing:
157
173
  raise OSError(errno.EPIPE, 'Process already closed')
158
174
 
159
- stdin_fd = self._pipes['stdin']
175
+ stdin_fd = self._pipes.stdin
160
176
  if stdin_fd is None:
161
177
  raise OSError(errno.EPIPE, 'Process has no stdin channel')
162
178
 
@@ -167,51 +183,7 @@ class ProcessImpl(Process):
167
183
  dispatcher.write(chars)
168
184
  dispatcher.flush() # this must raise EPIPE if the pipe is closed
169
185
 
170
- def _get_execv_args(self) -> ta.Tuple[str, ta.Sequence[str]]:
171
- """
172
- Internal: turn a program name into a file name, using $PATH, make sure it exists / is executable, raising a
173
- ProcessError if not
174
- """
175
-
176
- try:
177
- commandargs = shlex.split(self._config.command)
178
- except ValueError as e:
179
- raise BadCommandError(f"can't parse command {self._config.command!r}: {e}") # noqa
180
-
181
- if commandargs:
182
- program = commandargs[0]
183
- else:
184
- raise BadCommandError('command is empty')
185
-
186
- if '/' in program:
187
- filename = program
188
- try:
189
- st = os.stat(filename)
190
- except OSError:
191
- st = None
192
-
193
- else:
194
- path = get_path()
195
- found = None
196
- st = None
197
- for dir in path: # noqa
198
- found = os.path.join(dir, program)
199
- try:
200
- st = os.stat(found)
201
- except OSError:
202
- pass
203
- else:
204
- break
205
- if st is None:
206
- filename = program
207
- else:
208
- filename = found # type: ignore
209
-
210
- # check_execv_args will raise a ProcessError if the execv args are bogus, we break it out into a separate
211
- # options method call here only to service unit tests
212
- check_execv_args(filename, commandargs, st)
213
-
214
- return filename, commandargs
186
+ #
215
187
 
216
188
  def change_state(self, new_state: ProcessState, expected: bool = True) -> bool:
217
189
  old_state = self._state
@@ -231,209 +203,14 @@ class ProcessImpl(Process):
231
203
 
232
204
  return True
233
205
 
234
- def _check_in_state(self, *states: ProcessState) -> None:
206
+ def check_in_state(self, *states: ProcessState) -> None:
235
207
  if self._state not in states:
236
- current_state = self._state.name
237
- allowable_states = ' '.join(s.name for s in states)
238
- process_name = as_string(self._config.name)
239
- raise RuntimeError('Assertion failed for %s: %s not in %s' % (process_name, current_state, allowable_states)) # noqa
240
-
241
- def _record_spawn_err(self, msg: str) -> None:
242
- self._spawn_err = msg
243
- log.info('_spawn_err: %s', msg)
244
-
245
- def spawn(self) -> ta.Optional[int]:
246
- process_name = as_string(self._config.name)
247
-
248
- if self.pid:
249
- log.warning('process \'%s\' already running', process_name)
250
- return None
251
-
252
- self._killing = False
253
- self._spawn_err = None
254
- self._exitstatus = None
255
- self._system_stop = False
256
- self._administrative_stop = False
257
-
258
- self._last_start = time.time()
259
-
260
- self._check_in_state(
261
- ProcessState.EXITED,
262
- ProcessState.FATAL,
263
- ProcessState.BACKOFF,
264
- ProcessState.STOPPED,
265
- )
266
-
267
- self.change_state(ProcessState.STARTING)
268
-
269
- try:
270
- filename, argv = self._get_execv_args()
271
- except ProcessError as what:
272
- self._record_spawn_err(what.args[0])
273
- self._check_in_state(ProcessState.STARTING)
274
- self.change_state(ProcessState.BACKOFF)
275
- return None
276
-
277
- try:
278
- self._dispatchers, self._pipes = self._make_dispatchers() # type: ignore
279
- except OSError as why:
280
- code = why.args[0]
281
- if code == errno.EMFILE:
282
- # too many file descriptors open
283
- msg = f"too many open files to spawn '{process_name}'"
284
- else:
285
- msg = f"unknown error making dispatchers for '{process_name}': {errno.errorcode.get(code, code)}"
286
- self._record_spawn_err(msg)
287
- self._check_in_state(ProcessState.STARTING)
288
- self.change_state(ProcessState.BACKOFF)
289
- return None
290
-
291
- try:
292
- pid = os.fork()
293
- except OSError as why:
294
- code = why.args[0]
295
- if code == errno.EAGAIN:
296
- # process table full
297
- msg = f'Too many processes in process table to spawn \'{process_name}\''
298
- else:
299
- msg = f'unknown error during fork for \'{process_name}\': {errno.errorcode.get(code, code)}'
300
- self._record_spawn_err(msg)
301
- self._check_in_state(ProcessState.STARTING)
302
- self.change_state(ProcessState.BACKOFF)
303
- close_parent_pipes(self._pipes)
304
- close_child_pipes(self._pipes)
305
- return None
306
-
307
- if pid != 0:
308
- return self._spawn_as_parent(pid)
309
-
310
- else:
311
- self._spawn_as_child(filename, argv)
312
- return None
313
-
314
- def _make_dispatchers(self) -> ta.Tuple[ta.Mapping[int, Dispatcher], ta.Mapping[str, int]]:
315
- use_stderr = not self._config.redirect_stderr
316
-
317
- p = make_pipes(use_stderr)
318
- stdout_fd, stderr_fd, stdin_fd = p['stdout'], p['stderr'], p['stdin']
319
-
320
- dispatchers: ta.Dict[int, Dispatcher] = {}
321
-
322
- dispatcher_kw = dict(
323
- event_callbacks=self._event_callbacks,
324
- )
325
-
326
- etype: ta.Type[ProcessCommunicationEvent]
327
- if stdout_fd is not None:
328
- etype = ProcessCommunicationStdoutEvent
329
- dispatchers[stdout_fd] = check_isinstance(self._output_dispatcher_factory(
330
- self,
331
- etype,
332
- stdout_fd,
333
- **dispatcher_kw,
334
- ), OutputDispatcher)
335
-
336
- if stderr_fd is not None:
337
- etype = ProcessCommunicationStderrEvent
338
- dispatchers[stderr_fd] = check_isinstance(self._output_dispatcher_factory(
339
- self,
340
- etype,
341
- stderr_fd,
342
- **dispatcher_kw,
343
- ), OutputDispatcher)
344
-
345
- if stdin_fd is not None:
346
- dispatchers[stdin_fd] = check_isinstance(self._input_dispatcher_factory(
347
- self,
348
- 'stdin',
349
- stdin_fd,
350
- **dispatcher_kw,
351
- ), InputDispatcher)
352
-
353
- return dispatchers, p
354
-
355
- def _spawn_as_parent(self, pid: int) -> int:
356
- # Parent
357
- self._pid = pid
358
- close_child_pipes(self._pipes)
359
- log.info('spawned: \'%s\' with pid %s', as_string(self._config.name), pid)
360
- self._spawn_err = None
361
- self._delay = time.time() + self._config.startsecs
362
- self.context.pid_history[pid] = self
363
- return pid
364
-
365
- def _prepare_child_fds(self) -> None:
366
- os.dup2(self._pipes['child_stdin'], 0)
367
- os.dup2(self._pipes['child_stdout'], 1)
368
- if self._config.redirect_stderr:
369
- os.dup2(self._pipes['child_stdout'], 2)
370
- else:
371
- os.dup2(self._pipes['child_stderr'], 2)
208
+ raise ProcessStateError(
209
+ f'Check failed for {self._config.name}: '
210
+ f'{self._state.name} not in {" ".join(s.name for s in states)}',
211
+ )
372
212
 
373
- for i in range(3, self.context.config.minfds):
374
- if i in self._inherited_fds:
375
- continue
376
- close_fd(i)
377
-
378
- def _spawn_as_child(self, filename: str, argv: ta.Sequence[str]) -> None:
379
- try:
380
- # prevent child from receiving signals sent to the parent by calling os.setpgrp to create a new process
381
- # group for the child; this prevents, for instance, the case of child processes being sent a SIGINT when
382
- # running supervisor in foreground mode and Ctrl-C in the terminal window running supervisord is pressed.
383
- # Presumably it also prevents HUP, etc received by supervisord from being sent to children.
384
- os.setpgrp()
385
-
386
- self._prepare_child_fds()
387
- # sending to fd 2 will put this output in the stderr log
388
-
389
- # set user
390
- setuid_msg = self.set_uid()
391
- if setuid_msg:
392
- uid = self._config.uid
393
- msg = f"couldn't setuid to {uid}: {setuid_msg}\n"
394
- os.write(2, as_bytes('supervisor: ' + msg))
395
- return # finally clause will exit the child process
396
-
397
- # set environment
398
- env = os.environ.copy()
399
- env['SUPERVISOR_ENABLED'] = '1'
400
- env['SUPERVISOR_PROCESS_NAME'] = self._config.name
401
- if self._group:
402
- env['SUPERVISOR_GROUP_NAME'] = self._group.config.name
403
- if self._config.environment is not None:
404
- env.update(self._config.environment)
405
-
406
- # change directory
407
- cwd = self._config.directory
408
- try:
409
- if cwd is not None:
410
- os.chdir(os.path.expanduser(cwd))
411
- except OSError as why:
412
- code = errno.errorcode.get(why.args[0], why.args[0])
413
- msg = f"couldn't chdir to {cwd}: {code}\n"
414
- os.write(2, as_bytes('supervisor: ' + msg))
415
- return # finally clause will exit the child process
416
-
417
- # set umask, then execve
418
- try:
419
- if self._config.umask is not None:
420
- os.umask(self._config.umask)
421
- os.execve(filename, list(argv), env)
422
- except OSError as why:
423
- code = errno.errorcode.get(why.args[0], why.args[0])
424
- msg = f"couldn't exec {argv[0]}: {code}\n"
425
- os.write(2, as_bytes('supervisor: ' + msg))
426
- except Exception: # noqa
427
- (file, fun, line), t, v, tbinfo = compact_traceback()
428
- error = f'{t}, {v}: file: {file} line: {line}'
429
- msg = f"couldn't exec {filename}: {error}\n"
430
- os.write(2, as_bytes('supervisor: ' + msg))
431
-
432
- # this point should only be reached if execve failed. the finally clause will exit the child process.
433
-
434
- finally:
435
- os.write(2, as_bytes('supervisor: child process was not spawned\n'))
436
- real_exit(127) # exit process with code for spawn failure
213
+ #
437
214
 
438
215
  def _check_and_adjust_for_system_clock_rollback(self, test_time):
439
216
  """
@@ -479,7 +256,7 @@ class ProcessImpl(Process):
479
256
  self._delay = 0
480
257
  self._backoff = 0
481
258
  self._system_stop = True
482
- self._check_in_state(ProcessState.BACKOFF)
259
+ self.check_in_state(ProcessState.BACKOFF)
483
260
  self.change_state(ProcessState.FATAL)
484
261
 
485
262
  def kill(self, sig: int) -> ta.Optional[str]:
@@ -523,7 +300,7 @@ class ProcessImpl(Process):
523
300
  self._killing = True
524
301
  self._delay = now + self._config.stopwaitsecs
525
302
  # we will already be in the STOPPING state if we're doing a SIGKILL as a result of overrunning stopwaitsecs
526
- self._check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
303
+ self.check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
527
304
  self.change_state(ProcessState.STOPPING)
528
305
 
529
306
  pid = self.pid
@@ -568,7 +345,7 @@ class ProcessImpl(Process):
568
345
 
569
346
  log.debug('sending %s (pid %s) sig %s', process_name, self.pid, sig_name(sig))
570
347
 
571
- self._check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
348
+ self.check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
572
349
 
573
350
  try:
574
351
  try:
@@ -597,7 +374,7 @@ class ProcessImpl(Process):
597
374
  def finish(self, sts: int) -> None:
598
375
  """The process was reaped and we need to report and manage its state."""
599
376
 
600
- self.drain()
377
+ self._dispatchers.drain()
601
378
 
602
379
  es, msg = decode_wait_status(sts)
603
380
 
@@ -628,7 +405,7 @@ class ProcessImpl(Process):
628
405
  self._exitstatus = es
629
406
 
630
407
  fmt, args = 'stopped: %s (%s)', (process_name, msg)
631
- self._check_in_state(ProcessState.STOPPING)
408
+ self.check_in_state(ProcessState.STOPPING)
632
409
  self.change_state(ProcessState.STOPPED)
633
410
  if exit_expected:
634
411
  log.info(fmt, *args)
@@ -639,7 +416,7 @@ class ProcessImpl(Process):
639
416
  # the program did not stay up long enough to make it to RUNNING implies STARTING -> BACKOFF
640
417
  self._exitstatus = None
641
418
  self._spawn_err = 'Exited too quickly (process log may have details)'
642
- self._check_in_state(ProcessState.STARTING)
419
+ self.check_in_state(ProcessState.STARTING)
643
420
  self.change_state(ProcessState.BACKOFF)
644
421
  log.warning('exited: %s (%s)', process_name, msg + '; not expected')
645
422
 
@@ -655,7 +432,7 @@ class ProcessImpl(Process):
655
432
  if self._state == ProcessState.STARTING:
656
433
  self.change_state(ProcessState.RUNNING)
657
434
 
658
- self._check_in_state(ProcessState.RUNNING)
435
+ self.check_in_state(ProcessState.RUNNING)
659
436
 
660
437
  if exit_expected:
661
438
  # expected exit code
@@ -669,19 +446,8 @@ class ProcessImpl(Process):
669
446
 
670
447
  self._pid = 0
671
448
  close_parent_pipes(self._pipes)
672
- self._pipes = {}
673
- self._dispatchers = {}
674
-
675
- def set_uid(self) -> ta.Optional[str]:
676
- if self._config.uid is None:
677
- return None
678
- msg = drop_privileges(self._config.uid)
679
- return msg
680
-
681
- def __repr__(self) -> str:
682
- # repr can't return anything other than a native string, but the name might be unicode - a problem on Python 2.
683
- name = self._config.name
684
- return f'<Subprocess at {id(self)} with name {name} in state {self.get_state().name}>'
449
+ self._pipes = ProcessPipes()
450
+ self._dispatchers = Dispatchers([])
685
451
 
686
452
  def get_state(self) -> ProcessState:
687
453
  return self._state
@@ -723,7 +489,7 @@ class ProcessImpl(Process):
723
489
  # proc.config.startsecs,
724
490
  self._delay = 0
725
491
  self._backoff = 0
726
- self._check_in_state(ProcessState.STARTING)
492
+ self.check_in_state(ProcessState.STARTING)
727
493
  self.change_state(ProcessState.RUNNING)
728
494
  msg = ('entered RUNNING state, process has stayed up for > than %s seconds (startsecs)' % self._config.startsecs) # noqa
729
495
  logger.info('success: %s %s', process_name, msg)
@@ -743,7 +509,7 @@ class ProcessImpl(Process):
743
509
  log.warning('killing \'%s\' (%s) with SIGKILL', process_name, self.pid)
744
510
  self.kill(signal.SIGKILL)
745
511
 
746
- def create_auto_child_logs(self) -> None:
512
+ def after_setuid(self) -> None:
747
513
  # temporary logfiles which are erased at start time
748
514
  # get_autoname = self.context.get_auto_child_log_name # noqa
749
515
  # sid = self.context.config.identifier # noqa
@@ -0,0 +1,38 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import typing as ta
4
+
5
+ from .users import User
6
+
7
+
8
+ ##
9
+
10
+
11
+ SupervisorUser = ta.NewType('SupervisorUser', User)
12
+
13
+
14
+ ##
15
+
16
+
17
+ class DaemonizeListener(abc.ABC): # noqa
18
+ def before_daemonize(self) -> None: # noqa
19
+ pass
20
+
21
+ def after_daemonize(self) -> None: # noqa
22
+ pass
23
+
24
+
25
+ DaemonizeListeners = ta.NewType('DaemonizeListeners', ta.Sequence[DaemonizeListener])
26
+
27
+
28
+ ##
29
+
30
+
31
+ class SupervisorSetup(abc.ABC):
32
+ @abc.abstractmethod
33
+ def setup(self) -> None:
34
+ raise NotImplementedError
35
+
36
+ @abc.abstractmethod
37
+ def cleanup(self) -> None:
38
+ raise NotImplementedError