ominfra 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev130__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.
@@ -3,13 +3,13 @@ import abc
3
3
  import functools
4
4
  import typing as ta
5
5
 
6
+ from omlish.lite.fdio.handlers import FdIoHandler
7
+
6
8
  from .configs import ProcessConfig
7
9
  from .configs import ProcessGroupConfig
8
- from .configs import ServerConfig
9
10
  from .states import ProcessState
10
11
  from .states import SupervisorState
11
12
  from .utils.collections import KeyedCollectionAccessors
12
- from .utils.ostypes import Fd
13
13
  from .utils.ostypes import Pid
14
14
  from .utils.ostypes import Rc
15
15
 
@@ -21,6 +21,10 @@ if ta.TYPE_CHECKING:
21
21
  ##
22
22
 
23
23
 
24
+ class ExitNow(Exception): # noqa
25
+ pass
26
+
27
+
24
28
  ServerEpoch = ta.NewType('ServerEpoch', int)
25
29
 
26
30
 
@@ -44,12 +48,7 @@ class ConfigPriorityOrdered(abc.ABC):
44
48
  ##
45
49
 
46
50
 
47
- class ServerContext(abc.ABC):
48
- @property
49
- @abc.abstractmethod
50
- def config(self) -> ServerConfig:
51
- raise NotImplementedError
52
-
51
+ class SupervisorStateManager(abc.ABC):
53
52
  @property
54
53
  @abc.abstractmethod
55
54
  def state(self) -> SupervisorState:
@@ -63,57 +62,25 @@ class ServerContext(abc.ABC):
63
62
  ##
64
63
 
65
64
 
66
- class Dispatcher(abc.ABC):
67
- @property
65
+ class HasDispatchers(abc.ABC):
68
66
  @abc.abstractmethod
69
- def process(self) -> 'Process':
67
+ def get_dispatchers(self) -> 'Dispatchers':
70
68
  raise NotImplementedError
71
69
 
72
- @property
73
- @abc.abstractmethod
74
- def channel(self) -> str:
75
- raise NotImplementedError
76
70
 
71
+ class ProcessDispatcher(FdIoHandler, abc.ABC):
77
72
  @property
78
73
  @abc.abstractmethod
79
- def fd(self) -> Fd:
74
+ def channel(self) -> str:
80
75
  raise NotImplementedError
81
76
 
82
77
  @property
83
78
  @abc.abstractmethod
84
- def closed(self) -> bool:
85
- raise NotImplementedError
86
-
87
- #
88
-
89
- @abc.abstractmethod
90
- def close(self) -> None:
91
- raise NotImplementedError
92
-
93
- @abc.abstractmethod
94
- def handle_error(self) -> None:
95
- raise NotImplementedError
96
-
97
- #
98
-
99
- @abc.abstractmethod
100
- def readable(self) -> bool:
101
- raise NotImplementedError
102
-
103
- @abc.abstractmethod
104
- def writable(self) -> bool:
79
+ def process(self) -> 'Process':
105
80
  raise NotImplementedError
106
81
 
107
- #
108
-
109
- def handle_read_event(self) -> None:
110
- raise TypeError
111
-
112
- def handle_write_event(self) -> None:
113
- raise TypeError
114
-
115
82
 
116
- class OutputDispatcher(Dispatcher, abc.ABC):
83
+ class ProcessOutputDispatcher(ProcessDispatcher, abc.ABC):
117
84
  @abc.abstractmethod
118
85
  def remove_logs(self) -> None:
119
86
  raise NotImplementedError
@@ -123,7 +90,7 @@ class OutputDispatcher(Dispatcher, abc.ABC):
123
90
  raise NotImplementedError
124
91
 
125
92
 
126
- class InputDispatcher(Dispatcher, abc.ABC):
93
+ class ProcessInputDispatcher(ProcessDispatcher, abc.ABC):
127
94
  @abc.abstractmethod
