ominfra 0.0.0.dev126__py3-none-any.whl → 0.0.0.dev128__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.
- 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'):
|