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.
Files changed (44) hide show
  1. ominfra/clouds/aws/auth.py +1 -1
  2. ominfra/deploy/_executor.py +1 -1
  3. ominfra/deploy/poly/_main.py +1 -1
  4. ominfra/pyremote/_runcommands.py +1 -1
  5. ominfra/scripts/journald2aws.py +2 -2
  6. ominfra/scripts/supervisor.py +4736 -4166
  7. ominfra/supervisor/configs.py +34 -11
  8. ominfra/supervisor/context.py +7 -345
  9. ominfra/supervisor/dispatchers.py +21 -324
  10. ominfra/supervisor/dispatchersimpl.py +343 -0
  11. ominfra/supervisor/groups.py +33 -111
  12. ominfra/supervisor/groupsimpl.py +86 -0
  13. ominfra/supervisor/inject.py +45 -20
  14. ominfra/supervisor/main.py +3 -3
  15. ominfra/supervisor/pipes.py +85 -0
  16. ominfra/supervisor/poller.py +42 -38
  17. ominfra/supervisor/privileges.py +65 -0
  18. ominfra/supervisor/process.py +6 -742
  19. ominfra/supervisor/processimpl.py +516 -0
  20. ominfra/supervisor/setup.py +38 -0
  21. ominfra/supervisor/setupimpl.py +262 -0
  22. ominfra/supervisor/spawning.py +32 -0
  23. ominfra/supervisor/spawningimpl.py +350 -0
  24. ominfra/supervisor/supervisor.py +67 -84
  25. ominfra/supervisor/types.py +101 -47
  26. ominfra/supervisor/utils/__init__.py +0 -0
  27. ominfra/supervisor/utils/collections.py +52 -0
  28. ominfra/supervisor/utils/diag.py +31 -0
  29. ominfra/supervisor/utils/fds.py +46 -0
  30. ominfra/supervisor/utils/fs.py +47 -0
  31. ominfra/supervisor/utils/os.py +45 -0
  32. ominfra/supervisor/utils/ostypes.py +9 -0
  33. ominfra/supervisor/utils/signals.py +60 -0
  34. ominfra/supervisor/utils/strings.py +105 -0
  35. ominfra/supervisor/utils/users.py +67 -0
  36. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/METADATA +3 -3
  37. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/RECORD +41 -25
  38. ominfra/supervisor/datatypes.py +0 -175
  39. ominfra/supervisor/signals.py +0 -52
  40. ominfra/supervisor/utils.py +0 -206
  41. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/LICENSE +0 -0
  42. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/WHEEL +0 -0
  43. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/entry_points.txt +0 -0
  44. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/top_level.txt +0 -0
@@ -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 .types import ServerContext
16
-
17
-
18
- ##
10
+ from .utils.collections import KeyedCollectionAccessors
19
11
 
20
12
 
21
- ProcessFactory = ta.NewType('ProcessFactory', Func[Process]) # (config: ProcessConfig, group: ProcessGroup)
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
- def get(self, name: str) -> ta.Optional[ProcessGroup]:
124
- return self._by_name.get(name)
25
+ @property
26
+ def _by_key(self) -> ta.Mapping[str, ProcessGroup]:
27
+ return self._by_name
125
28
 
126
- def __getitem__(self, name: str) -> ProcessGroup:
127
- return self._by_name[name]
29
+ #
128
30
 
129
- def __len__(self) -> int:
130
- return len(self._by_name)
31
+ def all_processes(self) -> ta.Iterator[Process]:
32
+ for g in self:
33
+ yield from g
131
34
 
132
- def __iter__(self) -> ta.Iterator[ProcessGroup]:
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
@@ -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 .context import ServerEpoch
11
- from .dispatchers import InputDispatcherImpl
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 ProcessFactory
15
- from .groups import ProcessGroupImpl
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 InheritedFds
19
- from .process import InputDispatcherFactory
20
- from .process import OutputDispatcherFactory
21
- from .process import ProcessImpl
22
- from .signals import SignalReceiver
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.bind(get_poller_impl(), key=Poller, singleton=True),
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(ProcessGroups, singleton=True),
63
+ inj.bind(ProcessGroupManager, singleton=True),
53
64
  inj.bind(Supervisor, singleton=True),
54
65
 
55
- inj.bind_factory(ProcessGroupFactory, ProcessGroupImpl),
56
- inj.bind_factory(ProcessFactory, ProcessImpl),
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(OutputDispatcherFactory, OutputDispatcherImpl),
59
- inj.bind_factory(InputDispatcherFactory, InputDispatcherImpl),
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)
@@ -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 .process import InheritedFds
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 ExitNow
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)
@@ -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
- class Poller(abc.ABC):
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: int) -> None:
19
+ def register_readable(self, fd: Fd) -> None:
17
20
  raise NotImplementedError
18
21
 
19
22
  @abc.abstractmethod
20
- def register_writable(self, fd: int) -> None:
23
+ def register_writable(self, fd: Fd) -> None:
21
24
  raise NotImplementedError
22
25
 
23
26
  @abc.abstractmethod
24
- def unregister_readable(self, fd: int) -> None:
27
+ def unregister_readable(self, fd: Fd) -> None:
25
28
  raise NotImplementedError
26
29
 
27
30
  @abc.abstractmethod
28
- def unregister_writable(self, fd: int) -> None:
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[int], ta.List[int]]:
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[int] = set()
50
- self._writable: ta.Set[int] = set()
52
+ self._readable: ta.Set[Fd] = set()
53
+ self._writable: ta.Set[Fd] = set()
51
54
 
52
- def register_readable(self, fd: int) -> None:
55
+ def register_readable(self, fd: Fd) -> None:
53
56
  self._readable.add(fd)
54
57
 
55
- def register_writable(self, fd: int) -> None:
58
+ def register_writable(self, fd: Fd) -> None:
56
59
  self._writable.add(fd)
57
60
 
58
- def unregister_readable(self, fd: int) -> None:
61
+ def unregister_readable(self, fd: Fd) -> None:
59
62
  self._readable.discard(fd)
60
63
 
61
- def unregister_writable(self, fd: int) -> None:
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[int], ta.List[int]]:
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 err:
76
- if err.args[0] == errno.EINTR:
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 err.args[0] == errno.EBADF:
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[int] = set()
96
- self._writable: set[int] = set()
98
+ self._readable: set[Fd] = set()
99
+ self._writable: set[Fd] = set()
97
100
 
98
- def register_readable(self, fd: int) -> None:
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: int) -> None:
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: int) -> None:
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: int) -> None:
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[int], ta.List[int]]:
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[int, int]]:
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 err:
134
- if err.args[0] == errno.EINTR:
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: int, eventmask: int) -> bool:
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[int] = set()
159
- self._writable: set[int] = set()
161
+ self._readable: set[Fd] = set()
162
+ self._writable: set[Fd] = set()
160
163
 
161
- def register_readable(self, fd: int) -> None:
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: int) -> None:
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: int) -> None:
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: int) -> None:
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: int, kevent: 'select.kevent') -> None:
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[int], ta.List[int]]:
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 KqueuePoller is not None
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'):