128
95
  def write(self, chars: ta.Union[bytes, str]) -> None:
129
96
  raise NotImplementedError
@@ -136,7 +103,11 @@ class InputDispatcher(Dispatcher, abc.ABC):
136
103
  ##
137
104
 
138
105
 
139
- class Process(ConfigPriorityOrdered, abc.ABC):
106
+ class Process(
107
+ ConfigPriorityOrdered,
108
+ HasDispatchers,
109
+ abc.ABC,
110
+ ):
140
111
  @property
141
112
  @abc.abstractmethod
142
113
  def name(self) -> str:
@@ -159,11 +130,6 @@ class Process(ConfigPriorityOrdered, abc.ABC):
159
130
 
160
131
  #
161
132
 
162
- @property
163
- @abc.abstractmethod
164
- def context(self) -> ServerContext:
165
- raise NotImplementedError
166
-
167
133
  @abc.abstractmethod
168
134
  def finish(self, sts: Rc) -> None:
169
135
  raise NotImplementedError
@@ -180,18 +146,15 @@ class Process(ConfigPriorityOrdered, abc.ABC):
180
146
  def transition(self) -> None:
181
147
  raise NotImplementedError
182
148
 
149
+ @property
183
150
  @abc.abstractmethod
184
- def get_state(self) -> ProcessState:
151
+ def state(self) -> ProcessState:
185
152
  raise NotImplementedError
186
153
 
187
154
  @abc.abstractmethod
188
155
  def after_setuid(self) -> None:
189
156
  raise NotImplementedError
190
157
 
191
- @abc.abstractmethod
192
- def get_dispatchers(self) -> 'Dispatchers':
193
- raise NotImplementedError
194
-
195
158
 
196
159
  ##
197
160
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev128
3
+ Version: 0.0.0.dev130
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev128
16
- Requires-Dist: omlish==0.0.0.dev128
15
+ Requires-Dist: omdev==0.0.0.dev130
16
+ Requires-Dist: omlish==0.0.0.dev130
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -22,7 +22,7 @@ ominfra/clouds/aws/journald2aws/poster.py,sha256=hz1XuctW8GtLmfjhRvCFY6py52D4BzX
22
22
  ominfra/clouds/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  ominfra/clouds/gcp/auth.py,sha256=3PyfRJNgajjMqJFem3SKui0CqGeHEsZlvbRhuxFcZG8,1348
24
24
  ominfra/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- ominfra/deploy/_executor.py,sha256=osVBYirgpX1iJKyLHf7JwKD3AhPqJrnCrjI4qVrNFJo,34662
25
+ ominfra/deploy/_executor.py,sha256=3h7ViDW9WtF__sI_qK88SUgldalqdnpf5AlJGbCF9TY,35361
26
26
  ominfra/deploy/configs.py,sha256=qi0kwT7G2NH7dXLOQic-u6R3yeadup_QtvrjwWIggbM,435
27
27
  ominfra/deploy/remote.py,sha256=6ACmpXU1uBdyGs3Xsp97ktKFq30cJlzN9LRWNUWlGY4,2144
28
28
  ominfra/deploy/executor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
@@ -56,37 +56,38 @@ ominfra/journald/tailer.py,sha256=5abcFMfgi7fnY9ZEQe2ZVobaJxjQkeu6d9Kagw33a1w,33
56
56
  ominfra/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  ominfra/manage/manage.py,sha256=BttL8LFEknHZE_h2Pt5dAqbfUkv6qy43WI0raXBZ1a8,151
58
58
  ominfra/pyremote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- ominfra/pyremote/_runcommands.py,sha256=86bIzkhi5YLmtxQYyk8SBwPv8fCCfdu2NHHlxug4ACk,28498
59
+ ominfra/pyremote/_runcommands.py,sha256=TfO2cwXbw_1T-c_NDM7K8gWYJbU20Y3k9qEIOM3S6BI,29194
60
60
  ominfra/pyremote/bootstrap.py,sha256=RvMO3YGaN1E4sgUi1JEtiPak8cjvqtc_vRCq1yqbeZg,3370
