ominfra 0.0.0.dev126__py3-none-any.whl → 0.0.0.dev127__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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