ominfra 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev129__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  from .types import Dispatcher
3
- from .types import OutputDispatcher
3
+ from .types import ProcessOutputDispatcher
4
4
  from .utils.collections import KeyedCollection
5
5
  from .utils.ostypes import Fd
6
6
 
@@ -24,10 +24,10 @@ class Dispatchers(KeyedCollection[Fd, Dispatcher]):
24
24
 
25
25
  def remove_logs(self) -> None:
26
26
  for d in self:
27
- if isinstance(d, OutputDispatcher):
27
+ if isinstance(d, ProcessOutputDispatcher):
28
28
  d.remove_logs()
29
29
 
30
30
  def reopen_logs(self) -> None:
31
31
  for d in self:
32
- if isinstance(d, OutputDispatcher):
32
+ if isinstance(d, ProcessOutputDispatcher):
33
33
  d.reopen_logs()
@@ -8,14 +8,15 @@ import typing as ta
8
8
  from omlish.lite.logs import log
9
9
 
10
10
  from .configs import ProcessConfig
11
+ from .configs import ServerConfig
11
12
  from .events import EventCallbacks
12
13
  from .events import ProcessCommunicationEvent
13
14
  from .events import ProcessLogStderrEvent
14
15
  from .events import ProcessLogStdoutEvent
15
- from .types import Dispatcher
16
- from .types import InputDispatcher
17
- from .types import OutputDispatcher
18
16
  from .types import Process
17
+ from .types import ProcessDispatcher
18
+ from .types import ProcessInputDispatcher
19
+ from .types import ProcessOutputDispatcher
19
20
  from .utils.diag import compact_traceback
20
21
  from .utils.fds import read_fd
21
22
  from .utils.ostypes import Fd
@@ -24,7 +25,7 @@ from .utils.strings import find_prefix_at_end
24
25
  from .utils.strings import strip_escapes
25
26
 
26
27
 
27
- class BaseDispatcherImpl(Dispatcher, abc.ABC):
28
+ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
28
29
  def __init__(
29
30
  self,
30
31
  process: Process,
@@ -32,6 +33,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
32
33
  fd: Fd,
33
34
  *,
34
35
  event_callbacks: EventCallbacks,
36
+ server_config: ServerConfig,
35
37
  ) -> None:
36
38
  super().__init__()
37
39
 
@@ -39,6 +41,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
39
41
  self._channel = channel # 'stderr' or 'stdout'
40
42
  self._fd = fd
41
43
  self._event_callbacks = event_callbacks
44
+ self._server_config = server_config
42
45
 
43
46
  self._closed = False # True if close() has been called
44
47
 
@@ -79,7 +82,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
79
82
  self.close()
80
83
 
81
84
 
