ominfra 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev129__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.
@@ -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