ominfra 0.0.0.dev127__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 (36) hide show
  1. ominfra/scripts/supervisor.py +723 -731
  2. ominfra/supervisor/configs.py +34 -11
  3. ominfra/supervisor/context.py +5 -9
  4. ominfra/supervisor/dispatchers.py +4 -3
  5. ominfra/supervisor/dispatchersimpl.py +10 -9
  6. ominfra/supervisor/groups.py +1 -1
  7. ominfra/supervisor/inject.py +5 -5
  8. ominfra/supervisor/main.py +2 -2
  9. ominfra/supervisor/pipes.py +15 -13
  10. ominfra/supervisor/poller.py +36 -35
  11. ominfra/supervisor/{processes.py → process.py} +2 -1
  12. ominfra/supervisor/{processesimpl.py → processimpl.py} +35 -40
  13. ominfra/supervisor/setup.py +1 -1
  14. ominfra/supervisor/setupimpl.py +4 -3
  15. ominfra/supervisor/spawning.py +2 -1
  16. ominfra/supervisor/spawningimpl.py +15 -12
  17. ominfra/supervisor/supervisor.py +16 -8
  18. ominfra/supervisor/types.py +7 -9
  19. ominfra/supervisor/utils/__init__.py +0 -0
  20. ominfra/supervisor/utils/diag.py +31 -0
  21. ominfra/supervisor/utils/fds.py +46 -0
  22. ominfra/supervisor/utils/fs.py +47 -0
  23. ominfra/supervisor/utils/os.py +45 -0
  24. ominfra/supervisor/utils/ostypes.py +9 -0
  25. ominfra/supervisor/utils/strings.py +105 -0
  26. ominfra/supervisor/{users.py → utils/users.py} +11 -8
  27. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/METADATA +3 -3
  28. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/RECORD +34 -29
  29. ominfra/supervisor/datatypes.py +0 -113
  30. ominfra/supervisor/utils.py +0 -206
  31. /ominfra/supervisor/{collections.py → utils/collections.py} +0 -0
  32. /ominfra/supervisor/{signals.py → utils/signals.py} +0 -0
  33. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/LICENSE +0 -0
  34. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/WHEEL +0 -0
  35. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/entry_points.txt +0 -0
  36. {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev128.dist-info}/top_level.txt +0 -0
@@ -7,11 +7,21 @@ import typing as ta
7
7
 
8
8
  from ..configs import ConfigMapping
9
9
  from ..configs import build_config_named_children
10
- from .datatypes import byte_size
11
- from .datatypes import existing_directory
12
- from .datatypes import existing_dirpath
13
- from .datatypes import logging_level
14
- from .datatypes import octal_type
10
+ from .utils.fs import check_existing_dir
11
+ from .utils.fs import check_path_with_existing_dir
12
+ from .utils.strings import parse_bytes_size
13
+ from .utils.strings import parse_octal
14
+
15
+
16
+ ##
17
+
18
+
19
+ class RestartWhenExitUnexpected:
20
+ pass
21
+
22
+
23
+ class RestartUnconditionally:
24
+ pass
15
25
 
16
26
 
17
27
  ##
@@ -104,12 +114,12 @@ class ServerConfig:
104
114
  **kwargs: ta.Any,
105
115
  ) -> 'ServerConfig':
106
116
  return cls(
107
- umask=octal_type(umask),
108
- directory=existing_directory(directory) if directory is not None else None,
109
- logfile=existing_dirpath(logfile),
110
- logfile_maxbytes=byte_size(logfile_maxbytes),
111
- loglevel=logging_level(loglevel),
112
- pidfile=existing_dirpath(pidfile),
117
+ umask=parse_octal(umask),
118
+ directory=check_existing_dir(directory) if directory is not None else None,
119
+ logfile=check_path_with_existing_dir(logfile),
120
+ logfile_maxbytes=parse_bytes_size(logfile_maxbytes),
121
+ loglevel=parse_logging_level(loglevel),
122
+ pidfile=check_path_with_existing_dir(pidfile),
113
123
  child_logdir=child_logdir if child_logdir else tempfile.gettempdir(),
114
124
  **kwargs,
115
125
  )