82
- class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
85
+ class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispatcher):
83
86
  """
84
87
  Dispatcher for one channel (stdout or stderr) of one process. Serves several purposes:
85
88
 
@@ -95,12 +98,14 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
95
98
  fd: Fd,
96
99
  *,
97
100
  event_callbacks: EventCallbacks,
101
+ server_config: ServerConfig,
98
102
  ) -> None:
99
103
  super().__init__(
100
104
  process,
101
105
  event_type.channel,
102
106
  fd,
103
107
  event_callbacks=event_callbacks,
108
+ server_config=server_config,
104
109
  )
105
110
 
106
111
  self._event_type = event_type
@@ -124,11 +129,10 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
124
129
 
125
130
  self._main_log_level = logging.DEBUG
126
131
 
127
- self._log_to_main_log = process.context.config.loglevel <= self._main_log_level
132
+ self._log_to_main_log = self._server_config.loglevel <= self._main_log_level
128
133
 
129
- config = self._process.config
130
- self._stdout_events_enabled = config.stdout.events_enabled
131
- self._stderr_events_enabled = config.stderr.events_enabled
134
+ self._stdout_events_enabled = self._process.config.stdout.events_enabled
135
+ self._stderr_events_enabled = self._process.config.stderr.events_enabled
132
136
 
133
137
  _child_log: ta.Optional[logging.Logger] = None # the current logger (normal_log or capture_log)
134
138
  _normal_log: ta.Optional[logging.Logger] = None # the "normal" (non-capture) logger
@@ -199,7 +203,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
199
203
  if not data:
200
204
  return
201
205
 
202
- if self._process.context.config.strip_ansi:
206
+ if self._server_config.strip_ansi:
203
207
  data = strip_escapes(as_bytes(data))
204
208
 
205
209
  if self._child_log:
@@ -297,7 +301,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
297
301
  self.close()
298
302
 
299
303
 
300
- class InputDispatcherImpl(BaseDispatcherImpl, InputDispatcher):
304
+ class ProcessInputDispatcherImpl(BaseProcessDispatcherImpl, ProcessInputDispatcher):
301
305
  def __init__(
302
306
  self,
303
307
  process: Process,
@@ -305,12 +309,14 @@ class InputDispatcherImpl(BaseDispatcherImpl, InputDispatcher):
305
309
  fd: Fd,
306
310
  *,
307
311
  event_callbacks: EventCallbacks,
312
+ server_config: ServerConfig,
308
313
  ) -> None:
309
314
  super().__init__(
310
315
  process,
311
316
  channel,
312
317
  fd,
313
318
  event_callbacks=event_callbacks,
319
+ server_config=server_config,
314
320
  )
315
321
 
316
322
  self._input_buffer = b''
@@ -61,7 +61,7 @@ class ProcessGroupImpl(ProcessGroup):
61
61
  #
62
62
 
63
63
  def get_unstopped_processes(self) -> ta.List[Process]:
64
- return [x for x in self if not x.get_state().stopped]
64
+ return [x for x in self if not x.state.stopped]
65
65
 
66
66
  def stop_all(self) -> None:
67
67
  processes = list(self._by_name.values())
@@ -69,7 +69,7 @@ class ProcessGroupImpl(ProcessGroup):
69
69
  processes.reverse() # stop in desc priority order
70
70
 
71
71
  for proc in processes:
72
- state = proc.get_state()
72
+ state = proc.state
73
73
  if state == ProcessState.RUNNING:
74
74
  # RUNNING -> STOPPING
75
75
  proc.stop()
@@ -6,13 +6,13 @@ from omlish.lite.inject import InjectorBindings
6
6
  from omlish.lite.inject import inj
7
7
 
8
8
  from .configs import ServerConfig
9
- from .context import ServerContextImpl
10
- from .dispatchersimpl import InputDispatcherImpl
11
- from .dispatchersimpl import OutputDispatcherImpl
9
+ from .dispatchersimpl import ProcessInputDispatcherImpl
10
+ from .dispatchersimpl import ProcessOutputDispatcherImpl
12
11
  from .events import EventCallbacks
13
12
  from .groups import ProcessGroupManager
14
13
  from .groupsimpl import ProcessFactory
15
14
  from .groupsimpl import ProcessGroupImpl
15
+ from .io import IoManager
16
16
  from .poller import Poller
17
17
  from .poller import get_poller_impl
18
18
  from .process import PidHistory
@@ -23,15 +23,16 @@ from .setup import DaemonizeListeners
23
23
  from .setup import SupervisorUser
24
24
  from .setupimpl import SupervisorSetup
25
25
  from .setupimpl import SupervisorSetupImpl
26
+ from .signals import SignalHandler
26
27
  from .spawningimpl import InheritedFds
27
- from .spawningimpl import InputDispatcherFactory
28
- from .spawningimpl import OutputDispatcherFactory
28
+ from .spawningimpl import ProcessInputDispatcherFactory
29
+ from .spawningimpl import ProcessOutputDispatcherFactory
29
30
  from .spawningimpl import ProcessSpawningImpl
30
31
  from .supervisor import ProcessGroupFactory
31
- from .supervisor import SignalHandler
32
32
  from .supervisor import Supervisor
33
- from .types import ServerContext
33
+ from .supervisor import SupervisorStateManagerImpl
34
34
  from .types import ServerEpoch
35
+ from .types import SupervisorStateManager
35
36
  from .utils.signals import SignalReceiver
36
37
  from .utils.users import get_user
37
38
 
@@ -52,17 +53,21 @@ def bind_server(
52
53
 
53
54
  inj.bind(DaemonizeListener, array=True, to_key=Poller),
54
55
 
55
- inj.bind(ServerContextImpl, singleton=True),
56
- inj.bind(ServerContext, to_key=ServerContextImpl),
57
-
58
56
  inj.bind(EventCallbacks, singleton=True),
59
57
 
60
58
  inj.bind(SignalReceiver, singleton=True),
61
59
 
60
+ inj.bind(IoManager, singleton=True),
61
+
62
62
  inj.bind(SignalHandler, singleton=True),
63
+
63
64
  inj.bind(ProcessGroupManager, singleton=True),
65
+
64
66
  inj.bind(Supervisor, singleton=True),
65
67
 
68
+ inj.bind(SupervisorStateManagerImpl, singleton=True),
69
+ inj.bind(SupervisorStateManager, to_key=SupervisorStateManagerImpl),
70
+
66
71
  inj.bind(PidHistory()),
67
72
 
68
73
  inj.bind_factory(ProcessGroupImpl, ProcessGroupFactory),
@@ -70,8 +75,8 @@ def bind_server(
70
75
 
71
76
  inj.bind_factory(ProcessSpawningImpl, ProcessSpawningFactory),
72
77
 
73
- inj.bind_factory(OutputDispatcherImpl, OutputDispatcherFactory),
74
- inj.bind_factory(InputDispatcherImpl, InputDispatcherFactory),
78
+ inj.bind_factory(ProcessOutputDispatcherImpl, ProcessOutputDispatcherFactory),
79
+ inj.bind_factory(ProcessInputDispatcherImpl, ProcessInputDispatcherFactory),
75
80
  ]
76
81
 
77
82
  #
@@ -0,0 +1,82 @@
1
+ # ruff: noqa: UP006 UP007
2
+ from omlish.lite.logs import log
3
+
4
+ from .dispatchers import Dispatchers
5
+ from .groups import ProcessGroupManager
6
+ from .poller import Poller
7
+ from .types import ExitNow
8
+
9
+
10
+ ##
11
+
12
+
13
+ class IoManager:
14
+ def __init__(
15
+ self,
16
+ *,
17
+ poller: Poller,
18
+ process_groups: ProcessGroupManager,
19
+ ) -> None:
20
+ super().__init__()
21
+
22
+ self._poller = poller
23
+ self._process_groups = process_groups
24
+
25
+ def get_dispatchers(self) -> Dispatchers:
26
+ return Dispatchers(
27
+ d
28
+ for p in self._process_groups.all_processes()
29
+ for d in p.get_dispatchers()
30
+ )
31
+
32
+ def poll(self) -> None:
33
+ dispatchers = self.get_dispatchers()
34
+
35
+ for fd, dispatcher in dispatchers.items():
36
+ if dispatcher.readable():
37
+ self._poller.register_readable(fd)
38
+ if dispatcher.writable():
39
+ self._poller.register_writable(fd)
40
+
41
+ timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
42
+ r, w = self._poller.poll(timeout)
43
+
44
+ for fd in r:
45
+ if fd in dispatchers:
46
+ try:
47
+ dispatcher = dispatchers[fd]
48
+ log.debug('read event caused by %r', dispatcher)
49
+ dispatcher.handle_read_event()
50
+ if not dispatcher.readable():
51
+ self._poller.unregister_readable(fd)
52
+ except ExitNow:
53
+ raise
54
+ except Exception: # noqa
55
+ dispatchers[fd].handle_error()
56
+ else:
57
+ # if the fd is not in combined map, we should unregister it. otherwise, it will be polled every
58
+ # time, which may cause 100% cpu usage
59
+ log.debug('unexpected read event from fd %r', fd)
60
+ try:
61
+ self._poller.unregister_readable(fd)
62
+ except Exception: # noqa
63
+ pass
64
+
65
+ for fd in w:
66
+ if fd in dispatchers:
67
+ try:
68
+ dispatcher = dispatchers[fd]
69
+ log.debug('write event caused by %r', dispatcher)
70
+ dispatcher.handle_write_event()
71
+ if not dispatcher.writable():
72
+ self._poller.unregister_writable(fd)
73
+ except ExitNow:
74
+ raise
75
+ except Exception: # noqa
76
+ dispatchers[fd].handle_error()
77
+ else:
78
+ log.debug('unexpected write event from fd %r', fd)
79
+ try:
80
+ self._poller.unregister_writable(fd)
81
+ except Exception: # noqa
82
+ pass
@@ -37,17 +37,17 @@ from omlish.lite.http.coroserver import CoroHttpServer
37
37
  from omlish.lite.inject import inj
38
38
  from omlish.lite.journald import journald_log_handler_factory
39
39
  from omlish.lite.logs import configure_standard_logging
40
+ from omlish.lite.runtime import is_debugger_attached
40
41
 
41
42
  from ..configs import read_config_file
42
43
  from .configs import ServerConfig
43
44
  from .configs import prepare_server_config
44
- from .context import ServerContextImpl
45
- from .context import ServerEpoch
46
45
  from .inject import bind_server
47
46
  from .spawningimpl import InheritedFds
48
47
  from .states import SupervisorState
49
- from .supervisor import ExitNow
50
48
  from .supervisor import Supervisor
49
+ from .types import ExitNow
50
+ from .types import ServerEpoch
51
51
  from .utils.fds import get_open_fds
52
52
 
53
53
 
@@ -79,7 +79,7 @@ def main(
79
79
  if not no_logging:
80
80
  configure_standard_logging(
81
81
  'INFO',
82
- handler_factory=journald_log_handler_factory if not args.no_journald else None,
82
+ handler_factory=journald_log_handler_factory if not (args.no_journald or is_debugger_attached()) else None,
83
83
  )
84
84
 
85
85
  #
@@ -102,7 +102,6 @@ def main(
102
102
  inherited_fds=inherited_fds,
103
103
  ))
104
104
 
105
- context = injector[ServerContextImpl]
106
105
  supervisor = injector[Supervisor]
107
106
 
108
107
  try:
@@ -110,7 +109,7 @@ def main(
110
109
  except ExitNow:
111
110
  pass
112
111
 
113
- if context.state < SupervisorState.RESTARTING:
112
+ if supervisor.state < SupervisorState.RESTARTING:
114
113
  break
115
114
 
116
115
 
@@ -22,10 +22,10 @@ from .spawning import ProcessSpawnError
22
22
  from .spawning import ProcessSpawning
23
23
  from .states import ProcessState
24
24
  from .states import SupervisorState
25
- from .types import InputDispatcher
26
25
  from .types import Process
27
26
  from .types import ProcessGroup
28
- from .types import ServerContext
27
+ from .types import ProcessInputDispatcher
28
+ from .types import SupervisorStateManager
29
29
  from .utils.os import decode_wait_status
30
30
  from .utils.ostypes import Pid
31
31
  from .utils.ostypes import Rc
@@ -47,7 +47,7 @@ class ProcessImpl(Process):
47
47
  config: ProcessConfig,
48
48
  group: ProcessGroup,
49
49
  *,
50
- context: ServerContext,
50
+ supervisor_states: SupervisorStateManager,
51
51
  event_callbacks: EventCallbacks,
52
52
  process_spawning_factory: ProcessSpawningFactory,
53
53
  ) -> None:
@@ -56,7 +56,7 @@ class ProcessImpl(Process):
56
56
  self._config = config
57
57
  self._group = group
58
58
 
59
- self._context = context
59
+ self._supervisor_states = supervisor_states
60
60
  self._event_callbacks = event_callbacks
61
61
 
62
62
  self._spawning = process_spawning_factory(self)
@@ -87,7 +87,7 @@ class ProcessImpl(Process):
87
87
  #
88
88
 
89
89
  def __repr__(self) -> str:
90
- return f'<Subprocess at {id(self)} with name {self._config.name} in state {self.get_state().name}>'
90
+ return f'<Subprocess at {id(self)} with name {self._config.name} in state {self._state.name}>'
91
91
 
92
92
  #
93
93
 
@@ -109,10 +109,6 @@ class ProcessImpl(Process):
109
109
 
110
110
  #
111
111
 
112
- @property
113
- def context(self) -> ServerContext:
114
- return self._context
115
-
116
112
  @property
117
113
  def state(self) -> ProcessState:
118
114
  return self._state
@@ -175,7 +171,7 @@ class ProcessImpl(Process):
175
171
  if stdin_fd is None:
176
172
  raise OSError(errno.EPIPE, 'Process has no stdin channel')
177
173
 
178
- dispatcher = check_isinstance(self._dispatchers[stdin_fd], InputDispatcher)
174
+ dispatcher = check_isinstance(self._dispatchers[stdin_fd], ProcessInputDispatcher)
179
175
  if dispatcher.closed:
180
176
  raise OSError(errno.EPIPE, "Process' stdin channel is closed")
181
177
 
@@ -445,9 +441,6 @@ class ProcessImpl(Process):
445
441
  self._pipes = ProcessPipes()
446
442
  self._dispatchers = Dispatchers([])
447
443
 
448
- def get_state(self) -> ProcessState:
449
- return self._state
450
-
451
444
  def transition(self) -> None:
452
445
  now = time.time()
453
446
  state = self._state
@@ -456,7 +449,7 @@ class ProcessImpl(Process):
456
449
 
457
450
  logger = log
458
451
 
459
- if self.context.state > SupervisorState.RESTARTING:
452
+ if self._supervisor_states.state > SupervisorState.RESTARTING:
460
453
  # dont start any processes if supervisor is shutting down
461
454
  if state == ProcessState.EXITED:
462
455
  if self._config.autorestart:
@@ -0,0 +1,66 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import signal
3
+
4
+ from omlish.lite.logs import log
5
+
6
+ from .groups import ProcessGroupManager
7
+ from .states import SupervisorState
8
+ from .types import ProcessOutputDispatcher
9
+ from .types import SupervisorStateManager
10
+ from .utils.signals import SignalReceiver
11
+ from .utils.signals import sig_name
12
+
13
+
14
+ class SignalHandler:
15
+ def __init__(
16
+ self,
17
+ *,
18
+ states: SupervisorStateManager,
19
+ signal_receiver: SignalReceiver,
20
+ process_groups: ProcessGroupManager,
21
+ ) -> None:
22
+ super().__init__()
23
+
24
+ self._states = states
25
+ self._signal_receiver = signal_receiver
26
+ self._process_groups = process_groups
27
+
28
+ def set_signals(self) -> None:
29
+ self._signal_receiver.install(
30
+ signal.SIGTERM,
31
+ signal.SIGINT,
32
+ signal.SIGQUIT,
33
+ signal.SIGHUP,
34
+ signal.SIGCHLD,
35
+ signal.SIGUSR2,
36
+ )
37
+
38
+ def handle_signals(self) -> None:
39
+ sig = self._signal_receiver.get_signal()
40
+ if not sig:
41
+ return
42
+
43
+ if sig in (signal.SIGTERM, signal.SIGINT, signal.SIGQUIT):
44
+ log.warning('received %s indicating exit request', sig_name(sig))
45
+ self._states.set_state(SupervisorState.SHUTDOWN)
46
+
47
+ elif sig == signal.SIGHUP:
48
+ if self._states.state == SupervisorState.SHUTDOWN:
49
+ log.warning('ignored %s indicating restart request (shutdown in progress)', sig_name(sig)) # noqa
50
+ else:
51
+ log.warning('received %s indicating restart request', sig_name(sig)) # noqa
52
+ self._states.set_state(SupervisorState.RESTARTING)
53
+
54
+ elif sig == signal.SIGCHLD:
55
+ log.debug('received %s indicating a child quit', sig_name(sig))
56
+
57
+ elif sig == signal.SIGUSR2:
58
+ log.info('received %s indicating log reopen request', sig_name(sig))
59
+
60
+ for p in self._process_groups.all_processes():
61
+ for d in p.get_dispatchers():
62
+ if isinstance(d, ProcessOutputDispatcher):
63
+ d.reopen_logs()
64
+
65
+ else:
66
+ log.debug('received %s indicating nothing', sig_name(sig))
@@ -30,10 +30,10 @@ from .spawning import ProcessSpawnError
30
30
  from .spawning import ProcessSpawning
31
31
  from .spawning import SpawnedProcess
32
32
  from .types import Dispatcher
33
- from .types import InputDispatcher
34
- from .types import OutputDispatcher
35
33
  from .types import Process
36
34
  from .types import ProcessGroup
35
+ from .types import ProcessInputDispatcher
36
+ from .types import ProcessOutputDispatcher
37
37
  from .utils.diag import compact_traceback
38
38
  from .utils.fds import close_fd
39
39
  from .utils.fs import get_path
@@ -44,11 +44,11 @@ from .utils.ostypes import Rc
44
44
  from .utils.strings import as_bytes
45
45
 
46
46
 
47
- class OutputDispatcherFactory(Func3[Process, ta.Type[ProcessCommunicationEvent], Fd, OutputDispatcher]):
47
+ class ProcessOutputDispatcherFactory(Func3[Process, ta.Type[ProcessCommunicationEvent], Fd, ProcessOutputDispatcher]):
48
48
  pass
49
49
 
50
50
 
51
- class InputDispatcherFactory(Func3[Process, str, Fd, InputDispatcher]):
51
+ class ProcessInputDispatcherFactory(Func3[Process, str, Fd, ProcessInputDispatcher]):
52
52
  pass
53
53
 
54
54
 
@@ -66,8 +66,8 @@ class ProcessSpawningImpl(ProcessSpawning):
66
66
  server_config: ServerConfig,
67
67
  pid_history: PidHistory,
68
68
 
69
- output_dispatcher_factory: OutputDispatcherFactory,
70
- input_dispatcher_factory: InputDispatcherFactory,
69
+ output_dispatcher_factory: ProcessOutputDispatcherFactory,
70
+ input_dispatcher_factory: ProcessInputDispatcherFactory,
71
71
 
72
72
  inherited_fds: ta.Optional[InheritedFds] = None,
73
73
  ) -> None:
@@ -207,21 +207,21 @@ class ProcessSpawningImpl(ProcessSpawning):
207
207
  self.process,
208
208
  ProcessCommunicationStdoutEvent,
209
209
  pipes.stdout,
210
- ), OutputDispatcher))
210
+ ), ProcessOutputDispatcher))
211
211
 
212
212
  if pipes.stderr is not None:
213
213
  dispatchers.append(check_isinstance(self._output_dispatcher_factory(
214
214
  self.process,
215
215
  ProcessCommunicationStderrEvent,
216
216
  pipes.stderr,
217
- ), OutputDispatcher))
217
+ ), ProcessOutputDispatcher))
218
218
 
219
219
  if pipes.stdin is not None:
220
220
  dispatchers.append(check_isinstance(self._input_dispatcher_factory(
221
221
  self.process,
222
222
  'stdin',
223
223
  pipes.stdin,
224
- ), InputDispatcher))
224
+ ), ProcessInputDispatcher))
225
225
 
226
226
  return Dispatchers(dispatchers)
227
227