61
61
  ominfra/pyremote/runcommands.py,sha256=bviS0_TDIoZVAe4h-_iavbvJtVSFu8lnk7fQ5iasCWE,1571
62
62
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
- ominfra/scripts/journald2aws.py,sha256=UpwV8P1ZE8ywXW-8xpCaTCBRWe94BuG_MS1CXfD3wcw,128547
64
- ominfra/scripts/supervisor.py,sha256=ZqUDlxRxSML5Ty0sOKuils35km5-9xjYBQF4yQDnVaY,222164
63
+ ominfra/scripts/journald2aws.py,sha256=FmjNwUPQDCOmIbUxwkN7mNHATf_lXf1uwp0ST71IHFA,131405
64
+ ominfra/scripts/supervisor.py,sha256=DHvE1h2s57IvHEIZnqsmhx7BrrO20Qbd21IwkQGasmM,242622
65
65
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
66
66
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
67
67
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
68
68
  ominfra/supervisor/configs.py,sha256=AhBlbifwDXc0acEhcbdv9jphJL-SBFODFDAWDVckzAE,3945
69
- ominfra/supervisor/context.py,sha256=UDJc0mVHHFg3T7MyO7gehWX1wwmFvw-7xeKLl-cC8jc,2595
70
- ominfra/supervisor/dispatchers.py,sha256=qWtVLx-4H3I-Io7OPcVgRpIYehGsA4q3Duo5ZWdUM4Y,970
71
- ominfra/supervisor/dispatchersimpl.py,sha256=t1VFcofj1kTH1q13Z-S1OUTXixPwgSqJkh5A5IkHKPA,10956
69
+ ominfra/supervisor/dispatchers.py,sha256=dfjog5PyVAJaFzgFI0tpy38ZgDiFrewMjVi21ksrlAg,1007
70
+ ominfra/supervisor/dispatchersimpl.py,sha256=k86MSDxIHInOBopYehNuCDcSM_QsxrrGuME0UjnSs_o,11249
72
71
  ominfra/supervisor/events.py,sha256=w3HQFrq-SuroYWoQfNFYeU1phnTvHTgsAqA6TGtAafI,6593
73
72
  ominfra/supervisor/exceptions.py,sha256=Qbu211H3CLlSmi9LsSikOwrcL5HgJP9ugvcKWlGTAoI,750
74
- ominfra/supervisor/groups.py,sha256=g5Zp_lkVhn1FSe6GSEPbaELincG5a46ctv1xpB-WmnQ,2163
75
- ominfra/supervisor/groupsimpl.py,sha256=_5Mxf6VdVV375KgwcuUTpErUH4FBpmzbNm3vbGB_JaQ,2304
76
- ominfra/supervisor/inject.py,sha256=ZCfloXxURjiVTutzudGFWybrVCvjDD_C3HayMATCMrY,3042
77
- ominfra/supervisor/main.py,sha256=RCCpMX4D2RKT8vZMF82pENKpXfW3GUdNgMEUzUtpqL0,4140
73
+ ominfra/supervisor/groups.py,sha256=MBbsbt8Zh_WEYkGOr1KXa82gnPVw9wPB2lAnqhugXSc,2457
74
+ ominfra/supervisor/groupsimpl.py,sha256=nIrW4SmB0W6c2jOR_HhkfVcH4eHyLZnG1FJ0MCzc6mQ,2292
75
+ ominfra/supervisor/http.py,sha256=y0tos6zbb4k-WvNQXlgAeK1qi5mKXkyXO9bVJt4OXew,3227
76
+ ominfra/supervisor/inject.py,sha256=onKUudx5eBbNuXwEqeiaVIIA3ZbpUSDiEAKg9GBiWG8,4514
77
+ ominfra/supervisor/io.py,sha256=2NO4BYC-PznIrJpTFxN8UEAhd_codfNm_HI424gYQ3c,3294
78
+ ominfra/supervisor/main.py,sha256=ebe7skFPfwXV2meMVRndhuLZmz-LiuHH1x1CgiarR0o,4132
78
79
  ominfra/supervisor/pipes.py,sha256=XrJ9lD04tPdzZD3xhhYKxpBKHWhZ0Ii315E78bgj7ws,2233
