ominfra 0.0.0.dev129__py3-none-any.whl → 0.0.0.dev131__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.
@@ -37,32 +37,32 @@ class ProcessConfig:
37
37
  umask: ta.Optional[int] = None
38
38
  priority: int = 999
39
39
 
40
- autostart: bool = True
41
- autorestart: str = 'unexpected'
40
+ auto_start: bool = True
41
+ auto_restart: str = 'unexpected'
42
42
 
43
- startsecs: int = 1
44
- startretries: int = 3
43
+ start_secs: int = 1
44
+ start_retries: int = 3
45
45
 
46
- numprocs: int = 1
47
- numprocs_start: int = 0
46
+ num_procs: int = 1
47
+ num_procs_start: int = 0
48
48
 
49
49
  @dc.dataclass(frozen=True)
50
50
  class Log:
51
51
  file: ta.Optional[str] = None
52
- capture_maxbytes: ta.Optional[int] = None
52
+ capture_max_bytes: ta.Optional[int] = None
53
53
  events_enabled: bool = False
54
54
  syslog: bool = False
55
55
  backups: ta.Optional[int] = None
56
- maxbytes: ta.Optional[int] = None
56
+ max_bytes: ta.Optional[int] = None
57
57
 
58
58
  stdout: Log = Log()
59
59
  stderr: Log = Log()
60
60
 
61
- stopsignal: int = signal.SIGTERM
62
- stopwaitsecs: int = 10
63
- stopasgroup: bool = False
61
+ stop_signal: int = signal.SIGTERM
62
+ stop_wait_secs: int = 10
63
+ stop_as_group: bool = False
64
64
 
65
- killasgroup: bool = False
65
+ kill_as_group: bool = False
66
66
 
67
67
  exitcodes: ta.Sequence[int] = (0,)
68
68
 
@@ -87,14 +87,14 @@ class ServerConfig:
87
87
  umask: int = 0o22
88
88
  directory: ta.Optional[str] = None
89
89
  logfile: str = 'supervisord.log'
90
- logfile_maxbytes: int = 50 * 1024 * 1024
90
+ logfile_max_bytes: int = 50 * 1024 * 1024
91
91
  logfile_backups: int = 10
92
92
  loglevel: int = logging.INFO
93
93
  pidfile: str = 'supervisord.pid'
94
94
  identifier: str = 'supervisor'
95
95
  child_logdir: str = '/dev/null'
96
- minfds: int = 1024
97
- minprocs: int = 200
96
+ min_fds: int = 1024
97
+ min_procs: int = 200
98
98
  nocleanup: bool = False
99
99
  strip_ansi: bool = False
100
100
  silent: bool = False
@@ -107,7 +107,7 @@ class ServerConfig:
107
107
  umask: ta.Union[int, str] = 0o22,
108
108
  directory: ta.Optional[str] = None,
109
109
  logfile: str = 'supervisord.log',
110
- logfile_maxbytes: ta.Union[int, str] = 50 * 1024 * 1024,
110
+ logfile_max_bytes: ta.Union[int, str] = 50 * 1024 * 1024,
111
111
  loglevel: ta.Union[int, str] = logging.INFO,
112
112
  pidfile: str = 'supervisord.pid',
113
113
  child_logdir: ta.Optional[str] = None,
@@ -117,7 +117,7 @@ class ServerConfig:
117
117
  umask=parse_octal(umask),
118
118
  directory=check_existing_dir(directory) if directory is not None else None,
119
119
  logfile=check_path_with_existing_dir(logfile),
120
- logfile_maxbytes=parse_bytes_size(logfile_maxbytes),
120
+ logfile_max_bytes=parse_bytes_size(logfile_max_bytes),
121
121
  loglevel=parse_logging_level(loglevel),
122
122
  pidfile=check_path_with_existing_dir(pidfile),
123
123
  child_logdir=child_logdir if child_logdir else tempfile.gettempdir(),
@@ -1,13 +1,14 @@
1
1
  # ruff: noqa: UP006 UP007
2
- from .types import Dispatcher
2
+ from omlish.lite.fdio.handlers import FdIoHandler
3
+
3
4
  from .types import ProcessOutputDispatcher
4
5
  from .utils.collections import KeyedCollection
5
6
  from .utils.ostypes import Fd
6
7
 
