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.
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'):