79
- ominfra/supervisor/poller.py,sha256=LnQVttPCm8a1UtnDvsho6zLw8NP-2_2VUiNM-d0w_FU,7776
80
80
  ominfra/supervisor/privileges.py,sha256=bO7rJGT7cMOBALK_4D4NiQnOS5dOYb14Sz66R-ymG24,2071
81
81
  ominfra/supervisor/process.py,sha256=UaubVxsxVqDnbuWVpTH0DTGbJGLO0vGJ9mNcvy2kCXM,217
82
- ominfra/supervisor/processimpl.py,sha256=0i0P3gdxuJsgcvy2VTKN6-NYgcBjozUaPsA_tUcEmb4,18769
82
+ ominfra/supervisor/processimpl.py,sha256=vpyRVOYV8_AW5eCtgAa2LKWyW9FiqA5bKmBqF1KYE5c,18715
83
83
  ominfra/supervisor/setup.py,sha256=7HwwwI-WT_Z0WjZ9_l5Orr4K298nKKhQ1f_ZgGsi9TU,622
84
84
  ominfra/supervisor/setupimpl.py,sha256=S_YgCH3XzLsFIAriJROvDMUDh7OzVVJoxzEzCkbb4g4,9648
85
+ ominfra/supervisor/signals.py,sha256=jY52naUifcAjd6nICTP1ZW3IQSPsHB4cvbsJo8_QV_U,2196
85
86
  ominfra/supervisor/spawning.py,sha256=i1k3tmqWyU-KIN7kel-JVxTVGnLiTIVmZzlstJSZpjM,622
86
- ominfra/supervisor/spawningimpl.py,sha256=0InDyRPh7gAEX07lg6eUYMymX0RikHOz_ieAghxKx8Y,11063
87
+ ominfra/supervisor/spawningimpl.py,sha256=Med8UJH7vP3IaihTJIaqfndhB5mD5wht6Feim674HKs,11161
87
88
  ominfra/supervisor/states.py,sha256=9yoNOSwalRcKEnCP9zG6tVS0oivo5tCeuH6AaaW7Jpc,890
88
- ominfra/supervisor/supervisor.py,sha256=3yBaGJHJGqDXlARjpHWg7371N6jb29HaFEI-_fSp-xU,12028
89
- ominfra/supervisor/types.py,sha256=i2C7ZBvLqjVliIYAYQnPx2WwIQ14HIVV1hTUgB2mbBM,4740
89
+ ominfra/supervisor/supervisor.py,sha256=XiLVq8qMLKGR50yTgW5njtqVkZCupf-cXDjmh7bJtXQ,9515
90
+ ominfra/supervisor/types.py,sha256=RamAYEF3fISfKwlIMRleLOFuKVWrxjQJGI6p76jK03c,3959
90
91
  ominfra/supervisor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
92
  ominfra/supervisor/utils/collections.py,sha256=vcfmVYS4QngMdtEI1DvdRIcubmy55Wj40NCzW27_rIY,1361
92
93
  ominfra/supervisor/utils/diag.py,sha256=ujz4gkW7p3wmbaKFM8Hz5eHEwpoUkbB8JeDvcHilCz0,705
@@ -102,9 +103,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
102
103
  ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