@@ -129,3 +139,16 @@ def prepare_server_config(dct: ta.Mapping[str, ta.Any]) -> ta.Mapping[str, ta.An
129
139
  group_dcts = build_config_named_children(out.get('groups'))
130
140
  out['groups'] = [prepare_process_group_config(group_dct) for group_dct in group_dcts or []]
131
141
  return out
142
+
143
+
144
+ ##
145
+
146
+
147
+ def parse_logging_level(value: ta.Union[str, int]) -> int:
148
+ if isinstance(value, int):
149
+ return value
150
+ s = str(value).lower()
151
+ level = logging.getLevelNamesMapping().get(s.upper())
152
+ if level is None:
153
+ raise ValueError(f'bad logging level name {value!r}')
154
+ return level
@@ -8,10 +8,11 @@ from omlish.lite.logs import log
8
8
  from .configs import ServerConfig
9
9
  from .poller import Poller
10
10
  from .states import SupervisorState
11
- from .types import Process
12
11
  from .types import ServerContext
13
12
  from .types import ServerEpoch
14
- from .utils import mktempfile
13
+ from .utils.fs import mktempfile
14
+ from .utils.ostypes import Pid
15
+ from .utils.ostypes import Rc
15
16
 
16
17
 
17
18
  class ServerContextImpl(ServerContext):
@@ -28,7 +29,6 @@ class ServerContextImpl(ServerContext):
28
29
  self._poller = poller
29
30
  self._epoch = epoch
30
31
 
31
- self._pid_history: ta.Dict[int, Process] = {}
32
32
  self._state: SupervisorState = SupervisorState.RUNNING
33
33
 
34
34
  @property
@@ -50,13 +50,9 @@ class ServerContextImpl(ServerContext):
50
50
  def set_state(self, state: SupervisorState) -> None:
51
51
  self._state = state
52
52
 
53
- @property
54
- def pid_history(self) -> ta.Dict[int, Process]:
55
- return self._pid_history
56
-
57
53
  #
58
54
 
59
- def waitpid(self) -> ta.Tuple[ta.Optional[int], ta.Optional[int]]:
55
+ def waitpid(self) -> ta.Tuple[ta.Optional[Pid], ta.Optional[Rc]]:
60
56
  # Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
61
57
  # still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
62
58
  # waitpid is interrupted by SIGCHLD, as long as we call waitpid again (which happens every so often during the
@@ -72,7 +68,7 @@ class ServerContextImpl(ServerContext):
72
68
  if code == errno.EINTR:
73
69
  log.debug('EINTR during reap')
74
70
  pid, sts = None, None
75
- return pid, sts
71
+ return pid, sts # type: ignore
76
72
 
77
73
  def get_auto_child_log_name(self, name: str, identifier: str, channel: str) -> str:
78
74
  prefix = f'{name}-{channel}---{identifier}-'
@@ -1,11 +1,12 @@
1
1
  # ruff: noqa: UP006 UP007
2
- from .collections import KeyedCollection
3
2
  from .types import Dispatcher
4
3
  from .types import OutputDispatcher
4
+ from .utils.collections import KeyedCollection
5
+ from .utils.ostypes import Fd
5
6
 
6
7
 
7
- class Dispatchers(KeyedCollection[int, Dispatcher]):
8
- def _key(self, v: Dispatcher) -> int:
8
+ class Dispatchers(KeyedCollection[Fd, Dispatcher]):
9
+ def _key(self, v: Dispatcher) -> Fd:
9
10
  return v.fd
10
11
 
11
12
  #
@@ -16,11 +16,12 @@ from .types import Dispatcher
16
16
  from .types import InputDispatcher
17
17
  from .types import OutputDispatcher
18
18
  from .types import Process
19
- from .utils import as_bytes
20
- from .utils import compact_traceback
21
- from .utils import find_prefix_at_end
22
- from .utils import read_fd
23
- from .utils import strip_escapes
19
+ from .utils.diag import compact_traceback
20
+ from .utils.fds import read_fd
21
+ from .utils.ostypes import Fd
22
+ from .utils.strings import as_bytes
23
+ from .utils.strings import find_prefix_at_end
24
+ from .utils.strings import strip_escapes
24
25
 
25
26
 
26
27
  class BaseDispatcherImpl(Dispatcher, abc.ABC):
@@ -28,7 +29,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
28
29
  self,
29
30
  process: Process,
30
31
  channel: str,
31
- fd: int,
32
+ fd: Fd,
32
33
  *,
33
34
  event_callbacks: EventCallbacks,
34
35
  ) -> None:
@@ -57,7 +58,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
57
58
  return self._channel
58
59
 
59
60
  @property
60
- def fd(self) -> int:
61
+ def fd(self) -> Fd:
61
62
  return self._fd
62
63
 
63
64
  @property
@@ -91,7 +92,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
91
92
  self,
92
93
  process: Process,
93
94
  event_type: ta.Type[ProcessCommunicationEvent],