7
8
 
8
- class Dispatchers(KeyedCollection[Fd, Dispatcher]):
9
- def _key(self, v: Dispatcher) -> Fd:
10
- return v.fd
9
+ class Dispatchers(KeyedCollection[Fd, FdIoHandler]):
10
+ def _key(self, v: FdIoHandler) -> Fd:
11
+ return Fd(v.fd())
11
12
 
12
13
  #
13
14
 
@@ -16,9 +17,9 @@ class Dispatchers(KeyedCollection[Fd, Dispatcher]):
16
17
  # note that we *must* call readable() for every dispatcher, as it may have side effects for a given
17
18
  # dispatcher (eg. call handle_listener_state_change for event listener processes)
18
19
  if d.readable():
19
- d.handle_read_event()
20
+ d.on_readable()
20
21
  if d.writable():
21
- d.handle_write_event()
22
+ d.on_writable()
22
23
 
23
24
  #
24
25
 
@@ -60,7 +60,6 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
60
60
  def channel(self) -> str:
61
61
  return self._channel
62
62
 
63
- @property
64
63
  def fd(self) -> Fd:
65
64
  return self._fd
66
65
 
@@ -75,7 +74,7 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
75
74
  log.debug('fd %s closed, stopped monitoring %s', self._fd, self)
76
75
  self._closed = True
77
76
 
78
- def handle_error(self) -> None:
77
+ def on_error(self, exc: ta.Optional[BaseException] = None) -> None:
79
78
  nil, t, v, tbinfo = compact_traceback()
80
79
 
81
80
  log.critical('uncaptured python exception, closing channel %s (%s:%s %s)', repr(self), t, v, tbinfo)
@@ -148,7 +147,7 @@ class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispat
148
147
  channel = self._channel # noqa
149
148
 
150
149
  logfile = self._lc.file
151
- maxbytes = self._lc.maxbytes # noqa
150
+ max_bytes = self._lc.max_bytes # noqa
152
151
  backups = self._lc.backups # noqa
153
152
  to_syslog = self._lc.syslog
154
153
 
@@ -160,8 +159,8 @@ class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispat
160
159
  # self.normal_log,
161
160
  # filename=logfile,
162
161
  # fmt='%(message)s',
163
- # rotating=bool(maxbytes), # optimization
164
- # maxbytes=maxbytes,
162
+ # rotating=bool(max_bytes), # optimization
163
+ # max_bytes=max_bytes,
165
164
  # backups=backups,
166
165
  # )
167
166
 
@@ -173,17 +172,17 @@ class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispat
173
172
 
174
173
  def _init_capture_log(self) -> None:
175
174
  """
176
- Configure the capture log for this process. This log is used to temporarily capture output when special output
175
+ Configure the capture log for this process. This log is used to temporarily capture output when special output
177
176
  is detected. Sets self.capture_log if capturing is enabled.
178
177
  """
179
178
 
180
- capture_maxbytes = self._lc.capture_maxbytes
181
- if capture_maxbytes:
179
+ capture_max_bytes = self._lc.capture_max_bytes
180
+ if capture_max_bytes:
182
181
  self._capture_log = logging.getLogger(__name__)
183
182
  # loggers.handle_boundIO(
184
183
  # self._capture_log,
185
184
  # fmt='%(message)s',
186
- # maxbytes=capture_maxbytes,
185
+ # max_bytes=capture_max_bytes,
187
186
  # )
188
187
 
189
188
  def remove_logs(self) -> None:
@@ -291,12 +290,12 @@ class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispat
291
290
  return False
292
291
  return True
293
292
 
294
- def handle_read_event(self) -> None:
293
+ def on_readable(self) -> None:
295
294
  data = read_fd(self._fd)
296
295
  self._output_buffer += data
297
296
  self.record_output()
298
297
  if not data:
299
- # if we get no data back from the pipe, it means that the child process has ended. See
298
+ # if we get no data back from the pipe, it means that the child process has ended. See
300
299
  # mail.python.org/pipermail/python-dev/2004-August/046850.html
301
300
  self.close()
302
301
 