103
104
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
105
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
105
- ominfra-0.0.0.dev128.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
106
- ominfra-0.0.0.dev128.dist-info/METADATA,sha256=JV6Hw2ZOpDub73wDD4p69Qq9F6XbbKlthV7CmH6G_-k,731
107
- ominfra-0.0.0.dev128.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
108
- ominfra-0.0.0.dev128.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
109
- ominfra-0.0.0.dev128.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
110
- ominfra-0.0.0.dev128.dist-info/RECORD,,
106
+ ominfra-0.0.0.dev130.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
107
+ ominfra-0.0.0.dev130.dist-info/METADATA,sha256=PjbMdzNCdOt4JYdmoDTM92oJu056qJOvTng2oncmCIU,731
108
+ ominfra-0.0.0.dev130.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
109
+ ominfra-0.0.0.dev130.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
110
+ ominfra-0.0.0.dev130.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
111
+ ominfra-0.0.0.dev130.dist-info/RECORD,,
@@ -1,80 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- import errno
3
- import os
4
- import typing as ta
5
-
6
- from omlish.lite.logs import log
7
-
8
- from .configs import ServerConfig
9
- from .poller import Poller
10
- from .states import SupervisorState
11
- from .types import ServerContext
12
- from .types import ServerEpoch
13
- from .utils.fs import mktempfile
14
- from .utils.ostypes import Pid
15
- from .utils.ostypes import Rc
16
-
17
-
18
- class ServerContextImpl(ServerContext):
19
- def __init__(
20
- self,
21
- config: ServerConfig,
22
- poller: Poller,
23
- *,
24
- epoch: ServerEpoch = ServerEpoch(0),
25
- ) -> None:
26
- super().__init__()
27
-
28
- self._config = config
29
- self._poller = poller
30
- self._epoch = epoch
31
-
32
- self._state: SupervisorState = SupervisorState.RUNNING
33
-
34
- @property
35
- def config(self) -> ServerConfig:
36
- return self._config
37
-
38
- @property
39
- def epoch(self) -> ServerEpoch:
40
- return self._epoch
41
-
42
- @property
43
- def first(self) -> bool:
44
- return not self._epoch
45
-
46
- @property
47
- def state(self) -> SupervisorState:
48
- return self._state
49
-
50
- def set_state(self, state: SupervisorState) -> None:
51
- self._state = state
52
-
53
- #
54
-
55
- def waitpid(self) -> ta.Tuple[ta.Optional[Pid], ta.Optional[Rc]]:
56
- # Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
57
- # still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
58
- # waitpid is interrupted by SIGCHLD, as long as we call waitpid again (which happens every so often during the
59
- # normal course in the mainloop), we'll eventually reap the child that we tried to reap during the interrupted
60
- # call. At least on Linux, this appears to be true, or at least stopping 50 processes at once never left zombies
61
- # lying around.
62
- try:
63
- pid, sts = os.waitpid(-1, os.WNOHANG)
64
- except OSError as exc:
65
- code = exc.args[0]
66
- if code not in (errno.ECHILD, errno.EINTR):
67
- log.critical('waitpid error %r; a process may not be cleaned up properly', code)
68
- if code == errno.EINTR:
69
- log.debug('EINTR during reap')
70
- pid, sts = None, None
71
- return pid, sts # type: ignore
72
-
73
- def get_auto_child_log_name(self, name: str, identifier: str, channel: str) -> str:
74
- prefix = f'{name}-{channel}---{identifier}-'
75
- logfile = mktempfile(
76
- suffix='.log',
77
- prefix=prefix,
78
- dir=self.config.child_logdir,
79
- )
80
- return logfile
@@ -1,240 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- import abc
3
- import errno
4
- import select
5
- import sys
6
- import typing as ta
7
-
8
- from omlish.lite.logs import log
9
-
10
- from .setup import DaemonizeListener
11
- from .utils.ostypes import Fd
12
-
13
-
14
- class Poller(DaemonizeListener, abc.ABC):
15
- def __init__(self) -> None:
16
- super().__init__()
17
-
18
- @abc.abstractmethod
19
- def register_readable(self, fd: Fd) -> None:
20
- raise NotImplementedError
21
-
22
- @abc.abstractmethod
23
- def register_writable(self, fd: Fd) -> None:
24
- raise NotImplementedError
25
-
26
- @abc.abstractmethod
27
- def unregister_readable(self, fd: Fd) -> None:
28
- raise NotImplementedError
29
-
30
- @abc.abstractmethod
31
- def unregister_writable(self, fd: Fd) -> None:
32
- raise NotImplementedError
33
-
34
- @abc.abstractmethod
35
- def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
36
- raise NotImplementedError
37
-
38
- def before_daemonize(self) -> None: # noqa
39
- pass
40
-
41
- def after_daemonize(self) -> None: # noqa
42
- pass
43
-
44
- def close(self) -> None: # noqa
45
- pass
46
-
47
-
48
- class SelectPoller(Poller):
49
- def __init__(self) -> None:
50
- super().__init__()
51
-
52
- self._readable: ta.Set[Fd] = set()
53
- self._writable: ta.Set[Fd] = set()
54
-
55
- def register_readable(self, fd: Fd) -> None:
56
- self._readable.add(fd)
57
-
58
- def register_writable(self, fd: Fd) -> None:
59
- self._writable.add(fd)
60
-
61
- def unregister_readable(self, fd: Fd) -> None:
62
- self._readable.discard(fd)
63
-
64
- def unregister_writable(self, fd: Fd) -> None:
65
- self._writable.discard(fd)
66
-
67
- def unregister_all(self) -> None:
68
- self._readable.clear()
69
- self._writable.clear()
70
-
71
- def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
72
- try:
73
- r, w, x = select.select(
74
- self._readable,
75
- self._writable,
76
- [], timeout,
77
- )
78
- except OSError as exc:
79
- if exc.args[0] == errno.EINTR:
80
- log.debug('EINTR encountered in poll')
81
- return [], []
82
- if exc.args[0] == errno.EBADF:
83
- log.debug('EBADF encountered in poll')
84
- self.unregister_all()
85
- return [], []
86
- raise
87
- return r, w
88
-
89
-
90
- class PollPoller(Poller):
91
- _READ = select.POLLIN | select.POLLPRI | select.POLLHUP
92
- _WRITE = select.POLLOUT
93
-
94
- def __init__(self) -> None:
95
- super().__init__()
96
-
97
- self._poller = select.poll()
98
- self._readable: set[Fd] = set()
99
- self._writable: set[Fd] = set()
100
-
101
- def register_readable(self, fd: Fd) -> None:
102
- self._poller.register(fd, self._READ)
103
- self._readable.add(fd)
104
-
105
- def register_writable(self, fd: Fd) -> None:
106
- self._poller.register(fd, self._WRITE)
107
- self._writable.add(fd)
108
-
109
- def unregister_readable(self, fd: Fd) -> None:
110
- self._readable.discard(fd)
111
- self._poller.unregister(fd)
112
- if fd in self._writable:
113
- self._poller.register(fd, self._WRITE)
114
-
115
- def unregister_writable(self, fd: Fd) -> None:
116
- self._writable.discard(fd)
117
- self._poller.unregister(fd)
118
- if fd in self._readable:
119
- self._poller.register(fd, self._READ)
120
-
121
- def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
122
- fds = self._poll_fds(timeout) # type: ignore
123
- readable, writable = [], []
124
- for fd, eventmask in fds:
125
- if self._ignore_invalid(fd, eventmask):
126
- continue
127
- if eventmask & self._READ:
128
- readable.append(fd)
129
- if eventmask & self._WRITE:
130
- writable.append(fd)
131
- return readable, writable
132
-
133
- def _poll_fds(self, timeout: float) -> ta.List[ta.Tuple[Fd, Fd]]:
134
- try:
135
- return self._poller.poll(timeout * 1000) # type: ignore
136
- except OSError as exc:
137
- if exc.args[0] == errno.EINTR:
138
- log.debug('EINTR encountered in poll')
139
- return []
140
- raise
141
-
142
- def _ignore_invalid(self, fd: Fd, eventmask: int) -> bool:
143
- if eventmask & select.POLLNVAL:
144
- # POLLNVAL means `fd` value is invalid, not open. When a process quits it's `fd`s are closed so there is no
145
- # more reason to keep this `fd` registered If the process restarts it's `fd`s are registered again.
146
- self._poller.unregister(fd)
147
- self._readable.discard(fd)
148
- self._writable.discard(fd)
149
- return True
150
- return False
151
-
152
-
153
- if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
154
- class KqueuePoller(Poller):
155
- max_events = 1000
156
-
157
- def __init__(self) -> None:
158
- super().__init__()
159
-
160
- self._kqueue: ta.Optional[ta.Any] = select.kqueue()
161
- self._readable: set[Fd] = set()
162
- self._writable: set[Fd] = set()
163
-
164
- def register_readable(self, fd: Fd) -> None:
165
- self._readable.add(fd)
166
- kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
167
- self._kqueue_control(fd, kevent)
168
-
169
- def register_writable(self, fd: Fd) -> None:
170
- self._writable.add(fd)
171
- kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_ADD)
172
- self._kqueue_control(fd, kevent)
173
-
174
- def unregister_readable(self, fd: Fd) -> None:
175
- kevent = select.kevent(fd, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_DELETE)
176
- self._readable.discard(fd)
177
- self._kqueue_control(fd, kevent)
178
-
179
- def unregister_writable(self, fd: Fd) -> None:
180
- kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE, flags=select.KQ_EV_DELETE)
181
- self._writable.discard(fd)
182
- self._kqueue_control(fd, kevent)
183
-
184
- def _kqueue_control(self, fd: Fd, kevent: 'select.kevent') -> None:
185
- try:
186
- self._kqueue.control([kevent], 0) # type: ignore
187
- except OSError as error:
188
- if error.errno == errno.EBADF:
189
- log.debug('EBADF encountered in kqueue. Invalid file descriptor %s', fd)
190
- else:
191
- raise
192
-
193
- def poll(self, timeout: ta.Optional[float]) -> ta.Tuple[ta.List[Fd], ta.List[Fd]]:
194
- readable, writable = [], [] # type: ignore
195
-
196
- try:
197
- kevents = self._kqueue.control(None, self.max_events, timeout) # type: ignore
198
- except OSError as error:
199
- if error.errno == errno.EINTR:
200
- log.debug('EINTR encountered in poll')
201
- return readable, writable
202
- raise
203
-
204
- for kevent in kevents:
205
- if kevent.filter == select.KQ_FILTER_READ:
206
- readable.append(kevent.ident)
207
- if kevent.filter == select.KQ_FILTER_WRITE:
208
- writable.append(kevent.ident)
209
-
210
- return readable, writable
211
-
212
- def before_daemonize(self) -> None:
213
- self.close()
214
-
215
- def after_daemonize(self) -> None:
216
- self._kqueue = select.kqueue()
217
- for fd in self._readable:
218
- self.register_readable(fd)
219
- for fd in self._writable:
220
- self.register_writable(fd)
221
-
222
- def close(self) -> None:
223
- self._kqueue.close() # type: ignore
224
- self._kqueue = None
225
-
226
- else:
227
- KqueuePoller = None
228
-
229
-
230
- def get_poller_impl() -> ta.Type[Poller]:
231
- if (
232
- (sys.platform == 'darwin' or sys.platform.startswith('freebsd')) and
233
- hasattr(select, 'kqueue') and
234
- KqueuePoller is not None
235
- ):
236
- return KqueuePoller
237
- elif hasattr(select, 'poll'):
238
- return PollPoller
239
- else:
240
- return SelectPoller