94
- fd: int,
95
+ fd: Fd,
95
96
  *,
96
97
  event_callbacks: EventCallbacks,
97
98
  ) -> None:
@@ -301,7 +302,7 @@ class InputDispatcherImpl(BaseDispatcherImpl, InputDispatcher):
301
302
  self,
302
303
  process: Process,
303
304
  channel: str,
304
- fd: int,
305
+ fd: Fd,
305
306
  *,
306
307
  event_callbacks: EventCallbacks,
307
308
  ) -> None:
@@ -1,13 +1,13 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import typing as ta
3
3
 
4
- from .collections import KeyedCollectionAccessors
5
4
  from .configs import ProcessGroupConfig
6
5
  from .events import EventCallbacks
7
6
  from .events import ProcessGroupAddedEvent
8
7
  from .events import ProcessGroupRemovedEvent
9
8
  from .types import Process
10
9
  from .types import ProcessGroup
10
+ from .utils.collections import KeyedCollectionAccessors
11
11
 
12
12
 
13
13
  class ProcessGroupManager(KeyedCollectionAccessors[str, ProcessGroup]):
@@ -15,15 +15,14 @@ from .groupsimpl import ProcessFactory
15
15
  from .groupsimpl import ProcessGroupImpl
16
16
  from .poller import Poller
17
17
  from .poller import get_poller_impl
18
- from .processes import PidHistory
19
- from .processesimpl import ProcessImpl
20
- from .processesimpl import ProcessSpawningFactory
18
+ from .process import PidHistory
19
+ from .processimpl import ProcessImpl
20
+ from .processimpl import ProcessSpawningFactory
21
21
  from .setup import DaemonizeListener
22
22
  from .setup import DaemonizeListeners
23
23
  from .setup import SupervisorUser
24
24
  from .setupimpl import SupervisorSetup
25
25
  from .setupimpl import SupervisorSetupImpl
26
- from .signals import SignalReceiver
27
26
  from .spawningimpl import InheritedFds
28
27
  from .spawningimpl import InputDispatcherFactory
29
28
  from .spawningimpl import OutputDispatcherFactory
@@ -33,7 +32,8 @@ from .supervisor import SignalHandler
33
32
  from .supervisor import Supervisor
34
33
  from .types import ServerContext
35
34
  from .types import ServerEpoch
36
- from .users import get_user
35
+ from .utils.signals import SignalReceiver
36
+ from .utils.users import get_user
37
37
 
38
38
 