@@ -329,15 +328,12 @@ class ProcessInputDispatcherImpl(BaseProcessDispatcherImpl, ProcessInputDispatch
329
328
  return True
330
329
  return False
331
330
 
332
- def readable(self) -> bool:
333
- return False
334
-
335
331
  def flush(self) -> None:
336
332
  # other code depends on this raising EPIPE if the pipe is closed
337
333
  sent = os.write(self._fd, as_bytes(self._input_buffer))
338
334
  self._input_buffer = self._input_buffer[sent:]
339
335
 
340
- def handle_write_event(self) -> None:
336
+ def on_writable(self) -> None:
341
337
  if self._input_buffer:
342
338
  try:
343
339
  self.flush()
@@ -2,15 +2,20 @@
2
2
  import typing as ta
3
3
 
4
4
  from .configs import ProcessGroupConfig
5
+ from .dispatchers import Dispatchers
5
6
  from .events import EventCallbacks
6
7
  from .events import ProcessGroupAddedEvent
7
8
  from .events import ProcessGroupRemovedEvent
9
+ from .types import HasDispatchers
8
10
  from .types import Process
9
11
  from .types import ProcessGroup
10
12
  from .utils.collections import KeyedCollectionAccessors
11
13
 
12
14
 
13
- class ProcessGroupManager(KeyedCollectionAccessors[str, ProcessGroup]):
15
+ class ProcessGroupManager(
16
+ KeyedCollectionAccessors[str, ProcessGroup],
17
+ HasDispatchers,
18
+ ):
14
19
  def __init__(
15
20
  self,
16
21
  *,
@@ -34,6 +39,16 @@ class ProcessGroupManager(KeyedCollectionAccessors[str, ProcessGroup]):
34
39
 
35
40
  #
36
41
 
42
+ def get_dispatchers(self) -> Dispatchers:
43
+ return Dispatchers(
44
+ d
45
+ for g in self
46
+ for p in g
47
+ for d in p.get_dispatchers()
48
+ )
49
+
50
+ #
51
+
37
52
  def add(self, group: ProcessGroup) -> None:
38
53
  if (name := group.name) in self._by_name:
39
54
  raise KeyError(f'Process group already exists: {name}')
@@ -0,0 +1,130 @@
1
+ # ruff: noqa: U006 UP007
2
+ import json
3
+ import socket
4
+ import typing as ta
5
+
6
+ from omlish.lite.check import check_not_none
7
+ from omlish.lite.fdio.corohttp import CoroHttpServerConnectionFdIoHandler
8
+ from omlish.lite.fdio.handlers import SocketFdIoHandler
9
+ from omlish.lite.http.handlers import HttpHandler
10
+ from omlish.lite.http.handlers import HttpHandlerRequest
11
+ from omlish.lite.http.handlers import HttpHandlerResponse
12
+ from omlish.lite.json import JSON_PRETTY_KWARGS
13
+ from omlish.lite.socket import SocketAddress
14
+
15
+ from .dispatchers import Dispatchers
16
+ from .groups import ProcessGroupManager
17
+ from .types import HasDispatchers
18
+
19
+
20
+ ##
21
+
22
+
23
+ class SocketServerFdIoHandler(SocketFdIoHandler):
24
+ def __init__(
25
+ self,
26
+ addr: SocketAddress,
27
+ on_connect: ta.Callable[[socket.socket, SocketAddress], None],
28
+ ) -> None:
29
+ sock = socket.create_server(addr)
30
+ sock.setblocking(False)
31
+
32
+ super().__init__(addr, sock)
33
+
34
+ self._on_connect = on_connect
35
+
36
+ sock.listen(1)
37
+
38
+ def readable(self) -> bool:
39
+ return True
40
+
41
+ def on_readable(self) -> None:
42
+ cli_sock, cli_addr = check_not_none(self._sock).accept()
43
+ cli_sock.setblocking(False)
44
+
45
+ self._on_connect(cli_sock, cli_addr)
46
+
47
+
48
+ ##
49
+
50
+
51
+ class HttpServer(HasDispatchers):
52
+ class Address(ta.NamedTuple):
53
+ a: SocketAddress
54
+
55
+ class Handler(ta.NamedTuple):
56
+ h: HttpHandler
57
+
58
+ def __init__(
59
+ self,
60
+ handler: Handler,
61
+ addr: Address = Address(('localhost', 8000)),
62
+ ) -> None:
63
+ super().__init__()
64
+
65
+ self._handler = handler.h
66
+ self._addr = addr.a
67
+
68
+ self._server = SocketServerFdIoHandler(self._addr, self._on_connect)
69
+
70
+ self._conns: ta.List[CoroHttpServerConnectionFdIoHandler] = []
71
+
72
+ def get_dispatchers(self) -> Dispatchers:
73
+ l = []
74
+ for c in self._conns:
75
+ if not c.closed:
76
+ l.append(c)
77
+ self._conns = l
78
+ return Dispatchers([
79
+ self._server,
80
+ *l,
81
+ ])
82
+
83
+ def _on_connect(self, sock: socket.socket, addr: SocketAddress) -> None:
84
+ conn = CoroHttpServerConnectionFdIoHandler(
85
+ addr,
86
+ sock,
87
+ self._handler,
88
+ )
89
+
90
+ self._conns.append(conn)
91
+
92
+
93
+ ##
94
+
95
+
96
+ class SupervisorHttpHandler:
97
+ def __init__(
98
+ self,
99
+ *,
100
+ groups: ProcessGroupManager,
101
+ ) -> None:
102
+ super().__init__()
103
+
104
+ self._groups = groups
105
+
106
+ def handle(self, req: HttpHandlerRequest) -> HttpHandlerResponse:
107
+ dct = {
108
+ 'method': req.method,
109
+ 'path': req.path,
110
+ 'data': len(req.data or b''),
111
+ 'groups': {
112
+ g.name: {
113
+ 'processes': {
114
+ p.name: {
115
+ 'pid': p.pid,
116
+ }
117
+ for p in g
118
+ },
119
+ }
120
+ for g in self._groups
121
+ },
122
+ }
123
+
124
+ return HttpHandlerResponse(
125
+ 200,
126
+ data=json.dumps(dct, **JSON_PRETTY_KWARGS).encode('utf-8') + b'\n',
127
+ headers={
128
+ 'Content-Type': 'application/json',
129
+ },
130
+ )
@@ -1,6 +1,11 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
2
3
  import typing as ta
3
4
 
5
+ from omlish.lite.fdio.kqueue import KqueueFdIoPoller # noqa
6
+ from omlish.lite.fdio.pollers import FdIoPoller
7
+ from omlish.lite.fdio.pollers import PollFdIoPoller # noqa
8
+ from omlish.lite.fdio.pollers import SelectFdIoPoller
4
9
  from omlish.lite.inject import InjectorBindingOrBindings
5
10
  from omlish.lite.inject import InjectorBindings
6
11
  from omlish.lite.inject import inj
@@ -12,9 +17,10 @@ from .events import EventCallbacks
12
17
  from .groups import ProcessGroupManager
13
18
  from .groupsimpl import ProcessFactory
14
19
  from .groupsimpl import ProcessGroupImpl
20
+ from .http import HttpServer
21
+ from .http import SupervisorHttpHandler
22
+ from .io import HasDispatchersList
15
23
  from .io import IoManager
16
- from .poller import Poller
17
- from .poller import get_poller_impl
18
24
  from .process import PidHistory
19
25
  from .processimpl import ProcessImpl
20
26
  from .processimpl import ProcessSpawningFactory
@@ -31,12 +37,24 @@ from .spawningimpl import ProcessSpawningImpl
31
37
  from .supervisor import ProcessGroupFactory
32
38
  from .supervisor import Supervisor
33
39
  from .supervisor import SupervisorStateManagerImpl
40
+ from .types import HasDispatchers
34
41
  from .types import ServerEpoch
35
42
  from .types import SupervisorStateManager
36
43
  from .utils.signals import SignalReceiver
37
44
  from .utils.users import get_user
38
45
 
39
46
 
47
+ @dc.dataclass(frozen=True)
48
+ class _FdIoPollerDaemonizeListener(DaemonizeListener):
49
+ _poller: FdIoPoller
50
+
51
+ def before_daemonize(self) -> None:
52
+ self._poller.close()
53
+
54
+ def after_daemonize(self) -> None:
55
+ self._poller.reopen()
56
+
57
+
40
58
  def bind_server(
41
59
  config: ServerConfig,
42
60
  *,
@@ -46,22 +64,24 @@ def bind_server(
46
64
  lst: ta.List[InjectorBindingOrBindings] = [
47
65
  inj.bind(config),
48
66
 
67
+ inj.bind_array(DaemonizeListener),
49
68
  inj.bind_array_type(DaemonizeListener, DaemonizeListeners),
50
69
 
51
70
  inj.bind(SupervisorSetupImpl, singleton=True),
52
71
  inj.bind(SupervisorSetup, to_key=SupervisorSetupImpl),
53
72
 
54
- inj.bind(DaemonizeListener, array=True, to_key=Poller),
55
-
56
73
  inj.bind(EventCallbacks, singleton=True),
57
74
 
58
75
  inj.bind(SignalReceiver, singleton=True),
59
76
 
60
77
  inj.bind(IoManager, singleton=True),
78
+ inj.bind_array(HasDispatchers),
79
+ inj.bind_array_type(HasDispatchers, HasDispatchersList),
61
80
 
62
81
  inj.bind(SignalHandler, singleton=True),
63
82
 
64
83
  inj.bind(ProcessGroupManager, singleton=True),
84
+ inj.bind(HasDispatchers, array=True, to_key=ProcessGroupManager),
65
85
 
66
86
  inj.bind(Supervisor, singleton=True),
67
87
 
@@ -94,7 +114,26 @@ def bind_server(
94
114
 
95
115
  #
96
116
 
97
- lst.append(inj.bind(get_poller_impl(), key=Poller, singleton=True))
117
+ poller_impl = next(filter(None, [
118
+ KqueueFdIoPoller,
119
+ PollFdIoPoller,
120
+ SelectFdIoPoller,
121
+ ]))
122
+ lst.append(inj.bind(poller_impl, key=FdIoPoller, singleton=True))
123
+ inj.bind(_FdIoPollerDaemonizeListener, array=True, singleton=True)
124
+
125
+ #
126
+
127
+ def _provide_http_handler(s: SupervisorHttpHandler) -> HttpServer.Handler:
128
+ return HttpServer.Handler(s.handle)
129
+
130
+ lst.extend([
131
+ inj.bind(HttpServer, singleton=True, eager=True),
132
+ inj.bind(HasDispatchers, array=True, to_key=HttpServer),
133
+
134
+ inj.bind(SupervisorHttpHandler, singleton=True),
135
+ inj.bind(_provide_http_handler),
136
+ ])
98
137
 
99
138
  #
100
139
 
ominfra/supervisor/io.py CHANGED
@@ -1,58 +1,71 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import typing as ta
3
+
4
+ from omlish.lite.fdio.pollers import FdIoPoller
2
5
  from omlish.lite.logs import log
3
6
 
4
7
  from .dispatchers import Dispatchers
5
- from .groups import ProcessGroupManager
6
- from .poller import Poller
7
8
  from .types import ExitNow
9
+ from .types import HasDispatchers
10
+ from .utils.ostypes import Fd
8
11
 
9
12
 
10
13
  ##
11
14
 
12
15
 
13
- class IoManager:
16
+ HasDispatchersList = ta.NewType('HasDispatchersList', ta.Sequence[HasDispatchers])
17
+
18
+
19
+ class IoManager(HasDispatchers):
14
20
  def __init__(
15
21
  self,
16
22
  *,
17
- poller: Poller,
18
- process_groups: ProcessGroupManager,
23
+ poller: FdIoPoller,
24
+ has_dispatchers_list: HasDispatchersList,
19
25
  ) -> None:
20
26
  super().__init__()
21
27
 
22
28
  self._poller = poller
23
- self._process_groups = process_groups
29
+ self._has_dispatchers_list = has_dispatchers_list
24
30
 
25
31
  def get_dispatchers(self) -> Dispatchers:
26
32
  return Dispatchers(
27
33
  d
28
- for p in self._process_groups.all_processes()
29
- for d in p.get_dispatchers()
34
+ for hd in self._has_dispatchers_list
35
+ for d in hd.get_dispatchers()
30
36
  )
31
37
 
32
38
  def poll(self) -> None:
33
39
  dispatchers = self.get_dispatchers()
34
40
 
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)
41
+ self._poller.update(
42
+ {fd for fd, d in dispatchers.items() if d.readable()},
43
+ {fd for fd, d in dispatchers.items() if d.writable()},
44
+ )
40
45
 
41
46
  timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
42
- r, w = self._poller.poll(timeout)
47
+ log.info(f'Polling: {timeout=}') # noqa
48
+ polled = self._poller.poll(timeout)
49
+ log.info(f'Polled: {polled=}') # noqa
50
+ if polled.msg is not None:
51
+ log.error(polled.msg)
52
+ if polled.exc is not None:
53
+ log.error('Poll exception: %r', polled.exc)
43
54
 
44
- for fd in r:
55
+ for r in polled.r:
56
+ fd = Fd(r)
45
57
  if fd in dispatchers:
58
+ dispatcher = dispatchers[fd]
46
59
  try:
47
- dispatcher = dispatchers[fd]
48
60
  log.debug('read event caused by %r', dispatcher)
49
- dispatcher.handle_read_event()
61
+ dispatcher.on_readable()
50
62
  if not dispatcher.readable():
51
63
  self._poller.unregister_readable(fd)
52
64
  except ExitNow:
53
65
  raise
54
- except Exception: # noqa
55
- dispatchers[fd].handle_error()
66
+ except Exception as exc: # noqa
67
+ log.exception('Error in dispatcher: %r', dispatcher)
68
+ dispatcher.on_error(exc)
56
69
  else:
57
70
  # if the fd is not in combined map, we should unregister it. otherwise, it will be polled every
58
71
  # time, which may cause 100% cpu usage
@@ -62,18 +75,20 @@ class IoManager:
62
75
  except Exception: # noqa
63
76
  pass
64
77
 
65
- for fd in w:
78
+ for w in polled.w:
79
+ fd = Fd(w)
66
80
  if fd in dispatchers:
81
+ dispatcher = dispatchers[fd]
67
82
  try:
68
- dispatcher = dispatchers[fd]
69
83
  log.debug('write event caused by %r', dispatcher)
70
- dispatcher.handle_write_event()
84
+ dispatcher.on_writable()
71
85
  if not dispatcher.writable():
72
86
  self._poller.unregister_writable(fd)
73
87
  except ExitNow:
74
88
  raise
75
- except Exception: # noqa
76
- dispatchers[fd].handle_error()
89
+ except Exception as exc: # noqa
90
+ log.exception('Error in dispatcher: %r', dispatcher)
91
+ dispatcher.on_error(exc)
77
92
  else:
78
93
  log.debug('unexpected write event from fd %r', fd)
79
94
  try:
@@ -29,8 +29,8 @@ class ProcessPipes:
29
29
 
30
30
  def make_process_pipes(stderr=True) -> ProcessPipes:
31
31
  """
32
- Create pipes for parent to child stdin/stdout/stderr communications. Open fd in non-blocking mode so we can
33
- read them in the mainloop without blocking. If stderr is False, don't create a pipe for stderr.
32
+ Create pipes for parent to child stdin/stdout/stderr communications. Open fd in non-blocking mode so we can read
33
+ them in the mainloop without blocking. If stderr is False, don't create a pipe for stderr.
34
34
  """
35
35
 
36
36
  pipes: ta.Dict[str, ta.Optional[Fd]] = {
@@ -7,9 +7,8 @@ import typing as ta
7
7
 
8
8
  def drop_privileges(user: ta.Union[int, str, None]) -> ta.Optional[str]:
9
9
  """
10
- Drop privileges to become the specified user, which may be a username or uid. Called for supervisord startup
11
- and when spawning subprocesses. Returns None on success or a string error message if privileges could not be
12
- dropped.
10
+ Drop privileges to become the specified user, which may be a username or uid. Called for supervisord startup and
11
+ when spawning subprocesses. Returns None on success or a string error message if privileges could not be dropped.
13
12
  """
14
13
 
15
14
  if user is None:
@@ -33,9 +32,8 @@ def drop_privileges(user: ta.Union[int, str, None]) -> ta.Optional[str]:
33
32
  current_uid = os.getuid()
34
33
 
35
34
  if current_uid == uid:
36
- # do nothing and return successfully if the uid is already the current one. this allows a supervisord
37
- # running as an unprivileged user "foo" to start a process where the config has "user=foo" (same user) in
38
- # it.
35
+ # do nothing and return successfully if the uid is already the current one. this allows a supervisord running as
36
+ # an unprivileged user "foo" to start a process where the config has "user=foo" (same user) in it.
39
37
  return None
40
38
 
41
39
  if current_uid != 0: