ominfra 0.0.0.dev126__py3-none-any.whl → 0.0.0.dev128__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/clouds/aws/auth.py +1 -1
- ominfra/deploy/_executor.py +1 -1
- ominfra/deploy/poly/_main.py +1 -1
- ominfra/pyremote/_runcommands.py +1 -1
- ominfra/scripts/journald2aws.py +2 -2
- ominfra/scripts/supervisor.py +4736 -4166
- ominfra/supervisor/configs.py +34 -11
- ominfra/supervisor/context.py +7 -345
- ominfra/supervisor/dispatchers.py +21 -324
- ominfra/supervisor/dispatchersimpl.py +343 -0
- ominfra/supervisor/groups.py +33 -111
- ominfra/supervisor/groupsimpl.py +86 -0
- ominfra/supervisor/inject.py +45 -20
- ominfra/supervisor/main.py +3 -3
- ominfra/supervisor/pipes.py +85 -0
- ominfra/supervisor/poller.py +42 -38
- ominfra/supervisor/privileges.py +65 -0
- ominfra/supervisor/process.py +6 -742
- ominfra/supervisor/processimpl.py +516 -0
- ominfra/supervisor/setup.py +38 -0
- ominfra/supervisor/setupimpl.py +262 -0
- ominfra/supervisor/spawning.py +32 -0
- ominfra/supervisor/spawningimpl.py +350 -0
- ominfra/supervisor/supervisor.py +67 -84
- ominfra/supervisor/types.py +101 -47
- ominfra/supervisor/utils/__init__.py +0 -0
- ominfra/supervisor/utils/collections.py +52 -0
- ominfra/supervisor/utils/diag.py +31 -0
- ominfra/supervisor/utils/fds.py +46 -0
- ominfra/supervisor/utils/fs.py +47 -0
- ominfra/supervisor/utils/os.py +45 -0
- ominfra/supervisor/utils/ostypes.py +9 -0
- ominfra/supervisor/utils/signals.py +60 -0
- ominfra/supervisor/utils/strings.py +105 -0
- ominfra/supervisor/utils/users.py +67 -0
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/RECORD +41 -25
- ominfra/supervisor/datatypes.py +0 -175
- ominfra/supervisor/signals.py +0 -52
- ominfra/supervisor/utils.py +0 -206
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/top_level.txt +0 -0
ominfra/supervisor/groups.py
CHANGED
@@ -1,114 +1,16 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import typing as ta
|
3
3
|
|
4
|
-
from omlish.lite.check import check_isinstance
|
5
|
-
from omlish.lite.typing import Func
|
6
|
-
|
7
4
|
from .configs import ProcessGroupConfig
|
8
5
|
from .events import EventCallbacks
|
9
6
|
from .events import ProcessGroupAddedEvent
|
10
7
|
from .events import ProcessGroupRemovedEvent
|
11
|
-
from .states import ProcessState
|
12
|
-
from .types import Dispatcher
|
13
8
|
from .types import Process
|
14
9
|
from .types import ProcessGroup
|
15
|
-
from .
|
16
|
-
|
17
|
-
|
18
|
-
##
|
10
|
+
from .utils.collections import KeyedCollectionAccessors
|
19
11
|
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
class ProcessGroupImpl(ProcessGroup):
|
25
|
-
def __init__(
|
26
|
-
self,
|
27
|
-
config: ProcessGroupConfig,
|
28
|
-
context: ServerContext,
|
29
|
-
*,
|
30
|
-
process_factory: ProcessFactory,
|
31
|
-
):
|
32
|
-
super().__init__()
|
33
|
-
|
34
|
-
self._config = config
|
35
|
-
self._context = context
|
36
|
-
self._process_factory = process_factory
|
37
|
-
|
38
|
-
self._processes = {}
|
39
|
-
for pconfig in self._config.processes or []:
|
40
|
-
process = check_isinstance(self._process_factory(pconfig, self), Process)
|
41
|
-
self._processes[pconfig.name] = process
|
42
|
-
|
43
|
-
@property
|
44
|
-
def config(self) -> ProcessGroupConfig:
|
45
|
-
return self._config
|
46
|
-
|
47
|
-
@property
|
48
|
-
def name(self) -> str:
|
49
|
-
return self._config.name
|
50
|
-
|
51
|
-
@property
|
52
|
-
def context(self) -> ServerContext:
|
53
|
-
return self._context
|
54
|
-
|
55
|
-
def __repr__(self):
|
56
|
-
# repr can't return anything other than a native string, but the name might be unicode - a problem on Python 2.
|
57
|
-
name = self._config.name
|
58
|
-
return f'<{self.__class__.__name__} instance at {id(self)} named {name}>'
|
59
|
-
|
60
|
-
def remove_logs(self) -> None:
|
61
|
-
for process in self._processes.values():
|
62
|
-
process.remove_logs()
|
63
|
-
|
64
|
-
def reopen_logs(self) -> None:
|
65
|
-
for process in self._processes.values():
|
66
|
-
process.reopen_logs()
|
67
|
-
|
68
|
-
def stop_all(self) -> None:
|
69
|
-
processes = list(self._processes.values())
|
70
|
-
processes.sort()
|
71
|
-
processes.reverse() # stop in desc priority order
|
72
|
-
|
73
|
-
for proc in processes:
|
74
|
-
state = proc.get_state()
|
75
|
-
if state == ProcessState.RUNNING:
|
76
|
-
# RUNNING -> STOPPING
|
77
|
-
proc.stop()
|
78
|
-
|
79
|
-
elif state == ProcessState.STARTING:
|
80
|
-
# STARTING -> STOPPING
|
81
|
-
proc.stop()
|
82
|
-
|
83
|
-
elif state == ProcessState.BACKOFF:
|
84
|
-
# BACKOFF -> FATAL
|
85
|
-
proc.give_up()
|
86
|
-
|
87
|
-
def get_unstopped_processes(self) -> ta.List[Process]:
|
88
|
-
return [x for x in self._processes.values() if not x.get_state().stopped]
|
89
|
-
|
90
|
-
def get_dispatchers(self) -> ta.Dict[int, Dispatcher]:
|
91
|
-
dispatchers: dict = {}
|
92
|
-
for process in self._processes.values():
|
93
|
-
dispatchers.update(process.get_dispatchers())
|
94
|
-
return dispatchers
|
95
|
-
|
96
|
-
def before_remove(self) -> None:
|
97
|
-
pass
|
98
|
-
|
99
|
-
def transition(self) -> None:
|
100
|
-
for proc in self._processes.values():
|
101
|
-
proc.transition()
|
102
|
-
|
103
|
-
def after_setuid(self) -> None:
|
104
|
-
for proc in self._processes.values():
|
105
|
-
proc.create_auto_child_logs()
|
106
|
-
|
107
|
-
|
108
|
-
##
|
109
|
-
|
110
|
-
|
111
|
-
class ProcessGroups:
|
13
|
+
class ProcessGroupManager(KeyedCollectionAccessors[str, ProcessGroup]):
|
112
14
|
def __init__(
|
113
15
|
self,
|
114
16
|
*,
|
@@ -120,20 +22,17 @@ class ProcessGroups:
|
|
120
22
|
|
121
23
|
self._by_name: ta.Dict[str, ProcessGroup] = {}
|
122
24
|
|
123
|
-
|
124
|
-
|
25
|
+
@property
|
26
|
+
def _by_key(self) -> ta.Mapping[str, ProcessGroup]:
|
27
|
+
return self._by_name
|
125
28
|
|
126
|
-
|
127
|
-
return self._by_name[name]
|
29
|
+
#
|
128
30
|
|
129
|
-
def
|
130
|
-
|
31
|
+
def all_processes(self) -> ta.Iterator[Process]:
|
32
|
+
for g in self:
|
33
|
+
yield from g
|
131
34
|
|
132
|
-
|
133
|
-
return iter(self._by_name.values())
|
134
|
-
|
135
|
-
def all(self) -> ta.Mapping[str, ProcessGroup]:
|
136
|
-
return self._by_name
|
35
|
+
#
|
137
36
|
|
138
37
|
def add(self, group: ProcessGroup) -> None:
|
139
38
|
if (name := group.name) in self._by_name:
|
@@ -155,3 +54,26 @@ class ProcessGroups:
|
|
155
54
|
def clear(self) -> None:
|
156
55
|
# FIXME: events?
|
157
56
|
self._by_name.clear()
|
57
|
+
|
58
|
+
#
|
59
|
+
|
60
|
+
class Diff(ta.NamedTuple):
|
61
|
+
added: ta.List[ProcessGroupConfig]
|
62
|
+
changed: ta.List[ProcessGroupConfig]
|
63
|
+
removed: ta.List[ProcessGroupConfig]
|
64
|
+
|
65
|
+
def diff(self, new: ta.Sequence[ProcessGroupConfig]) -> Diff:
|
66
|
+
cur = [group.config for group in self]
|
67
|
+
|
68
|
+
cur_by_name = {cfg.name: cfg for cfg in cur}
|
69
|
+
new_by_name = {cfg.name: cfg for cfg in new}
|
70
|
+
|
71
|
+
added = [cand for cand in new if cand.name not in cur_by_name]
|
72
|
+
removed = [cand for cand in cur if cand.name not in new_by_name]
|
73
|
+
changed = [cand for cand in new if cand != cur_by_name.get(cand.name, cand)]
|
74
|
+
|
75
|
+
return ProcessGroupManager.Diff(
|
76
|
+
added,
|
77
|
+
changed,
|
78
|
+
removed,
|
79
|
+
)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.check import check_isinstance
|
5
|
+
from omlish.lite.typing import Func2
|
6
|
+
|
7
|
+
from .configs import ProcessConfig
|
8
|
+
from .configs import ProcessGroupConfig
|
9
|
+
from .states import ProcessState
|
10
|
+
from .types import Process
|
11
|
+
from .types import ProcessGroup
|
12
|
+
|
13
|
+
|
14
|
+
class ProcessFactory(Func2[ProcessConfig, ProcessGroup, Process]):
|
15
|
+
pass
|
16
|
+
|
17
|
+
|
18
|
+
class ProcessGroupImpl(ProcessGroup):
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
config: ProcessGroupConfig,
|
22
|
+
*,
|
23
|
+
process_factory: ProcessFactory,
|
24
|
+
):
|
25
|
+
super().__init__()
|
26
|
+
|
27
|
+
self._config = config
|
28
|
+
self._process_factory = process_factory
|
29
|
+
|
30
|
+
by_name: ta.Dict[str, Process] = {}
|
31
|
+
for pconfig in self._config.processes or []:
|
32
|
+
p = check_isinstance(self._process_factory(pconfig, self), Process)
|
33
|
+
if p.name in by_name:
|
34
|
+
raise KeyError(f'name {p.name} of process {p} already registered by {by_name[p.name]}')
|
35
|
+
by_name[pconfig.name] = p
|
36
|
+
self._by_name = by_name
|
37
|
+
|
38
|
+
@property
|
39
|
+
def _by_key(self) -> ta.Mapping[str, Process]:
|
40
|
+
return self._by_name
|
41
|
+
|
42
|
+
#
|
43
|
+
|
44
|
+
def __repr__(self) -> str:
|
45
|
+
return f'<{self.__class__.__name__} instance at {id(self)} named {self._config.name}>'
|
46
|
+
|
47
|
+
#
|
48
|
+
|
49
|
+
@property
|
50
|
+
def name(self) -> str:
|
51
|
+
return self._config.name
|
52
|
+
|
53
|
+
@property
|
54
|
+
def config(self) -> ProcessGroupConfig:
|
55
|
+
return self._config
|
56
|
+
|
57
|
+
@property
|
58
|
+
def by_name(self) -> ta.Mapping[str, Process]:
|
59
|
+
return self._by_name
|
60
|
+
|
61
|
+
#
|
62
|
+
|
63
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
64
|
+
return [x for x in self if not x.get_state().stopped]
|
65
|
+
|
66
|
+
def stop_all(self) -> None:
|
67
|
+
processes = list(self._by_name.values())
|
68
|
+
processes.sort()
|
69
|
+
processes.reverse() # stop in desc priority order
|
70
|
+
|
71
|
+
for proc in processes:
|
72
|
+
state = proc.get_state()
|
73
|
+
if state == ProcessState.RUNNING:
|
74
|
+
# RUNNING -> STOPPING
|
75
|
+
proc.stop()
|
76
|
+
|
77
|
+
elif state == ProcessState.STARTING:
|
78
|
+
# STARTING -> STOPPING
|
79
|
+
proc.stop()
|
80
|
+
|
81
|
+
elif state == ProcessState.BACKOFF:
|
82
|
+
# BACKOFF -> FATAL
|
83
|
+
proc.give_up()
|
84
|
+
|
85
|
+
def before_remove(self) -> None:
|
86
|
+
pass
|
ominfra/supervisor/inject.py
CHANGED
@@ -7,27 +7,33 @@ from omlish.lite.inject import inj
|
|
7
7
|
|
8
8
|
from .configs import ServerConfig
|
9
9
|
from .context import ServerContextImpl
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .dispatchers import OutputDispatcherImpl
|
10
|
+
from .dispatchersimpl import InputDispatcherImpl
|
11
|
+
from .dispatchersimpl import OutputDispatcherImpl
|
13
12
|
from .events import EventCallbacks
|
14
|
-
from .groups import
|
15
|
-
from .
|
13
|
+
from .groups import ProcessGroupManager
|
14
|
+
from .groupsimpl import ProcessFactory
|
15
|
+
from .groupsimpl import ProcessGroupImpl
|
16
16
|
from .poller import Poller
|
17
17
|
from .poller import get_poller_impl
|
18
|
-
from .process import
|
19
|
-
from .
|
20
|
-
from .
|
21
|
-
from .
|
22
|
-
from .
|
18
|
+
from .process import PidHistory
|
19
|
+
from .processimpl import ProcessImpl
|
20
|
+
from .processimpl import ProcessSpawningFactory
|
21
|
+
from .setup import DaemonizeListener
|
22
|
+
from .setup import DaemonizeListeners
|
23
|
+
from .setup import SupervisorUser
|
24
|
+
from .setupimpl import SupervisorSetup
|
25
|
+
from .setupimpl import SupervisorSetupImpl
|
26
|
+
from .spawningimpl import InheritedFds
|
27
|
+
from .spawningimpl import InputDispatcherFactory
|
28
|
+
from .spawningimpl import OutputDispatcherFactory
|
29
|
+
from .spawningimpl import ProcessSpawningImpl
|
23
30
|
from .supervisor import ProcessGroupFactory
|
24
|
-
from .supervisor import ProcessGroups
|
25
31
|
from .supervisor import SignalHandler
|
26
32
|
from .supervisor import Supervisor
|
27
33
|
from .types import ServerContext
|
28
|
-
|
29
|
-
|
30
|
-
|
34
|
+
from .types import ServerEpoch
|
35
|
+
from .utils.signals import SignalReceiver
|
36
|
+
from .utils.users import get_user
|
31
37
|
|
32
38
|
|
33
39
|
def bind_server(
|
@@ -39,7 +45,12 @@ def bind_server(
|
|
39
45
|
lst: ta.List[InjectorBindingOrBindings] = [
|
40
46
|
inj.bind(config),
|
41
47
|
|
42
|
-
inj.
|
48
|
+
inj.bind_array_type(DaemonizeListener, DaemonizeListeners),
|
49
|
+
|
50
|
+
inj.bind(SupervisorSetupImpl, singleton=True),
|
51
|
+
inj.bind(SupervisorSetup, to_key=SupervisorSetupImpl),
|
52
|
+
|
53
|
+
inj.bind(DaemonizeListener, array=True, to_key=Poller),
|
43
54
|
|
44
55
|
inj.bind(ServerContextImpl, singleton=True),
|
45
56
|
inj.bind(ServerContext, to_key=ServerContextImpl),
|
@@ -49,14 +60,18 @@ def bind_server(
|
|
49
60
|
inj.bind(SignalReceiver, singleton=True),
|
50
61
|
|
51
62
|
inj.bind(SignalHandler, singleton=True),
|
52
|
-
inj.bind(
|
63
|
+
inj.bind(ProcessGroupManager, singleton=True),
|
53
64
|
inj.bind(Supervisor, singleton=True),
|
54
65
|
|
55
|
-
inj.
|
56
|
-
|
66
|
+
inj.bind(PidHistory()),
|
67
|
+
|
68
|
+
inj.bind_factory(ProcessGroupImpl, ProcessGroupFactory),
|
69
|
+
inj.bind_factory(ProcessImpl, ProcessFactory),
|
70
|
+
|
71
|
+
inj.bind_factory(ProcessSpawningImpl, ProcessSpawningFactory),
|
57
72
|
|
58
|
-
inj.bind_factory(
|
59
|
-
inj.bind_factory(
|
73
|
+
inj.bind_factory(OutputDispatcherImpl, OutputDispatcherFactory),
|
74
|
+
inj.bind_factory(InputDispatcherImpl, InputDispatcherFactory),
|
60
75
|
]
|
61
76
|
|
62
77
|
#
|
@@ -68,4 +83,14 @@ def bind_server(
|
|
68
83
|
|
69
84
|
#
|
70
85
|
|
86
|
+
if config.user is not None:
|
87
|
+
user = get_user(config.user)
|
88
|
+
lst.append(inj.bind(user, key=SupervisorUser))
|
89
|
+
|
90
|
+
#
|
91
|
+
|
92
|
+
lst.append(inj.bind(get_poller_impl(), key=Poller, singleton=True))
|
93
|
+
|
94
|
+
#
|
95
|
+
|
71
96
|
return inj.as_bindings(*lst)
|
ominfra/supervisor/main.py
CHANGED
@@ -44,11 +44,11 @@ from .configs import prepare_server_config
|
|
44
44
|
from .context import ServerContextImpl
|
45
45
|
from .context import ServerEpoch
|
46
46
|
from .inject import bind_server
|
47
|
-
from .
|
47
|
+
from .spawningimpl import InheritedFds
|
48
48
|
from .states import SupervisorState
|
49
|
+
from .supervisor import ExitNow
|
49
50
|
from .supervisor import Supervisor
|
50
|
-
from .utils import
|
51
|
-
from .utils import get_open_fds
|
51
|
+
from .utils.fds import get_open_fds
|
52
52
|
|
53
53
|
|
54
54
|
##
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
3
|
+
import fcntl
|
4
|
+
import os
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from .utils.fds import close_fd
|
8
|
+
from .utils.fds import make_pipe
|
9
|
+
from .utils.ostypes import Fd
|
10
|
+
|
11
|
+
|
12
|
+
@dc.dataclass(frozen=True)
|
13
|
+
class ProcessPipes:
|
14
|
+
child_stdin: ta.Optional[Fd] = None
|
15
|
+
stdin: ta.Optional[Fd] = None
|
16
|
+
|
17
|
+
stdout: ta.Optional[Fd] = None
|
18
|
+
child_stdout: ta.Optional[Fd] = None
|
19
|
+
|
20
|
+
stderr: ta.Optional[Fd] = None
|
21
|
+
child_stderr: ta.Optional[Fd] = None
|
22
|
+
|
23
|
+
def child_fds(self) -> ta.List[Fd]:
|
24
|
+
return [fd for fd in [self.child_stdin, self.child_stdout, self.child_stderr] if fd is not None]
|
25
|
+
|
26
|
+
def parent_fds(self) -> ta.List[Fd]:
|
27
|
+
return [fd for fd in [self.stdin, self.stdout, self.stderr] if fd is not None]
|
28
|
+
|
29
|
+
|
30
|
+
def make_process_pipes(stderr=True) -> ProcessPipes:
|
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.
|
34
|
+
"""
|
35
|
+
|
36
|
+
pipes: ta.Dict[str, ta.Optional[Fd]] = {
|
37
|
+
'child_stdin': None,
|
38
|
+
'stdin': None,
|
39
|
+
|
40
|
+
'stdout': None,
|
41
|
+
'child_stdout': None,
|
42
|
+
|
43
|
+
'stderr': None,
|
44
|
+
'child_stderr': None,
|
45
|
+
}
|
46
|
+
|
47
|
+
try:
|
48
|
+
pipes['child_stdin'], pipes['stdin'] = make_pipe()
|
49
|
+
pipes['stdout'], pipes['child_stdout'] = make_pipe()
|
50
|
+
|
51
|
+
if stderr:
|
52
|
+
pipes['stderr'], pipes['child_stderr'] = make_pipe()
|
53
|
+
|
54
|
+
for fd in (
|
55
|
+
pipes['stdout'],
|
56
|
+
pipes['stderr'],
|
57
|
+
pipes['stdin'],
|
58
|
+
):
|
59
|
+
if fd is not None:
|
60
|
+
flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NDELAY
|
61
|
+
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
62
|
+
|
63
|
+
return ProcessPipes(**pipes)
|
64
|
+
|
65
|
+
except OSError:
|
66
|
+
for fd in pipes.values():
|
67
|
+
if fd is not None:
|
68
|
+
close_fd(fd)
|
69
|
+
|
70
|
+
raise
|
71
|
+
|
72
|
+
|
73
|
+
def close_pipes(pipes: ProcessPipes) -> None:
|
74
|
+
close_parent_pipes(pipes)
|
75
|
+
close_child_pipes(pipes)
|
76
|
+
|
77
|
+
|
78
|
+
def close_parent_pipes(pipes: ProcessPipes) -> None:
|
79
|
+
for fd in pipes.parent_fds():
|
80
|
+
close_fd(fd)
|
81
|
+
|
82
|
+
|
83
|
+
def close_child_pipes(pipes: ProcessPipes) -> None:
|
84
|
+
for fd in pipes.child_fds():
|
85
|
+
close_fd(fd)
|
ominfra/supervisor/poller.py
CHANGED
@@ -7,29 +7,32 @@ import typing as ta
|
|
7
7
|
|
8
8
|
from omlish.lite.logs import log
|
9
9
|
|
10
|
+
from .setup import DaemonizeListener
|
11
|
+
from .utils.ostypes import Fd
|
10
12
|
|
11
|
-
|
13
|
+
|
14
|
+
class Poller(DaemonizeListener, abc.ABC):
|
12
15
|
def __init__(self) -> None:
|
13
16
|
super().__init__()
|
14
17
|
|
15
18
|
@abc.abstractmethod
|
16
|
-
def register_readable(self, fd:
|
19
|
+
def register_readable(self, fd: Fd) -> None:
|
17
20
|
raise NotImplementedError
|
18
21
|
|
19
22
|
@abc.abstractmethod
|
20
|
-
def register_writable(self, fd:
|
23
|
+
def register_writable(self, fd: Fd) -> None:
|
21
24
|
raise NotImplementedError
|
22
25
|
|
23
26
|
@abc.abstractmethod
|
24
|
-
def unregister_readable(self, fd:
|
27
|
+
def unregister_readable(self, fd: Fd) -> None:
|
25
28
|
raise NotImplementedError
|
26
29
|
|
27
30
|
@abc.abstractmethod
|
28
|
-
def unregister_writable(self, fd:
|
31
|
+
def unregister_writable(self, fd: Fd) -> None:
|
29
32
|
raise NotImplementedError
|
30
33
|
|
31
34
|
@abc.abstractmethod
|
32
|
-
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[
|
35
|
+
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
|
33
36
|
raise NotImplementedError
|
34
37
|
|
35
38
|
def before_daemonize(self) -> None: # noqa
|
@@ -46,37 +49,37 @@ class SelectPoller(Poller):
|
|
46
49
|
def __init__(self) -> None:
|
47
50
|
super().__init__()
|
48
51
|
|
49
|
-
self._readable: ta.Set[
|
50
|
-
self._writable: ta.Set[
|
52
|
+
self._readable: ta.Set[Fd] = set()
|
53
|
+
self._writable: ta.Set[Fd] = set()
|
51
54
|
|
52
|
-
def register_readable(self, fd:
|
55
|
+
def register_readable(self, fd: Fd) -> None:
|
53
56
|
self._readable.add(fd)
|
54
57
|
|
55
|
-
def register_writable(self, fd:
|
58
|
+
def register_writable(self, fd: Fd) -> None:
|
56
59
|
self._writable.add(fd)
|
57
60
|
|
58
|
-
def unregister_readable(self, fd:
|
61
|
+
def unregister_readable(self, fd: Fd) -> None:
|
59
62
|
self._readable.discard(fd)
|
60
63
|
|
61
|
-
def unregister_writable(self, fd:
|
64
|
+
def unregister_writable(self, fd: Fd) -> None:
|
62
65
|
self._writable.discard(fd)
|
63
66
|
|
64
67
|
def unregister_all(self) -> None:
|
65
68
|
self._readable.clear()
|
66
69
|
self._writable.clear()
|
67
70
|
|
68
|
-
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[
|
71
|
+
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
|
69
72
|
try:
|
70
73
|
r, w, x = select.select(
|
71
74
|
self._readable,
|
72
75
|
self._writable,
|
73
76
|
[], timeout,
|
74
77
|
)
|
75
|
-
except OSError as
|
76
|
-
if
|
78
|
+
except OSError as exc:
|
79
|
+
if exc.args[0] == errno.EINTR:
|
77
80
|
log.debug('EINTR encountered in poll')
|
78
81
|
return [], []
|
79
|
-
if
|
82
|
+
if exc.args[0] == errno.EBADF:
|
80
83
|
log.debug('EBADF encountered in poll')
|
81
84
|
self.unregister_all()
|
82
85
|
return [], []
|
@@ -92,30 +95,30 @@ class PollPoller(Poller):
|
|
92
95
|
super().__init__()
|
93
96
|
|
94
97
|
self._poller = select.poll()
|
95
|
-
self._readable: set[
|
96
|
-
self._writable: set[
|
98
|
+
self._readable: set[Fd] = set()
|
99
|
+
self._writable: set[Fd] = set()
|
97
100
|
|
98
|
-
def register_readable(self, fd:
|
101
|
+
def register_readable(self, fd: Fd) -> None:
|
99
102
|
self._poller.register(fd, self._READ)
|
100
103
|
self._readable.add(fd)
|
101
104
|
|
102
|
-
def register_writable(self, fd:
|
105
|
+
def register_writable(self, fd: Fd) -> None:
|
103
106
|
self._poller.register(fd, self._WRITE)
|
104
107
|
self._writable.add(fd)
|
105
108
|
|
106
|
-
def unregister_readable(self, fd:
|
109
|
+
def unregister_readable(self, fd: Fd) -> None:
|
107
110
|
self._readable.discard(fd)
|
108
111
|
self._poller.unregister(fd)
|
109
112
|
if fd in self._writable:
|
110
113
|
self._poller.register(fd, self._WRITE)
|
111
114
|
|
112
|
-
def unregister_writable(self, fd:
|
115
|
+
def unregister_writable(self, fd: Fd) -> None:
|
113
116
|
self._writable.discard(fd)
|
114
117
|
self._poller.unregister(fd)
|
115
118
|
if fd in self._readable:
|
116
119
|
self._poller.register(fd, self._READ)
|
117
120
|
|
118
|
-
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[
|
121
|
+
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
|
119
122
|
fds = self._poll_fds(timeout) # type: ignore
|
120
123
|
readable, writable = [], []
|
121
124
|
for fd, eventmask in fds:
|
@@ -127,16 +130,16 @@ class PollPoller(Poller):
|
|
127
130
|
writable.append(fd)
|
128
131
|
return readable, writable
|
129
132
|
|
130
|
-
def _poll_fds(self, timeout: float) -> ta.List[ta.Tuple[
|
133
|
+
def _poll_fds(self, timeout: float) -> ta.List[ta.Tuple[Fd, Fd]]:
|
131
134
|
try:
|
132
|
-
return self._poller.poll(timeout * 1000)
|
133
|
-
except OSError as
|
134
|
-
if
|
135
|
+
return self._poller.poll(timeout * 1000) # type: ignore
|
136
|
+
except OSError as exc:
|
137
|
+
if exc.args[0] == errno.EINTR:
|
135
138
|
log.debug('EINTR encountered in poll')
|
136
139
|
return []
|
137
140
|
raise
|
138
141
|
|
139
|
-
def _ignore_invalid(self, fd:
|
142
|
+
def _ignore_invalid(self, fd: Fd, eventmask: int) -> bool:
|
140
143
|
if eventmask & select.POLLNVAL:
|
141
144
|
# POLLNVAL means `fd` value is invalid, not open. When a process quits it's `fd`s are closed so there is no
|
142
145
|
# more reason to keep this `fd` registered If the process restarts it's `fd`s are registered again.
|
@@ -155,30 +158,30 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
|
155
158
|
super().__init__()
|
156
159
|
|
157
160
|
self._kqueue: ta.Optional[ta.Any] = select.kqueue()
|
158
|
-
self._readable: set[
|
159
|
-
self._writable: set[
|
161
|
+
self._readable: set[Fd] = set()
|
162
|
+
self._writable: set[Fd] = set()
|
160
163
|
|
161
|
-
def register_readable(self, fd:
|
164
|
+
def register_readable(self, fd: Fd) -> None:
|
162
165
|
self._readable.add(fd)
|
163
166
|
kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
|
164
167
|
self._kqueue_control(fd, kevent)
|
165
168
|
|
166
|
-
def register_writable(self, fd:
|
169
|
+
def register_writable(self, fd: Fd) -> None:
|
167
170
|
self._writable.add(fd)
|
168
171
|
kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_ADD)
|
169
172
|
self._kqueue_control(fd, kevent)
|
170
173
|
|
171
|
-
def unregister_readable(self, fd:
|
174
|
+
def unregister_readable(self, fd: Fd) -> None:
|
172
175
|
kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_DELETE)
|
173
176
|
self._readable.discard(fd)
|
174
177
|
self._kqueue_control(fd, kevent)
|
175
178
|
|
176
|
-
def unregister_writable(self, fd:
|
179
|
+
def unregister_writable(self, fd: Fd) -> None:
|
177
180
|
kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_DELETE)
|
178
181
|
self._writable.discard(fd)
|
179
182
|
self._kqueue_control(fd, kevent)
|
180
183
|
|
181
|
-
def _kqueue_control(self, fd:
|
184
|
+
def _kqueue_control(self, fd: Fd, kevent: 'select.kevent') -> None:
|
182
185
|
try:
|
183
186
|
self._kqueue.control([kevent], 0) # type: ignore
|
184
187
|
except OSError as error:
|
@@ -187,7 +190,7 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
|
187
190
|
else:
|
188
191
|
raise
|
189
192
|
|
190
|
-
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[
|
193
|
+
def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
|
191
194
|
readable, writable = [], [] # type: ignore
|
192
195
|
|
193
196
|
try:
|
@@ -226,8 +229,9 @@ else:
|
|
226
229
|
|
227
230
|
def get_poller_impl() -> ta.Type[Poller]:
|
228
231
|
if (
|
229
|
-
sys.platform == 'darwin' or sys.platform.startswith('freebsd') and
|
230
|
-
hasattr(select, 'kqueue') and
|
232
|
+
(sys.platform == 'darwin' or sys.platform.startswith('freebsd')) and
|
233
|
+
hasattr(select, 'kqueue') and
|
234
|
+
KqueuePoller is not None
|
231
235
|
):
|
232
236
|
return KqueuePoller
|
233
237
|
elif hasattr(select, 'poll'):
|