omlish 0.0.0.dev242__py3-none-any.whl → 0.0.0.dev243__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.
- omlish/__about__.py +2 -2
- omlish/asyncs/asyncio/sockets.py +45 -0
- omlish/lang/__init__.py +6 -3
- omlish/lang/contextmanagers.py +0 -97
- omlish/lite/contextmanagers.py +91 -12
- omlish/logs/protocol.py +14 -14
- omlish/sockets/server/server.py +71 -38
- omlish/sockets/wait.py +39 -0
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/RECORD +14 -12
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev242.dist-info → omlish-0.0.0.dev243.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import asyncio
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from ...lite.timeouts import Timeout
|
7
|
+
from ...lite.timeouts import TimeoutLike
|
8
|
+
|
9
|
+
|
10
|
+
async def asyncio_wait_until_can_connect(
|
11
|
+
host: ta.Any = None,
|
12
|
+
port: ta.Any = None,
|
13
|
+
*,
|
14
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
15
|
+
on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
|
16
|
+
sleep_s: float = .1,
|
17
|
+
exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (Exception,),
|
18
|
+
) -> None:
|
19
|
+
timeout = Timeout.of(timeout)
|
20
|
+
|
21
|
+
async def inner():
|
22
|
+
while True:
|
23
|
+
timeout()
|
24
|
+
|
25
|
+
try:
|
26
|
+
reader, writer = await asyncio.open_connection(host, port)
|
27
|
+
|
28
|
+
except asyncio.CancelledError:
|
29
|
+
raise
|
30
|
+
|
31
|
+
except exception as e: # noqa
|
32
|
+
if on_fail is not None:
|
33
|
+
on_fail(e)
|
34
|
+
|
35
|
+
else:
|
36
|
+
writer.close()
|
37
|
+
await asyncio.wait_for(writer.wait_closed(), timeout=timeout.or_(None))
|
38
|
+
break
|
39
|
+
|
40
|
+
await asyncio.sleep(min(sleep_s, timeout.remaining()))
|
41
|
+
|
42
|
+
if timeout() != float('inf'):
|
43
|
+
await asyncio.wait_for(inner(), timeout=timeout())
|
44
|
+
else:
|
45
|
+
await inner()
|
omlish/lang/__init__.py
CHANGED
@@ -54,18 +54,15 @@ from .cmp import ( # noqa
|
|
54
54
|
|
55
55
|
from .contextmanagers import ( # noqa
|
56
56
|
AsyncContextManager,
|
57
|
-
AsyncExitStacked,
|
58
57
|
ContextManaged,
|
59
58
|
ContextManager,
|
60
59
|
ContextWrapped,
|
61
60
|
DefaultLockable,
|
62
|
-
ExitStacked,
|
63
61
|
Lockable,
|
64
62
|
NOP_CONTEXT_MANAGER,
|
65
63
|
NopContextManager,
|
66
64
|
Timer,
|
67
65
|
a_defer,
|
68
|
-
attr_setting,
|
69
66
|
breakpoint_on_exception,
|
70
67
|
context_var_setting,
|
71
68
|
context_wrapped,
|
@@ -232,6 +229,12 @@ from .typing import ( # noqa
|
|
232
229
|
|
233
230
|
##
|
234
231
|
|
232
|
+
from ..lite.contextmanagers import ( # noqa
|
233
|
+
attr_setting,
|
234
|
+
AsyncExitStacked,
|
235
|
+
ExitStacked,
|
236
|
+
)
|
237
|
+
|
235
238
|
from ..lite.imports import ( # noqa
|
236
239
|
import_attr,
|
237
240
|
import_module,
|
omlish/lang/contextmanagers.py
CHANGED
@@ -170,103 +170,6 @@ def context_var_setting(var: contextvars.ContextVar[T], val: T) -> ta.Iterator[T
|
|
170
170
|
var.reset(token)
|
171
171
|
|
172
172
|
|
173
|
-
@contextlib.contextmanager
|
174
|
-
def attr_setting(obj, attr, val, *, default=None): # noqa
|
175
|
-
not_set = object()
|
176
|
-
orig = getattr(obj, attr, not_set)
|
177
|
-
try:
|
178
|
-
setattr(obj, attr, val)
|
179
|
-
if orig is not not_set:
|
180
|
-
yield orig
|
181
|
-
else:
|
182
|
-
yield default
|
183
|
-
finally:
|
184
|
-
if orig is not_set:
|
185
|
-
delattr(obj, attr)
|
186
|
-
else:
|
187
|
-
setattr(obj, attr, orig)
|
188
|
-
|
189
|
-
|
190
|
-
##
|
191
|
-
|
192
|
-
|
193
|
-
class ExitStacked:
|
194
|
-
@property
|
195
|
-
def _exit_stack(self) -> contextlib.ExitStack:
|
196
|
-
try:
|
197
|
-
return self.__exit_stack # type: ignore
|
198
|
-
except AttributeError:
|
199
|
-
es = self.__exit_stack = contextlib.ExitStack()
|
200
|
-
return es
|
201
|
-
|
202
|
-
def _enter_context(self, context_manager: ta.ContextManager[T]) -> T:
|
203
|
-
return self._exit_stack.enter_context(ta.cast(ta.ContextManager, context_manager))
|
204
|
-
|
205
|
-
def __enter__(self) -> ta.Self:
|
206
|
-
try:
|
207
|
-
superfn = super().__enter__ # type: ignore
|
208
|
-
except AttributeError:
|
209
|
-
ret = self
|
210
|
-
else:
|
211
|
-
ret = superfn()
|
212
|
-
self._exit_stack.__enter__()
|
213
|
-
return ret
|
214
|
-
|
215
|
-
def __exit__(
|
216
|
-
self,
|
217
|
-
exc_type: type[BaseException] | None,
|
218
|
-
exc_val: BaseException | None,
|
219
|
-
exc_tb: types.TracebackType | None,
|
220
|
-
) -> bool | None:
|
221
|
-
self._exit_stack.__exit__(exc_type, exc_val, exc_tb)
|
222
|
-
try:
|
223
|
-
superfn = super().__exit__ # type: ignore
|
224
|
-
except AttributeError:
|
225
|
-
return None
|
226
|
-
else:
|
227
|
-
return superfn(exc_type, exc_val, exc_tb)
|
228
|
-
|
229
|
-
|
230
|
-
class AsyncExitStacked:
|
231
|
-
@property
|
232
|
-
def _exit_stack(self) -> contextlib.AsyncExitStack:
|
233
|
-
try:
|
234
|
-
return self.__exit_stack # type: ignore
|
235
|
-
except AttributeError:
|
236
|
-
es = self.__exit_stack = contextlib.AsyncExitStack()
|
237
|
-
return es
|
238
|
-
|
239
|
-
async def _enter_async_context(self, context_manager: ta.AsyncContextManager[T]) -> T:
|
240
|
-
return await self._exit_stack.enter_async_context(ta.cast(ta.AsyncContextManager, context_manager))
|
241
|
-
|
242
|
-
def _enter_context(self, context_manager: ta.ContextManager[T]) -> T:
|
243
|
-
return self._exit_stack.enter_context(ta.cast(ta.ContextManager, context_manager))
|
244
|
-
|
245
|
-
async def __aenter__(self) -> ta.Self:
|
246
|
-
try:
|
247
|
-
superfn = super().__aenter__ # type: ignore
|
248
|
-
except AttributeError:
|
249
|
-
ret = self
|
250
|
-
else:
|
251
|
-
ret = await superfn()
|
252
|
-
await self._exit_stack.__aenter__()
|
253
|
-
return ret
|
254
|
-
|
255
|
-
async def __aexit__(
|
256
|
-
self,
|
257
|
-
exc_type: type[BaseException] | None,
|
258
|
-
exc_val: BaseException | None,
|
259
|
-
exc_tb: types.TracebackType | None,
|
260
|
-
) -> bool | None:
|
261
|
-
await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb)
|
262
|
-
try:
|
263
|
-
superfn = super().__aexit__ # type: ignore
|
264
|
-
except AttributeError:
|
265
|
-
return None
|
266
|
-
else:
|
267
|
-
return await superfn(exc_type, exc_val, exc_tb)
|
268
|
-
|
269
|
-
|
270
173
|
##
|
271
174
|
|
272
175
|
|
omlish/lite/contextmanagers.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# ruff: noqa: UP007
|
2
2
|
import contextlib
|
3
|
+
import sys
|
3
4
|
import typing as ta
|
4
5
|
|
5
6
|
from .check import check
|
@@ -14,20 +15,64 @@ AsyncExitStackedT = ta.TypeVar('AsyncExitStackedT', bound='AsyncExitStacked')
|
|
14
15
|
|
15
16
|
|
16
17
|
class ExitStacked:
|
18
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
19
|
+
super().__init_subclass__(**kwargs)
|
20
|
+
|
21
|
+
for a in ('__enter__', '__exit__'):
|
22
|
+
for b in cls.__bases__:
|
23
|
+
if b is ExitStacked:
|
24
|
+
continue
|
25
|
+
try:
|
26
|
+
fn = getattr(b, a)
|
27
|
+
except AttributeError:
|
28
|
+
pass
|
29
|
+
else:
|
30
|
+
if fn is not getattr(ExitStacked, a):
|
31
|
+
raise TypeError(f'ExitStacked subclass {cls} must not not override {a} via {b}')
|
32
|
+
|
17
33
|
_exit_stack: ta.Optional[contextlib.ExitStack] = None
|
18
34
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
35
|
+
@contextlib.contextmanager
|
36
|
+
def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
|
37
|
+
"""
|
38
|
+
Overridable wrapper around __enter__ which deliberately does not have access to an _exit_stack yet. Intended for
|
39
|
+
things like wrapping __enter__ in a lock.
|
40
|
+
"""
|
41
|
+
|
42
|
+
yield
|
24
43
|
|
44
|
+
@ta.final
|
45
|
+
def __enter__(self: ExitStackedT) -> ExitStackedT:
|
46
|
+
"""
|
47
|
+
Final because any contexts entered during this init must be exited if any exception is thrown, and user
|
48
|
+
overriding would likely interfere with that. Override `_enter_contexts` for such init.
|
49
|
+
"""
|
50
|
+
|
51
|
+
with self._exit_stacked_init_wrapper():
|
52
|
+
check.state(self._exit_stack is None)
|
53
|
+
es = self._exit_stack = contextlib.ExitStack()
|
54
|
+
es.__enter__()
|
55
|
+
try:
|
56
|
+
self._enter_contexts()
|
57
|
+
except Exception: # noqa
|
58
|
+
es.__exit__(*sys.exc_info())
|
59
|
+
raise
|
60
|
+
return self
|
61
|
+
|
62
|
+
@ta.final
|
25
63
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
26
64
|
if (es := self._exit_stack) is None:
|
27
65
|
return None
|
28
|
-
|
66
|
+
try:
|
67
|
+
self._exit_contexts()
|
68
|
+
except Exception: # noqa
|
69
|
+
es.__exit__(*sys.exc_info())
|
70
|
+
raise
|
29
71
|
return es.__exit__(exc_type, exc_val, exc_tb)
|
30
72
|
|
73
|
+
def _enter_contexts(self) -> None:
|
74
|
+
pass
|
75
|
+
|
31
76
|
def _exit_contexts(self) -> None:
|
32
77
|
pass
|
33
78
|
|
@@ -37,20 +82,54 @@ class ExitStacked:
|
|
37
82
|
|
38
83
|
|
39
84
|
class AsyncExitStacked:
|
85
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
86
|
+
super().__init_subclass__(**kwargs)
|
87
|
+
|
88
|
+
for a in ('__aenter__', '__aexit__'):
|
89
|
+
for b in cls.__bases__:
|
90
|
+
if b is AsyncExitStacked:
|
91
|
+
continue
|
92
|
+
try:
|
93
|
+
fn = getattr(b, a)
|
94
|
+
except AttributeError:
|
95
|
+
pass
|
96
|
+
else:
|
97
|
+
if fn is not getattr(AsyncExitStacked, a):
|
98
|
+
raise TypeError(f'AsyncExitStacked subclass {cls} must not not override {a} via {b}')
|
99
|
+
|
40
100
|
_exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
|
41
101
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
await es.__aenter__()
|
46
|
-
return self
|
102
|
+
@contextlib.asynccontextmanager
|
103
|
+
async def _async_exit_stacked_init_wrapper(self) -> ta.AsyncGenerator[None, None]:
|
104
|
+
yield
|
47
105
|
|
106
|
+
@ta.final
|
107
|
+
async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
|
108
|
+
async with self._async_exit_stacked_init_wrapper():
|
109
|
+
check.state(self._exit_stack is None)
|
110
|
+
es = self._exit_stack = contextlib.AsyncExitStack()
|
111
|
+
await es.__aenter__()
|
112
|
+
try:
|
113
|
+
await self._async_enter_contexts()
|
114
|
+
except Exception: # noqa
|
115
|
+
await es.__aexit__(*sys.exc_info())
|
116
|
+
raise
|
117
|
+
return self
|
118
|
+
|
119
|
+
@ta.final
|
48
120
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
49
121
|
if (es := self._exit_stack) is None:
|
50
122
|
return None
|
51
|
-
|
123
|
+
try:
|
124
|
+
await self._async_exit_contexts()
|
125
|
+
except Exception: # noqa
|
126
|
+
await es.__aexit__(*sys.exc_info())
|
127
|
+
raise
|
52
128
|
return await es.__aexit__(exc_type, exc_val, exc_tb)
|
53
129
|
|
130
|
+
async def _async_enter_contexts(self) -> None:
|
131
|
+
pass
|
132
|
+
|
54
133
|
async def _async_exit_contexts(self) -> None:
|
55
134
|
pass
|
56
135
|
|
omlish/logs/protocol.py
CHANGED
@@ -23,25 +23,25 @@ class Logging(ta.Protocol):
|
|
23
23
|
|
24
24
|
#
|
25
25
|
|
26
|
-
def debug(self, msg: str, *args, **kwargs) -> None:
|
26
|
+
def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
27
27
|
...
|
28
28
|
|
29
|
-
def info(self, msg: str, *args, **kwargs) -> None:
|
29
|
+
def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
30
30
|
...
|
31
31
|
|
32
|
-
def warning(self, msg: str, *args, **kwargs) -> None:
|
32
|
+
def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
33
33
|
...
|
34
34
|
|
35
|
-
def error(self, msg: str, *args, **kwargs) -> None:
|
35
|
+
def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
36
36
|
...
|
37
37
|
|
38
|
-
def exception(self, msg: str, *args, exc_info=True, **kwargs) -> None:
|
38
|
+
def exception(self, msg: str, *args: ta.Any, exc_info: bool = True, **kwargs) -> None:
|
39
39
|
...
|
40
40
|
|
41
|
-
def critical(self, msg: str, *args, **kwargs) -> None:
|
41
|
+
def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
42
42
|
...
|
43
43
|
|
44
|
-
def log(self, level: LogLevel, msg: str, *args, **kwargs) -> None:
|
44
|
+
def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
45
45
|
...
|
46
46
|
|
47
47
|
|
@@ -66,30 +66,30 @@ class AbstractLogging(abc.ABC):
|
|
66
66
|
|
67
67
|
#
|
68
68
|
|
69
|
-
def debug(self, msg: str, *args, **kwargs) -> None:
|
69
|
+
def debug(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
70
70
|
if self.is_enabled_for(logging.DEBUG):
|
71
71
|
self.log(logging.DEBUG, msg, args, **kwargs)
|
72
72
|
|
73
|
-
def info(self, msg: str, *args, **kwargs) -> None:
|
73
|
+
def info(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
74
74
|
if self.is_enabled_for(logging.INFO):
|
75
75
|
self.log(logging.INFO, msg, args, **kwargs)
|
76
76
|
|
77
|
-
def warning(self, msg: str, *args, **kwargs) -> None:
|
77
|
+
def warning(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
78
78
|
if self.is_enabled_for(logging.WARNING):
|
79
79
|
self.log(logging.WARNING, msg, args, **kwargs)
|
80
80
|
|
81
|
-
def error(self, msg: str, *args, **kwargs) -> None:
|
81
|
+
def error(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
82
82
|
if self.is_enabled_for(logging.ERROR):
|
83
83
|
self.log(logging.ERROR, msg, args, **kwargs)
|
84
84
|
|
85
|
-
def exception(self, msg: str, *args, exc_info=True, **kwargs) -> None:
|
85
|
+
def exception(self, msg: str, *args: ta.Any, exc_info: bool = True, **kwargs: ta.Any) -> None:
|
86
86
|
self.error(msg, *args, exc_info=exc_info, **kwargs)
|
87
87
|
|
88
|
-
def critical(self, msg: str, *args, **kwargs) -> None:
|
88
|
+
def critical(self, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
89
89
|
if self.is_enabled_for(logging.CRITICAL):
|
90
90
|
self.log(logging.CRITICAL, msg, args, **kwargs)
|
91
91
|
|
92
|
-
def log(self, level: LogLevel, msg: str, *args, **kwargs) -> None:
|
92
|
+
def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None:
|
93
93
|
if not isinstance(level, int):
|
94
94
|
raise TypeError('Level must be an integer.')
|
95
95
|
if self.is_enabled_for(level):
|
omlish/sockets/server/server.py
CHANGED
@@ -2,11 +2,14 @@
|
|
2
2
|
# ruff: noqa: UP006 UP007
|
3
3
|
import abc
|
4
4
|
import contextlib
|
5
|
+
import enum
|
5
6
|
import logging
|
6
7
|
import selectors
|
7
8
|
import threading
|
8
9
|
import typing as ta
|
9
10
|
|
11
|
+
from ...lite.contextmanagers import ExitStacked
|
12
|
+
from ...lite.contextmanagers import defer
|
10
13
|
from ..addresses import SocketAndAddress
|
11
14
|
from ..bind import SocketBinder
|
12
15
|
from ..io import close_socket_immediately
|
@@ -76,58 +79,88 @@ class SocketServer(abc.ABC):
|
|
76
79
|
|
77
80
|
#
|
78
81
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
class PollResult(enum.Enum):
|
83
|
+
TIMEOUT = enum.auto()
|
84
|
+
CONNECTION = enum.auto()
|
85
|
+
ERROR = enum.auto()
|
86
|
+
SHUTDOWN = enum.auto()
|
84
87
|
|
85
|
-
|
88
|
+
class PollContext(ExitStacked, abc.ABC):
|
89
|
+
@abc.abstractmethod
|
90
|
+
def poll(self, timeout: ta.Optional[float] = None) -> 'SocketServer.PollResult':
|
91
|
+
raise NotImplementedError
|
86
92
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# polling. Polling reduces our responsiveness to a shutdown request and wastes cpu at all other times.
|
91
|
-
with self.Selector() as selector:
|
92
|
-
selector.register(self._binder.fileno(), selectors.EVENT_READ)
|
93
|
+
class _PollContext(PollContext):
|
94
|
+
def __init__(self, server: 'SocketServer') -> None:
|
95
|
+
super().__init__()
|
93
96
|
|
94
|
-
|
97
|
+
self._server = server
|
95
98
|
|
96
|
-
|
97
|
-
self._is_shutdown.set()
|
99
|
+
_selector: ta.Any = None
|
98
100
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
poll_interval = self._poll_interval
|
101
|
+
def _enter_contexts(self) -> None:
|
102
|
+
self._enter_context(self._server._lock) # noqa: SLF001
|
103
|
+
self._enter_context(self._server._binder) # noqa: SLF001
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
105
|
+
self._server._binder.listen() # noqa: SLF001
|
106
|
+
|
107
|
+
self._server._is_shutdown.clear() # noqa: SLF001
|
108
|
+
self._enter_context(defer(self._server._is_shutdown.set)) # noqa
|
109
|
+
|
110
|
+
# XXX: Consider using another file descriptor or connecting to the socket to wake this up instead of
|
111
|
+
# polling. Polling reduces our responsiveness to a shutdown request and wastes cpu at all other times.
|
112
|
+
self._selector = self._enter_context(self._server.Selector())
|
113
|
+
self._selector.register(self._server._binder.fileno(), selectors.EVENT_READ) # noqa: SLF001
|
108
114
|
|
109
|
-
|
110
|
-
|
111
|
-
|
115
|
+
def poll(self, timeout: ta.Optional[float] = None) -> 'SocketServer.PollResult':
|
116
|
+
if self._server._should_shutdown: # noqa: SLF001
|
117
|
+
return SocketServer.PollResult.SHUTDOWN
|
112
118
|
|
113
|
-
|
114
|
-
try:
|
115
|
-
conn = self._binder.accept()
|
119
|
+
ready = self._selector.select(timeout)
|
116
120
|
|
117
|
-
|
118
|
-
|
121
|
+
# bpo-35017: shutdown() called during select(), exit immediately.
|
122
|
+
if self._server._should_shutdown: # noqa: SLF001
|
123
|
+
return SocketServer.PollResult.SHUTDOWN # type: ignore[unreachable]
|
119
124
|
|
120
|
-
|
125
|
+
if not ready:
|
126
|
+
return SocketServer.PollResult.TIMEOUT
|
121
127
|
|
122
|
-
|
123
|
-
|
128
|
+
try:
|
129
|
+
conn = self._server._binder.accept() # noqa: SLF001
|
130
|
+
|
131
|
+
except OSError as exc:
|
132
|
+
self._server._handle_error(exc) # noqa: SLF001
|
124
133
|
|
125
|
-
|
126
|
-
self._handle_error(exc, conn)
|
134
|
+
return SocketServer.PollResult.ERROR
|
127
135
|
|
128
|
-
|
136
|
+
try:
|
137
|
+
self._server._handler(conn) # noqa: SLF001
|
129
138
|
|
130
|
-
|
139
|
+
except Exception as exc: # noqa
|
140
|
+
self._server._handle_error(exc, conn) # noqa: SLF001
|
141
|
+
|
142
|
+
close_socket_immediately(conn.socket)
|
143
|
+
|
144
|
+
return SocketServer.PollResult.CONNECTION
|
145
|
+
|
146
|
+
def poll_context(self) -> PollContext:
|
147
|
+
return self._PollContext(self)
|
148
|
+
|
149
|
+
#
|
150
|
+
|
151
|
+
@contextlib.contextmanager
|
152
|
+
def loop_context(self, poll_interval: ta.Optional[float] = None) -> ta.Iterator[ta.Iterator[bool]]:
|
153
|
+
if poll_interval is None:
|
154
|
+
poll_interval = self._poll_interval
|
155
|
+
|
156
|
+
with self.poll_context() as pc:
|
157
|
+
def loop():
|
158
|
+
while True:
|
159
|
+
res = pc.poll(poll_interval)
|
160
|
+
if res in (SocketServer.PollResult.ERROR, SocketServer.PollResult.SHUTDOWN):
|
161
|
+
return
|
162
|
+
else:
|
163
|
+
yield res == SocketServer.PollResult.CONNECTION
|
131
164
|
|
132
165
|
yield loop()
|
133
166
|
|
omlish/sockets/wait.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import socket
|
4
|
+
import threading
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from ..lite.timeouts import Timeout
|
8
|
+
from ..lite.timeouts import TimeoutLike
|
9
|
+
|
10
|
+
|
11
|
+
def socket_wait_until_can_connect(
|
12
|
+
address: ta.Any,
|
13
|
+
*,
|
14
|
+
timeout: ta.Optional[TimeoutLike] = None,
|
15
|
+
on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
|
16
|
+
sleep_s: float = .1,
|
17
|
+
exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (Exception,),
|
18
|
+
cancel_event: ta.Optional[threading.Event] = None,
|
19
|
+
) -> None:
|
20
|
+
timeout = Timeout.of(timeout)
|
21
|
+
|
22
|
+
if cancel_event is None:
|
23
|
+
cancel_event = threading.Event()
|
24
|
+
|
25
|
+
while not cancel_event.is_set():
|
26
|
+
timeout()
|
27
|
+
|
28
|
+
try:
|
29
|
+
conn = socket.create_connection(address, timeout=timeout.or_(None))
|
30
|
+
|
31
|
+
except exception as e: # noqa
|
32
|
+
if on_fail is not None:
|
33
|
+
on_fail(e)
|
34
|
+
|
35
|
+
else:
|
36
|
+
conn.close()
|
37
|
+
break
|
38
|
+
|
39
|
+
cancel_event.wait(min(sleep_s, timeout.remaining()))
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=3c_F9MxYda9rfa68tINgteoJ57COTQpld-0xkq1JRwY,3380
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
5
5
|
omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
|
@@ -101,6 +101,7 @@ omlish/asyncs/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
101
101
|
omlish/asyncs/asyncio/all.py,sha256=EksCHjRQKobiGrxuDW72IaH53WJMs7rdj_ZDBI3iKcg,315
|
102
102
|
omlish/asyncs/asyncio/asyncio.py,sha256=mDjYNm1cylUhQ8slWXwdPoXasuWfafjzu78GHt2Mdig,2437
|
103
103
|
omlish/asyncs/asyncio/channels.py,sha256=ZbmsEmdK1fV96liHdcVpRqA2dAMkXJt4Q3rFAg3YOIw,1074
|
104
|
+
omlish/asyncs/asyncio/sockets.py,sha256=Ni5O80fNAccSMAGrlSkZ923Nawxi3FHoXrKj4nr0xlU,1266
|
104
105
|
omlish/asyncs/asyncio/streams.py,sha256=Uc9PCWSfBqrK2kdVtfjjQU1eaYTWYmZm8QISDj2xiuw,1004
|
105
106
|
omlish/asyncs/asyncio/subprocesses.py,sha256=f30-wi-3n9R5dftm4CMrzp23EEa4GX283bORixm1_UU,6931
|
106
107
|
omlish/asyncs/asyncio/timeouts.py,sha256=hokhi7jZSAtBv0ME3qL1cO5eshNA9ViEH7BLafNCBpQ,454
|
@@ -398,11 +399,11 @@ omlish/iterators/iterators.py,sha256=ghI4dO6WPyyFOLTIIMaHQ_IOy2xXaFpGPqveZ5YGIBU
|
|
398
399
|
omlish/iterators/recipes.py,sha256=53mkexitMhkwXQZbL6DrhpT0WePQ_56uXd5Jaw3DfzI,467
|
399
400
|
omlish/iterators/tools.py,sha256=Pi4ybXytUXVZ3xwK89xpPImQfYYId9p1vIFQvVqVLqA,2551
|
400
401
|
omlish/iterators/unique.py,sha256=0jAX3kwzVfRNhe0Tmh7kVP_Q2WBIn8POo_O-rgFV0rQ,1390
|
401
|
-
omlish/lang/__init__.py,sha256=
|
402
|
+
omlish/lang/__init__.py,sha256=T_hx_ygi-IqmeDPQ-uto2U4ZuAUDs-agOSAsF6WWLVI,4193
|
402
403
|
omlish/lang/cached.py,sha256=tQaqMu1LID0q4NSTk5vPXsgxIBWSFAmjs5AhQoEHoCQ,7833
|
403
404
|
omlish/lang/clsdct.py,sha256=sJYadm-fwzti-gsi98knR5qQUxriBmOqQE_qz3RopNk,1743
|
404
405
|
omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
|
405
|
-
omlish/lang/contextmanagers.py,sha256=
|
406
|
+
omlish/lang/contextmanagers.py,sha256=UPH6daYwSP9cH5AfSVsJyEHk1UURMGhVPM5ZRhp_Hvw,7576
|
406
407
|
omlish/lang/datetimes.py,sha256=ehI_DhQRM-bDxAavnp470XcekbbXc4Gdw9y1KpHDJT0,223
|
407
408
|
omlish/lang/descriptors.py,sha256=mZ2h9zJ__MMpw8hByjRbAiONcwfVb6GD0btNnVi8C5w,6573
|
408
409
|
omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
|
@@ -434,7 +435,7 @@ omlish/lite/__init__.py,sha256=ISLhM4q0LR1XXTCaHdZOZxBRyIsoZqYm4u0bf1BPcVk,148
|
|
434
435
|
omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
|
435
436
|
omlish/lite/check.py,sha256=OLwtE2x6nlbGx4vS3Rda7zMHpgqzDSLJminTAX2lqLA,13529
|
436
437
|
omlish/lite/configs.py,sha256=Ev_19sbII67pTWzInYjYqa9VyTiZBvyjhZqyG8TtufE,908
|
437
|
-
omlish/lite/contextmanagers.py,sha256=
|
438
|
+
omlish/lite/contextmanagers.py,sha256=XSCwr9GpPBJxXR9Vr07M4A_BH3uLpZettyoSE5KqJu8,5566
|
438
439
|
omlish/lite/dataclasses.py,sha256=t1G5-xOuvE6o6w9RyqHzLT9wHD0HkqBh5P8HUZWxGzs,1912
|
439
440
|
omlish/lite/imports.py,sha256=o9WWrNrWg0hKeMvaj91giaovED_9VFanN2MyEHBGekY,1346
|
440
441
|
omlish/lite/inject.py,sha256=-tTsOqqef-Ix5Tgl2DP_JAsNWJQDFUptERl3lk14Uzs,29007
|
@@ -462,7 +463,7 @@ omlish/logs/filters.py,sha256=2noFRyBez3y519fpfsDSt1vo8wX-85b8sMXZi5o_xyE,208
|
|
462
463
|
omlish/logs/handlers.py,sha256=zgSnKQA5q9Fu7T0Nkd7twog9H1Wg9-bDCzz4_F1TOBo,319
|
463
464
|
omlish/logs/json.py,sha256=zyqMWpZY3lk4WRk4wgmataBomGX9S3iDsydiM1sS-lI,1366
|
464
465
|
omlish/logs/noisy.py,sha256=Ubc-eTH6ZbGYsLfUUi69JAotwuUwzb-SJBeGo_0dIZI,348
|
465
|
-
omlish/logs/protocol.py,sha256=
|
466
|
+
omlish/logs/protocol.py,sha256=dfAR0_5kLEAkx0nhuWBhWMTVjWjhEl2uL-MxejrW1lk,4732
|
466
467
|
omlish/logs/proxy.py,sha256=A-ROPUUAlF397qTbEqhel6YhQMstNuXL3Xmts7w9dAo,2347
|
467
468
|
omlish/logs/standard.py,sha256=FbKdF2Z4Na5i2TNwKn0avLJXyICe2JKsPufjvKCHGn0,3162
|
468
469
|
omlish/logs/timing.py,sha256=XrFUHIPT4EHDujLKbGs9fGFMmoM3NEP8xPRaESJr7bQ,1513
|
@@ -565,9 +566,10 @@ omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
|
|
565
566
|
omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
|
566
567
|
omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
|
567
568
|
omlish/sockets/ports.py,sha256=Wm4mRFFz5MdD8KbdaEfT1c4PbJnsuK_iyJlZJE_-8jo,1402
|
569
|
+
omlish/sockets/wait.py,sha256=FSHzLR66RESMwHCV-Bu_mZWNNskRGfEwvM9q1arapV0,1049
|
568
570
|
omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
569
571
|
omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
|
570
|
-
omlish/sockets/server/server.py,sha256=
|
572
|
+
omlish/sockets/server/server.py,sha256=FkaishIxJuU4it9tTI7wzlGqJYzFGXzDrd_HgV0jAmU,6253
|
571
573
|
omlish/sockets/server/ssl.py,sha256=VE0GpdA-gYsN2m9_uvfDwWmXtIbRQqJomVdpGJO8o2M,1061
|
572
574
|
omlish/sockets/server/threading.py,sha256=YmW3Ym_p5j_F4SIH9BgRHIObywjq1HS39j9CGWIcMAY,2856
|
573
575
|
omlish/specs/__init__.py,sha256=zZwF8yXTEkSstYtORkDhVLK-_hWU8WOJCuBpognb_NY,118
|
@@ -727,9 +729,9 @@ omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
|
|
727
729
|
omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
|
728
730
|
omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
|
729
731
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
730
|
-
omlish-0.0.0.
|
731
|
-
omlish-0.0.0.
|
732
|
-
omlish-0.0.0.
|
733
|
-
omlish-0.0.0.
|
734
|
-
omlish-0.0.0.
|
735
|
-
omlish-0.0.0.
|
732
|
+
omlish-0.0.0.dev243.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
733
|
+
omlish-0.0.0.dev243.dist-info/METADATA,sha256=dvum371m78Sb6B5v3K-C8hyOFzA8rEjIQUyy8GZH2G0,4176
|
734
|
+
omlish-0.0.0.dev243.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
735
|
+
omlish-0.0.0.dev243.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
736
|
+
omlish-0.0.0.dev243.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
737
|
+
omlish-0.0.0.dev243.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|