ominfra 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev130__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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