39
39
  def bind_server(
@@ -46,9 +46,9 @@ from .context import ServerEpoch
46
46
  from .inject import bind_server
47
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
  ##
@@ -4,24 +4,26 @@ import fcntl
4
4
  import os
5
5
  import typing as ta
6
6
 
7
- from .utils import close_fd
7
+ from .utils.fds import close_fd
8
+ from .utils.fds import make_pipe
9
+ from .utils.ostypes import Fd
8
10
 
9
11
 
10
12
  @dc.dataclass(frozen=True)
11
13
  class ProcessPipes:
12
- child_stdin: ta.Optional[int] = None
13
- stdin: ta.Optional[int] = None
14
+ child_stdin: ta.Optional[Fd] = None
15
+ stdin: ta.Optional[Fd] = None
14
16
 
15
- stdout: ta.Optional[int] = None
16
- child_stdout: ta.Optional[int] = None
17
+ stdout: ta.Optional[Fd] = None
18
+ child_stdout: ta.Optional[Fd] = None
17
19
 
18
- stderr: ta.Optional[int] = None
19
- child_stderr: ta.Optional[int] = None
20
+ stderr: ta.Optional[Fd] = None
21
+ child_stderr: ta.Optional[Fd] = None
20
22
 
21
- def child_fds(self) -> ta.List[int]:
23
+ def child_fds(self) -> ta.List[Fd]:
22
24
  return [fd for fd in [self.child_stdin, self.child_stdout, self.child_stderr] if fd is not None]
23
25
 
24
- def parent_fds(self) -> ta.List[int]:
26
+ def parent_fds(self) -> ta.List[Fd]:
25
27
  return [fd for fd in [self.stdin, self.stdout, self.stderr] if fd is not None]
26
28
 
27
29
 
@@ -31,7 +33,7 @@ def make_process_pipes(stderr=True) -> ProcessPipes:
31
33
  read them in the mainloop without blocking. If stderr is False, don't create a pipe for stderr.
32
34
  """
33
35
 
34
- pipes: ta.Dict[str, ta.Optional[int]] = {
36
+ pipes: ta.Dict[str, ta.Optional[Fd]] = {
35
37
  'child_stdin': None,
36
38
  'stdin': None,
37
39
 
@@ -43,11 +45,11 @@ def make_process_pipes(stderr=True) -> ProcessPipes:
43
45
  }
44
46
 
45
47
  try:
46
- pipes['child_stdin'], pipes['stdin'] = os.pipe()
47
- pipes['stdout'], pipes['child_stdout'] = os.pipe()
48
+ pipes['child_stdin'], pipes['stdin'] = make_pipe()
49
+ pipes['stdout'], pipes['child_stdout'] = make_pipe()
48
50
 
49
51
  if stderr:
50
- pipes['stderr'], pipes['child_stderr'] = os.pipe()
52
+ pipes['stderr'], pipes['child_stderr'] = make_pipe()
51
53
 
52
54
  for fd in (
53
55
  pipes['stdout'],
@@ -8,6 +8,7 @@ import typing as ta
8
8
  from omlish.lite.logs import log
9
9
 
10
10
  from .setup import DaemonizeListener
11
+ from .utils.ostypes import Fd
11
12
 
12
13
 
13
14
  class Poller(DaemonizeListener, abc.ABC):
@@ -15,23 +16,23 @@ class Poller(DaemonizeListener, abc.ABC):
15
16
  super().__init__()
16
17
 
17
18
  @abc.abstractmethod
18
- def register_readable(self, fd: int) -> None:
19
+ def register_readable(self, fd: Fd) -> None:
19
20
  raise NotImplementedError
20
21
 
21
22
  @abc.abstractmethod
22
- def register_writable(self, fd: int) -> None:
23
+ def register_writable(self, fd: Fd) -> None:
23
24
  raise NotImplementedError
24
25
 
25
26
  @abc.abstractmethod
26
- def unregister_readable(self, fd: int) -> None:
27
+ def unregister_readable(self, fd: Fd) -> None:
27
28
  raise NotImplementedError
28
29
 
29
30
  @abc.abstractmethod
30
- def unregister_writable(self, fd: int) -> None:
31
+ def unregister_writable(self, fd: Fd) -> None:
31
32
  raise NotImplementedError
32
33
 
33
34
  @abc.abstractmethod
34
- 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]]:
35
36
  raise NotImplementedError
36
37
 
37
38
  def before_daemonize(self) -> None: # noqa
@@ -48,37 +49,37 @@ class SelectPoller(Poller):
48
49
  def __init__(self) -> None:
49
50
  super().__init__()
50
51
 
51
- self._readable: ta.Set[int] = set()
52
- self._writable: ta.Set[int] = set()
52
+ self._readable: ta.Set[Fd] = set()
53
+ self._writable: ta.Set[Fd] = set()
53
54
 
54
- def register_readable(self, fd: int) -> None:
55
+ def register_readable(self, fd: Fd) -> None:
55
56
  self._readable.add(fd)
56
57
 
57
- def register_writable(self, fd: int) -> None:
58
+ def register_writable(self, fd: Fd) -> None:
58
59
  self._writable.add(fd)
59
60
 
60
- def unregister_readable(self, fd: int) -> None:
61
+ def unregister_readable(self, fd: Fd) -> None:
61
62
  self._readable.discard(fd)
62
63
 
63
- def unregister_writable(self, fd: int) -> None:
64
+ def unregister_writable(self, fd: Fd) -> None:
64
65
  self._writable.discard(fd)
65
66
 
66
67
  def unregister_all(self) -> None:
67
68
  self._readable.clear()
68
69
  self._writable.clear()
69
70
 
70
- 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]]:
71
72
  try:
72
73
  r, w, x = select.select(
73
74
  self._readable,
74
75
  self._writable,
75
76
  [], timeout,
76
77
  )
77
- except OSError as err:
78
- if err.args[0] == errno.EINTR:
78
+ except OSError as exc:
79
+ if exc.args[0] == errno.EINTR:
79
80
  log.debug('EINTR encountered in poll')
80
81
  return [], []
81
- if err.args[0] == errno.EBADF:
82
+ if exc.args[0] == errno.EBADF:
82
83
  log.debug('EBADF encountered in poll')
83
84
  self.unregister_all()
84
85
  return [], []
@@ -94,30 +95,30 @@ class PollPoller(Poller):
94
95
  super().__init__()
95
96
 
96
97
  self._poller = select.poll()
97
- self._readable: set[int] = set()
98
- self._writable: set[int] = set()
98
+ self._readable: set[Fd] = set()
99
+ self._writable: set[Fd] = set()
99
100
 
100
- def register_readable(self, fd: int) -> None:
101
+ def register_readable(self, fd: Fd) -> None:
101
102
  self._poller.register(fd, self._READ)
102
103
  self._readable.add(fd)
103
104
 
104
- def register_writable(self, fd: int) -> None:
105
+ def register_writable(self, fd: Fd) -> None:
105
106
  self._poller.register(fd, self._WRITE)
106
107
  self._writable.add(fd)
107
108
 
108
- def unregister_readable(self, fd: int) -> None:
109
+ def unregister_readable(self, fd: Fd) -> None:
109
110
  self._readable.discard(fd)
110
111
  self._poller.unregister(fd)
111
112
  if fd in self._writable:
112
113
  self._poller.register(fd, self._WRITE)
113
114
 
114
- def unregister_writable(self, fd: int) -> None:
115
+ def unregister_writable(self, fd: Fd) -> None:
115
116
  self._writable.discard(fd)
116
117
  self._poller.unregister(fd)
117
118
  if fd in self._readable:
118
119
  self._poller.register(fd, self._READ)
119
120
 
120
- 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]]:
121
122
  fds = self._poll_fds(timeout) # type: ignore
122
123
  readable, writable = [], []
123
124
  for fd, eventmask in fds:
@@ -129,16 +130,16 @@ class PollPoller(Poller):
129
130
  writable.append(fd)
130
131
  return readable, writable
131
132
 
132
- 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]]:
133
134
  try:
134
- return self._poller.poll(timeout * 1000)
135
- except OSError as err:
136
- 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:
137
138
  log.debug('EINTR encountered in poll')
138
139
  return []
139
140
  raise
140
141
 
141
- def _ignore_invalid(self, fd: int, eventmask: int) -> bool:
142
+ def _ignore_invalid(self, fd: Fd, eventmask: int) -> bool:
142
143
  if eventmask & select.POLLNVAL:
143
144
  # POLLNVAL means `fd` value is invalid, not open. When a process quits it's `fd`s are closed so there is no
144
145
  # more reason to keep this `fd` registered If the process restarts it's `fd`s are registered again.
@@ -157,30 +158,30 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
157
158
  super().__init__()
158
159
 
159
160
  self._kqueue: ta.Optional[ta.Any] = select.kqueue()
160
- self._readable: set[int] = set()
161
- self._writable: set[int] = set()
161
+ self._readable: set[Fd] = set()
162
+ self._writable: set[Fd] = set()
162
163
 
163
- def register_readable(self, fd: int) -> None:
164
+ def register_readable(self, fd: Fd) -> None:
164
165
  self._readable.add(fd)
165
166
  kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
166
167
  self._kqueue_control(fd, kevent)
167
168
 
168
- def register_writable(self, fd: int) -> None:
169
+ def register_writable(self, fd: Fd) -> None:
169
170
  self._writable.add(fd)
170
171
  kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_ADD)
171
172
  self._kqueue_control(fd, kevent)
172
173
 
173
- def unregister_readable(self, fd: int) -> None:
174
+ def unregister_readable(self, fd: Fd) -> None:
174
175
  kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_DELETE)
175
176
  self._readable.discard(fd)
176
177
  self._kqueue_control(fd, kevent)
177
178
 
178
- def unregister_writable(self, fd: int) -> None:
179
+ def unregister_writable(self, fd: Fd) -> None:
179
180
  kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_DELETE)
180
181
  self._writable.discard(fd)
181
182
  self._kqueue_control(fd, kevent)
182
183
 
183
- def _kqueue_control(self, fd: int, kevent: 'select.kevent') -> None:
184
+ def _kqueue_control(self, fd: Fd, kevent: 'select.kevent') -> None:
184
185
  try:
185
186
  self._kqueue.control([kevent], 0) # type: ignore
186
187
  except OSError as error:
@@ -189,7 +190,7 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
189
190
  else:
190
191
  raise
191
192
 
192
- 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]]:
193
194
  readable, writable = [], [] # type: ignore
194
195
 
195
196
  try:
@@ -2,6 +2,7 @@
2
2
  import typing as ta
3
3
 
4
4
  from .types import Process
5
+ from .utils.ostypes import Pid
5
6
 
6
7
 
7
8
  ##
@@ -14,5 +15,5 @@ class ProcessStateError(RuntimeError):
14
15
  ##
15
16
 
16
17
 
17
- class PidHistory(ta.Dict[int, Process]):
18
+ class PidHistory(ta.Dict[Pid, Process]):
18
19
  pass