omlish 0.0.0.dev307__py3-none-any.whl → 0.0.0.dev309__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 CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev307'
2
- __revision__ = '7fd729bd16b72af663b113712ea301f150b2449d'
1
+ __version__ = '0.0.0.dev309'
2
+ __revision__ = '1304f03bec6edcdf467784eeceb7c0e00a7ec49c'
3
3
 
4
4
 
5
5
  #
@@ -50,11 +50,11 @@ class AnyioSubprocesses(AbstractAsyncSubprocesses):
50
50
  stderr = io.BytesIO()
51
51
  tg.start_soon(read_output, proc.stderr, stderr)
52
52
 
53
- if proc.stdin and run.input is not None:
54
- await proc.stdin.send(run.input)
55
- await proc.stdin.aclose()
53
+ if proc.stdin and run.input is not None:
54
+ await proc.stdin.send(run.input)
55
+ await proc.stdin.aclose()
56
56
 
57
- await proc.wait()
57
+ await proc.wait()
58
58
 
59
59
  if run.check and proc.returncode != 0:
60
60
  raise subprocess.CalledProcessError(
@@ -14,7 +14,7 @@ async def asyncio_wait_until_can_connect(
14
14
  host: ta.Any = None,
15
15
  port: ta.Any = None,
16
16
  *,
17
- timeout: ta.Optional[TimeoutLike] = None,
17
+ timeout: TimeoutLike = None,
18
18
  on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
19
19
  sleep_s: float = .1,
20
20
  exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (Exception,),
@@ -131,7 +131,7 @@ class AsyncioProcessCommunicator:
131
131
  async def communicate(
132
132
  self,
133
133
  input: ta.Any = None, # noqa
134
- timeout: ta.Optional[TimeoutLike] = None,
134
+ timeout: TimeoutLike = None,
135
135
  ) -> Communication:
136
136
  return await asyncio_maybe_timeout(self._communicate(input), timeout)
137
137
 
@@ -144,7 +144,7 @@ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
144
144
  self,
145
145
  proc: asyncio.subprocess.Process,
146
146
  input: ta.Any = None, # noqa
147
- timeout: ta.Optional[TimeoutLike] = None,
147
+ timeout: TimeoutLike = None,
148
148
  ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
149
149
  return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
150
150
 
@@ -155,7 +155,7 @@ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
155
155
  self,
156
156
  *cmd: str,
157
157
  shell: bool = False,
158
- timeout: ta.Optional[TimeoutLike] = None,
158
+ timeout: TimeoutLike = None,
159
159
  **kwargs: ta.Any,
160
160
  ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
161
161
  with self.prepare_and_wrap( *cmd, shell=shell, **kwargs) as (cmd, kwargs): # noqa
@@ -15,7 +15,7 @@ AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
15
15
 
16
16
  def asyncio_maybe_timeout(
17
17
  fut: AwaitableT,
18
- timeout: ta.Optional[TimeoutLike] = None,
18
+ timeout: TimeoutLike = None,
19
19
  ) -> AwaitableT:
20
20
  if timeout is not None:
21
21
  fut = asyncio.wait_for(fut, Timeout.of(timeout)()) # type: ignore
omlish/daemons/daemon.py CHANGED
@@ -109,7 +109,7 @@ class Daemon:
109
109
 
110
110
  def wait_sync(
111
111
  self,
112
- timeout: lang.TimeoutLike = lang.Timeout.Default,
112
+ timeout: lang.TimeoutLike = lang.Timeout.DEFAULT,
113
113
  *,
114
114
  max_tries: int | None = None,
115
115
  ) -> None:
@@ -140,7 +140,7 @@ class Daemon:
140
140
 
141
141
  return launcher.launch()
142
142
 
143
- def launch(self, timeout: lang.TimeoutLike = lang.Timeout.Default) -> None:
143
+ def launch(self, timeout: lang.TimeoutLike = lang.Timeout.DEFAULT) -> None:
144
144
  self.launch_no_wait()
145
145
 
146
146
  self.wait_sync(timeout)
@@ -32,6 +32,8 @@ TokenKind: ta.TypeAlias = ta.Literal[
32
32
  'HASH_UNDERSCORE',
33
33
  'META',
34
34
  'QUOTE',
35
+
36
+ 'SPACE',
35
37
  ]
36
38
 
37
39
 
@@ -88,7 +90,13 @@ class StreamLexError(Exception):
88
90
 
89
91
 
90
92
  class StreamLexer(GenMachine[str, Token]):
91
- def __init__(self) -> None:
93
+ def __init__(
94
+ self,
95
+ *,
96
+ include_space: bool = False,
97
+ ) -> None:
98
+ self._include_space = include_space
99
+
92
100
  self._ofs = 0
93
101
  self._line = 1
94
102
  self._col = 0
@@ -155,6 +163,8 @@ class StreamLexer(GenMachine[str, Token]):
155
163
  return None
156
164
 
157
165
  if c.isspace() or c == ',':
166
+ if self._include_space:
167
+ yield self._make_tok('SPACE', c, self.pos)
158
168
  continue
159
169
 
160
170
  if c in SINGLE_TOKENS:
@@ -303,23 +313,3 @@ class StreamLexer(GenMachine[str, Token]):
303
313
  src = self._flip_buf()
304
314
  yield self._make_tok('WORD', src, pos)
305
315
  return self._do_main(c)
306
-
307
-
308
- ##
309
-
310
-
311
- def test_lex():
312
- for s in [
313
- '"abc"',
314
- '{"a" "b"}',
315
- '1',
316
- '-1',
317
- '{a :b c 420}',
318
- '#{a}',
319
- ]:
320
- print(s)
321
- with StreamLexer() as lex:
322
- for c in [*s, '']:
323
- for t in lex(c):
324
- print(t)
325
- print()
@@ -178,10 +178,15 @@ class StreamParser(GenMachine[Token, ta.Any]):
178
178
  else:
179
179
  raise
180
180
 
181
- value: ta.Any
181
+ # ignored
182
+
183
+ if tok.kind in ('SPACE', 'COMMENT'):
184
+ continue
182
185
 
183
186
  # scalars
184
187
 
188
+ value: ta.Any
189
+
185
190
  if tok.kind == 'STRING':
186
191
  value = self._parse_string(tok)
187
192
 
@@ -197,9 +202,6 @@ class StreamParser(GenMachine[Token, ta.Any]):
197
202
  else:
198
203
  value = self._parse_word(tok)
199
204
 
200
- elif tok.kind == 'COMMENT':
201
- continue
202
-
203
205
  # open
204
206
 
205
207
  elif tok.kind == 'LPAREN':
@@ -324,8 +326,7 @@ class StreamParser(GenMachine[Token, ta.Any]):
324
326
 
325
327
  return self._char_maker(c)
326
328
 
327
- _INT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)')
328
- _BIGINT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)N')
329
+ _INT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)N?')
329
330
  _FLOAT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?(0|[1-9][0-9]*))?M?')
330
331
 
331
332
  def _parse_word(self, tok: Token) -> ta.Any:
@@ -345,10 +346,11 @@ class StreamParser(GenMachine[Token, ta.Any]):
345
346
  # 2r101010, 052, 8r52, 0x2a, 36r16, and 42 are all the same Long.
346
347
  # Floating point numbers are read as Doubles; with M suffix they are read as BigDecimals.
347
348
  # Ratios are supported, e.g. 22/7.
348
- return int(src)
349
+ if src.endswith('N'):
350
+ return int(src[:-1])
349
351
 
350
- elif self._BIGINT_PAT.fullmatch(src):
351
- return int(src[:-1])
352
+ else:
353
+ return int(src)
352
354
 
353
355
  elif self._FLOAT_PAT.fullmatch(src):
354
356
  return float(src)
@@ -376,25 +378,3 @@ def parse(src: str, **kwargs: ta.Any) -> ta.Any | None:
376
378
  if not values:
377
379
  return None
378
380
  return check.single(values)
379
-
380
-
381
- ##
382
-
383
-
384
- def test_parse():
385
- for s in [
386
- '"abc"',
387
- '"a\\bc"',
388
- '{"a" "b"}',
389
- '1',
390
- '-1',
391
- '{a :b c 420}',
392
- '#{a}',
393
- '(1 #_ 2 3)',
394
- '"foo\u1234bar"',
395
- '\\x',
396
- '\\u1234',
397
- ]:
398
- print(s)
399
- print(parse(s))
400
- print()
@@ -40,7 +40,14 @@ ControlTokenKind: ta.TypeAlias = ta.Literal[
40
40
  'COLON',
41
41
  ]
42
42
 
43
- TokenKind: ta.TypeAlias = ValueTokenKind | ControlTokenKind
43
+ SpaceTokenKind: ta.TypeAlias = ta.Literal['SPACE']
44
+
45
+ TokenKind: ta.TypeAlias = ta.Union[ # noqa
46
+ ValueTokenKind,
47
+ ControlTokenKind,
48
+ SpaceTokenKind,
49
+ ]
50
+
44
51
 
45
52
  #
46
53
 
@@ -109,8 +116,10 @@ class JsonStreamLexer(GenMachine[str, Token]):
109
116
  self,
110
117
  *,
111
118
  include_raw: bool = False,
119
+ include_space: bool = False,
112
120
  ) -> None:
113
121
  self._include_raw = include_raw
122
+ self._include_space = include_space
114
123
 
115
124
  self._ofs = 0
116
125
  self._line = 1
@@ -174,6 +183,8 @@ class JsonStreamLexer(GenMachine[str, Token]):
174
183
  return None
175
184
 
176
185
  if c.isspace():
186
+ if self._include_space:
187
+ yield self._make_tok('SPACE', c, c, self.pos)
177
188
  continue
178
189
 
179
190
  if c in CONTROL_TOKENS:
@@ -282,7 +293,11 @@ class JsonStreamLexer(GenMachine[str, Token]):
282
293
  if c in CONTROL_TOKENS:
283
294
  yield self._make_tok(CONTROL_TOKENS[c], c, c, pos)
284
295
 
285
- elif not c.isspace():
296
+ elif c.isspace():
297
+ if self._include_space:
298
+ yield self._make_tok('SPACE', c, c, self.pos)
299
+
300
+ else:
286
301
  self._raise(f'Unexpected character after number: {c}')
287
302
 
288
303
  return self._do_main()
@@ -102,6 +102,15 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
102
102
 
103
103
  #
104
104
 
105
+ def _next_tok(self):
106
+ while True:
107
+ tok = yield None
108
+
109
+ if tok.kind != 'SPACE':
110
+ return tok
111
+
112
+ #
113
+
105
114
  def _emit_event(self, v):
106
115
  if not self._stack:
107
116
  return ((v,), self._do_value())
@@ -129,7 +138,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
129
138
 
130
139
  def _do_value(self, *, must_be_present: bool = False):
131
140
  try:
132
- tok = yield None
141
+ tok = yield from self._next_tok()
133
142
  except GeneratorExit:
134
143
  if self._stack:
135
144
  raise JsonStreamParseError('Expected value') from None
@@ -180,7 +189,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
180
189
 
181
190
  def _do_object_body(self, *, must_be_present: bool = False):
182
191
  try:
183
- tok = yield None
192
+ tok = yield from self._next_tok()
184
193
  except GeneratorExit:
185
194
  raise JsonStreamParseError('Expected object body') from None
186
195
 
@@ -188,7 +197,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
188
197
  k = tok.value
189
198
 
190
199
  try:
191
- tok = yield None
200
+ tok = yield from self._next_tok()
192
201
  except GeneratorExit:
193
202
  raise JsonStreamParseError('Expected key') from None
194
203
  if tok.kind != 'COLON':
@@ -211,7 +220,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
211
220
 
212
221
  def _do_after_pair(self):
213
222
  try:
214
- tok = yield None
223
+ tok = yield from self._next_tok()
215
224
  except GeneratorExit:
216
225
  raise JsonStreamParseError('Expected continuation') from None
217
226
 
@@ -244,7 +253,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
244
253
 
245
254
  def _do_after_element(self):
246
255
  try:
247
- tok = yield None
256
+ tok = yield from self._next_tok()
248
257
  except GeneratorExit:
249
258
  raise JsonStreamParseError('Expected continuation') from None
250
259
 
omlish/lang/__init__.py CHANGED
@@ -184,12 +184,11 @@ from .functions import ( # noqa
184
184
  )
185
185
 
186
186
  from .generators import ( # noqa
187
- CoroutineGenerator,
188
- Generator,
189
187
  GeneratorLike,
190
188
  GeneratorMappedIterator,
191
189
  autostart,
192
- corogen,
190
+ capture_coroutine,
191
+ capture_generator,
193
192
  genmap,
194
193
  nextgen,
195
194
  )
omlish/lang/generators.py CHANGED
@@ -2,6 +2,7 @@ import abc
2
2
  import functools
3
3
  import typing as ta
4
4
 
5
+ from .classes.restrict import Abstract
5
6
  from .maybes import Maybe
6
7
  from .maybes import empty
7
8
  from .maybes import just
@@ -79,15 +80,29 @@ def adapt_generator_like(gl):
79
80
  ##
80
81
 
81
82
 
82
- class Generator(ta.Generator[O, I, R]):
83
+ class AbstractGeneratorCapture(Abstract, ta.Generic[O, I, R]):
83
84
  def __init__(self, g: ta.Generator[O, I, R]) -> None:
84
85
  super().__init__()
86
+
85
87
  self._g = g
86
88
 
87
89
  @property
88
90
  def g(self) -> ta.Generator[O, I, R]:
89
91
  return self._g
90
92
 
93
+ #
94
+
95
+ def close(self) -> None:
96
+ self._g.close()
97
+
98
+ def __enter__(self) -> ta.Self:
99
+ return self
100
+
101
+ def __exit__(self, exc_type, exc_val, exc_tb):
102
+ self._g.close()
103
+
104
+
105
+ class GeneratorCapture(AbstractGeneratorCapture[O, I, R], ta.Generator[O, I, R]):
91
106
  value: R
92
107
 
93
108
  def __iter__(self):
@@ -114,35 +129,11 @@ class Generator(ta.Generator[O, I, R]):
114
129
  self.value = e.value
115
130
  raise
116
131
 
117
- def close(self):
118
- self._g.close()
119
132
 
133
+ capture_generator = GeneratorCapture
120
134
 
121
- ##
122
-
123
-
124
- class CoroutineGenerator(ta.Generic[O, I, R]):
125
- def __init__(self, g: ta.Generator[O, I, R]) -> None:
126
- super().__init__()
127
- self._g = g
128
-
129
- @property
130
- def g(self) -> ta.Generator[O, I, R]:
131
- return self._g
132
-
133
- #
134
-
135
- def close(self) -> None:
136
- self._g.close()
137
-
138
- def __enter__(self) -> ta.Self:
139
- return self
140
-
141
- def __exit__(self, exc_type, exc_val, exc_tb):
142
- self._g.close()
143
-
144
- #
145
135
 
136
+ class CoroutineGeneratorCapture(AbstractGeneratorCapture[O, I, R]):
146
137
  class Output(ta.NamedTuple, ta.Generic[T]):
147
138
  v: T
148
139
 
@@ -192,7 +183,7 @@ class CoroutineGenerator(ta.Generic[O, I, R]):
192
183
  return self.Yield(o)
193
184
 
194
185
 
195
- corogen = CoroutineGenerator
186
+ capture_coroutine = CoroutineGeneratorCapture
196
187
 
197
188
 
198
189
  ##
omlish/lite/timeouts.py CHANGED
@@ -8,7 +8,7 @@ import time
8
8
  import typing as ta
9
9
 
10
10
 
11
- TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.Default'], ta.Iterable['TimeoutLike'], float] # ta.TypeAlias
11
+ TimeoutLike = ta.Union['Timeout', ta.Type['Timeout.DEFAULT'], ta.Iterable['TimeoutLike'], float, None] # ta.TypeAlias
12
12
 
13
13
 
14
14
  ##
@@ -54,7 +54,7 @@ class Timeout(abc.ABC):
54
54
 
55
55
  #
56
56
 
57
- class Default:
57
+ class DEFAULT: # Noqa
58
58
  def __new__(cls, *args, **kwargs): # noqa
59
59
  raise TypeError
60
60
 
@@ -65,7 +65,7 @@ class Timeout(abc.ABC):
65
65
  @classmethod
66
66
  def of(
67
67
  cls,
68
- obj: ta.Optional[TimeoutLike],
68
+ obj: TimeoutLike,
69
69
  default: ta.Union[TimeoutLike, ta.Type[_NOT_SPECIFIED]] = _NOT_SPECIFIED,
70
70
  ) -> 'Timeout':
71
71
  if obj is None:
@@ -80,8 +80,8 @@ class Timeout(abc.ABC):
80
80
  elif isinstance(obj, ta.Iterable):
81
81
  return CompositeTimeout(*[Timeout.of(c) for c in obj])
82
82
 
83
- elif obj is Timeout.Default:
84
- if default is Timeout._NOT_SPECIFIED or default is Timeout.Default:
83
+ elif obj is Timeout.DEFAULT:
84
+ if default is Timeout._NOT_SPECIFIED or default is Timeout.DEFAULT:
85
85
  raise RuntimeError('Must specify a default timeout')
86
86
 
87
87
  else:
@@ -64,7 +64,7 @@ class PidfilePinner(abc.ABC):
64
64
  self,
65
65
  path: str,
66
66
  *,
67
- timeout: ta.Optional[TimeoutLike] = None,
67
+ timeout: TimeoutLike = None,
68
68
  inheritable: bool = False, # Present to match Pidfile kwargs for convenience, but enforced to be False.
69
69
  **kwargs: ta.Any,
70
70
  ) -> ta.Iterator[int]:
omlish/sockets/ports.py CHANGED
@@ -38,7 +38,7 @@ def get_available_ports(
38
38
  *,
39
39
  host: ta.Optional[str] = None,
40
40
  exclude: ta.Optional[ta.Iterable[int]] = None,
41
- timeout: ta.Optional[TimeoutLike] = None,
41
+ timeout: TimeoutLike = None,
42
42
  ) -> ta.List[int]:
43
43
  exclude = set(exclude or [])
44
44
 
omlish/sockets/wait.py CHANGED
@@ -14,7 +14,7 @@ from ..lite.timeouts import TimeoutLike
14
14
  def socket_can_connect(
15
15
  address: ta.Any,
16
16
  *,
17
- timeout: ta.Optional[TimeoutLike] = None,
17
+ timeout: TimeoutLike = None,
18
18
  on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
19
19
  exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (ConnectionRefusedError,),
20
20
  ) -> bool:
@@ -36,7 +36,7 @@ def socket_can_connect(
36
36
  def socket_wait_until_can_connect(
37
37
  address: ta.Any,
38
38
  *,
39
- timeout: ta.Optional[TimeoutLike] = None,
39
+ timeout: TimeoutLike = None,
40
40
  on_fail: ta.Optional[ta.Callable[[BaseException], None]] = None,
41
41
  sleep_s: float = .1,
42
42
  exception: ta.Union[ta.Type[BaseException], ta.Tuple[ta.Type[BaseException], ...]] = (ConnectionRefusedError,),
@@ -16,6 +16,7 @@ from .types import ( # noqa
16
16
 
17
17
  NotSpecified,
18
18
  is_not_specified,
19
+ check_not_not_specified,
19
20
 
20
21
  Request,
21
22
  request,
@@ -0,0 +1,236 @@
1
+ import builtins
2
+ import functools
3
+ import json
4
+ import typing as ta
5
+ import uuid
6
+
7
+ import anyio.abc
8
+
9
+ from ... import check
10
+ from ... import lang
11
+ from ... import marshal as msh
12
+ from ...asyncs import anyio as aiu
13
+ from ...io.buffers import DelimitingBuffer
14
+ from .types import Error
15
+ from .types import Id
16
+ from .types import Message
17
+ from .types import NotSpecified
18
+ from .types import Object
19
+ from .types import Request
20
+ from .types import Response
21
+ from .types import detect_message_type
22
+ from .types import notification
23
+ from .types import request
24
+
25
+
26
+ ##
27
+
28
+
29
+ @lang.cached_function
30
+ def _create_id() -> str:
31
+ return str(uuid.uuid4())
32
+
33
+
34
+ class JsonrpcConnection:
35
+ def __init__(
36
+ self,
37
+ tg: anyio.abc.TaskGroup,
38
+ stream: anyio.abc.ByteStream,
39
+ *,
40
+ request_handler: ta.Callable[['JsonrpcConnection', Request], ta.Awaitable[None]] | None = None,
41
+ notification_handler: ta.Callable[['JsonrpcConnection', Request], ta.Awaitable[None]] | None = None,
42
+ default_timeout: float | None = 30.,
43
+ ) -> None:
44
+ super().__init__()
45
+
46
+ self._tg = tg
47
+ self._stream = stream
48
+ self._request_handler = request_handler
49
+ self._notification_handler = notification_handler
50
+ self._default_timeout = default_timeout
51
+
52
+ self._buf = DelimitingBuffer(b'\n')
53
+ self._response_futures_by_id: dict[Id, aiu.Future[Response]] = {}
54
+ self._send_lock = anyio.Lock()
55
+ self._shutdown_event = anyio.Event()
56
+ self._received_eof = False
57
+
58
+ #
59
+
60
+ class Error(Exception):
61
+ """Base class for JSON-RPC related errors."""
62
+
63
+ class TimeoutError(Error, builtins.TimeoutError): # noqa
64
+ """Raised when a request times out."""
65
+
66
+ class ConnectionError(Error, builtins.ConnectionError): # noqa
67
+ """Raised when there are connection-related issues."""
68
+
69
+ class ProtocolError(Error):
70
+ """Raised when there are protocol-related issues."""
71
+
72
+ #
73
+
74
+ async def __aenter__(self) -> 'JsonrpcConnection':
75
+ await self._tg.start(self._receive_loop)
76
+ return self
77
+
78
+ async def __aexit__(self, exc_type: type[BaseException] | None, *_: object) -> None:
79
+ self._shutdown_event.set()
80
+
81
+ ##
82
+
83
+ async def _handle_message(self, msg: Message) -> None:
84
+ if isinstance(msg, Response):
85
+ msg_id = msg.id
86
+ try:
87
+ resp_fut = self._response_futures_by_id[msg_id]
88
+ except KeyError:
89
+ raise NotImplementedError from None
90
+ resp_fut.set_value(msg)
91
+
92
+ elif isinstance(msg, Request):
93
+ if msg.is_notification:
94
+ if (mh := self._notification_handler) is not None:
95
+ await mh(self, msg)
96
+
97
+ else: # noqa
98
+ if (rh := self._request_handler) is not None:
99
+ await rh(self, msg)
100
+
101
+ else:
102
+ raise TypeError(msg)
103
+
104
+ #
105
+
106
+ CLOSED_EXCEPTIONS: ta.ClassVar[tuple[type[Exception], ...]] = (
107
+ anyio.ClosedResourceError,
108
+ anyio.EndOfStream,
109
+ )
110
+
111
+ ERROR_EXCEPTIONS: ta.ClassVar[tuple[type[Exception], ...]] = (
112
+ OSError,
113
+ anyio.BrokenResourceError,
114
+ )
115
+
116
+ async def _receive_message_batch(self) -> list[Message] | None:
117
+ check.state(not self._received_eof)
118
+
119
+ if self._shutdown_event.is_set():
120
+ return None
121
+
122
+ while True:
123
+ maybe_shutdown: lang.Maybe[bool]
124
+ maybe_data: lang.Maybe[lang.Outcome[bytes]]
125
+ (
126
+ maybe_shutdown,
127
+ maybe_data,
128
+ ) = await aiu.gather( # type: ignore
129
+ self._shutdown_event.wait,
130
+ functools.partial(lang.acapture, self._stream.receive),
131
+ take_first=True,
132
+ )
133
+
134
+ if self._shutdown_event.is_set():
135
+ return None
136
+
137
+ try:
138
+ data = maybe_data.must().unwrap()
139
+ except self.CLOSED_EXCEPTIONS:
140
+ data = b''
141
+ except self.ERROR_EXCEPTIONS as e:
142
+ raise JsonrpcConnection.ConnectionError('Failed to receive message') from e
143
+
144
+ if not data:
145
+ self._received_eof = True
146
+
147
+ lines = list(self._buf.feed(data))
148
+ if lines:
149
+ break
150
+
151
+ if not data:
152
+ return None
153
+
154
+ msgs: list[Message] = []
155
+ for line in lines:
156
+ if isinstance(line, DelimitingBuffer.Incomplete):
157
+ raise ConnectionError('Received incomplete message')
158
+
159
+ try:
160
+ dct = json.loads(line.decode('utf-8'))
161
+ except (UnicodeDecodeError, json.JSONDecodeError) as e:
162
+ raise JsonrpcConnection.ProtocolError from e
163
+
164
+ mcls = detect_message_type(dct)
165
+ try:
166
+ msg = msh.unmarshal(dct, mcls)
167
+ except Exception as e:
168
+ raise JsonrpcConnection.ProtocolError from e
169
+
170
+ msgs.append(msg)
171
+
172
+ return msgs
173
+
174
+ async def _receive_loop(
175
+ self,
176
+ *,
177
+ task_status: anyio.abc.TaskStatus[ta.Any] = anyio.TASK_STATUS_IGNORED,
178
+ ) -> None:
179
+ task_status.started()
180
+
181
+ while not self._shutdown_event.is_set():
182
+ msgs = await self._receive_message_batch()
183
+ if msgs is None:
184
+ break
185
+
186
+ for msg in msgs:
187
+ await self._handle_message(msg)
188
+
189
+ ##
190
+
191
+ async def send_message(self, msg: Message) -> None:
192
+ async with self._send_lock:
193
+ try:
194
+ await self._stream.send(json.dumps(msh.marshal(msg)).encode() + b'\n')
195
+ except self.ERROR_EXCEPTIONS as e:
196
+ raise ConnectionError('Failed to send message') from e
197
+
198
+ #
199
+
200
+ async def request(
201
+ self,
202
+ method: str,
203
+ params: Object | None = None,
204
+ *,
205
+ timeout: lang.TimeoutLike | None = lang.Timeout.DEFAULT,
206
+ ) -> ta.Any:
207
+ msg_id = _create_id()
208
+ req = request(msg_id, method, params)
209
+
210
+ fut = aiu.create_future[Response]()
211
+ self._response_futures_by_id[msg_id] = fut
212
+
213
+ try:
214
+ await self.send_message(req)
215
+
216
+ timeout_val = lang.Timeout.of(timeout, self._default_timeout)
217
+ try:
218
+ with anyio.fail_after(timeout_val.or_(None)):
219
+ await fut
220
+ except TimeoutError as e:
221
+ raise JsonrpcConnection.TimeoutError(f'Request timed out after {timeout_val} seconds') from e
222
+
223
+ response = fut.outcome.must().unwrap()
224
+
225
+ if response.error is not NotSpecified:
226
+ error = ta.cast(Error, response.error)
227
+ raise JsonrpcConnection.Error(f'Error {error.code}: {error.message}')
228
+
229
+ return response.result
230
+
231
+ finally:
232
+ self._response_futures_by_id.pop(msg_id, None) # noqa
233
+
234
+ async def notify(self, method: str, params: Object | None = None) -> None:
235
+ msg = notification(method, params)
236
+ await self.send_message(msg)
@@ -18,6 +18,8 @@ from ... import lang
18
18
  from ... import marshal as msh
19
19
 
20
20
 
21
+ T = ta.TypeVar('T')
22
+
21
23
  NUMBER_TYPES: tuple[type, ...] = (int, float)
22
24
  Number: ta.TypeAlias = int | float
23
25
 
@@ -44,6 +46,11 @@ def is_not_specified(v: ta.Any) -> bool:
44
46
  return v is NotSpecified
45
47
 
46
48
 
49
+ def check_not_not_specified(v: T | type[NotSpecified]) -> T:
50
+ check.arg(not is_not_specified(v))
51
+ return ta.cast(T, v)
52
+
53
+
47
54
  ##
48
55
 
49
56
 
@@ -87,10 +94,22 @@ class Response(lang.Final):
87
94
 
88
95
  _: dc.KW_ONLY
89
96
 
97
+ #
98
+
90
99
  result: ta.Any = dc.field(default=NotSpecified)
91
100
  error: ta.Union['Error', type[NotSpecified]] = dc.field(default=NotSpecified)
92
101
  dc.validate(lambda self: is_not_specified(self.result) ^ is_not_specified(self.error))
93
102
 
103
+ @property
104
+ def is_result(self) -> bool:
105
+ return not is_not_specified(self.result)
106
+
107
+ @property
108
+ def is_error(self) -> bool:
109
+ return not is_not_specified(self.error)
110
+
111
+ #
112
+
94
113
  jsonrpc: str = dc.field(default=VERSION)
95
114
  dc.validate(lambda self: self.jsonrpc == VERSION)
96
115
 
@@ -22,7 +22,7 @@ class AbstractAsyncSubprocesses(BaseSubprocesses):
22
22
  self,
23
23
  *cmd: str,
24
24
  input: ta.Any = None, # noqa
25
- timeout: ta.Optional[TimeoutLike] = None,
25
+ timeout: TimeoutLike = None,
26
26
  check: bool = False,
27
27
  capture_output: ta.Optional[bool] = None,
28
28
  **kwargs: ta.Any,
@@ -31,7 +31,7 @@ class SubprocessRunOutput(ta.Generic[T]):
31
31
  class SubprocessRun:
32
32
  cmd: ta.Sequence[str]
33
33
  input: ta.Any = None
34
- timeout: ta.Optional[TimeoutLike] = None
34
+ timeout: TimeoutLike = None
35
35
  check: bool = False
36
36
  capture_output: ta.Optional[bool] = None
37
37
  kwargs: ta.Optional[ta.Mapping[str, ta.Any]] = None
@@ -67,7 +67,7 @@ class SubprocessRun:
67
67
  cls,
68
68
  *cmd: str,
69
69
  input: ta.Any = None, # noqa
70
- timeout: ta.Optional[TimeoutLike] = None,
70
+ timeout: TimeoutLike = None,
71
71
  check: bool = False, # noqa
72
72
  capture_output: ta.Optional[bool] = None,
73
73
  **kwargs: ta.Any,
@@ -28,7 +28,7 @@ class AbstractSubprocesses(BaseSubprocesses, abc.ABC):
28
28
  self,
29
29
  *cmd: str,
30
30
  input: ta.Any = None, # noqa
31
- timeout: ta.Optional[TimeoutLike] = None,
31
+ timeout: TimeoutLike = None,
32
32
  check: bool = False,
33
33
  capture_output: ta.Optional[bool] = None,
34
34
  **kwargs: ta.Any,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev307
3
+ Version: 0.0.0.dev309
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=orgsRvtpHu8tdhaCvlP9v3P495OJopYYiHKjK68WtWg,8587
2
- omlish/__about__.py,sha256=YdK-L7JBeMEmm3Dl7hWjoFj3BouojWQPvOl_zSLUgMA,3478
2
+ omlish/__about__.py,sha256=AMalr2v0ecBESsAeIHXFu3Gev9RWMipfQsSGjc671qA,3478
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -102,16 +102,16 @@ omlish/asyncs/anyio/backends.py,sha256=jJIymWoiedaEJJm82gvKiJ41EWLQZ-bcyNHpbDpKK
102
102
  omlish/asyncs/anyio/futures.py,sha256=Nm1gLerZEnHk-rlsmr0UfK168IWIK6zA8EebZFtoY_E,2052
103
103
  omlish/asyncs/anyio/signals.py,sha256=ySSut5prdnoy0-5Ws5V1M4cC2ON_vY550vU10d2NHk8,893
104
104
  omlish/asyncs/anyio/streams.py,sha256=Zum2qd1t3EiH6yzGWFwxFw79m-IH2VY5sTUTiluFfIY,2164
105
- omlish/asyncs/anyio/subprocesses.py,sha256=jjMjlcwtIiy-_y-spPn3eTC5dzrqFNSAMTPaIcXH9S8,3002
105
+ omlish/asyncs/anyio/subprocesses.py,sha256=nyl1A9z3rymxQMvIekWHU3IAiKBu1CcEqm-Ag1cGRPY,3018
106
106
  omlish/asyncs/anyio/sync.py,sha256=ZmSNhSsEkPwlXThrpefhtVTxw4GJ9F0P-yKyo5vbbSk,1574
107
107
  omlish/asyncs/anyio/utils.py,sha256=X2Rz1DGrCJ0zkt1O5cHoMRaYKTPndBj6dzLhb09mVtE,1672
108
108
  omlish/asyncs/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  omlish/asyncs/asyncio/all.py,sha256=u2JpMEs-0AJ0Vd8yU10HvWD8rfKxdFfMiwBu2oDeuuQ,313
110
110
  omlish/asyncs/asyncio/channels.py,sha256=X3S951YTjTRDguMSQRlfu74mPuWkNd2ZEUWboLY58-M,1079
111
- omlish/asyncs/asyncio/sockets.py,sha256=oZAPeC545MGeSpVj_uQfy-BbzXsXHesjCkJSiuqKmAI,1271
111
+ omlish/asyncs/asyncio/sockets.py,sha256=QdXWswN9p9jOlaXUSYnCQZtwu1_jSQNWBiKPt6bws6o,1258
112
112
  omlish/asyncs/asyncio/streams.py,sha256=J_d1hgX4Mx9SfyW4DjOzh91PqzZmjOtiIB95ytF8Ygw,1009
113
- omlish/asyncs/asyncio/subprocesses.py,sha256=Ob5oiyue-YYpvQktzI54QpGsl8E0xEKFOVcb_atJ_R8,6935
114
- omlish/asyncs/asyncio/timeouts.py,sha256=LwFx93KSrefBobQoK4-yH5B6M-pbd7NdNksNzLBfLgQ,459
113
+ omlish/asyncs/asyncio/subprocesses.py,sha256=Mvy5cXHbY-JB5Yl57G05c2O7aEMAjSe29Ed_J63pvx0,6896
114
+ omlish/asyncs/asyncio/timeouts.py,sha256=c-DhZi0yFXhJ6NtHa4EhB1Vrr1QDVd-7HBRkr96Q2So,446
115
115
  omlish/asyncs/asyncio/utils.py,sha256=mDjYNm1cylUhQ8slWXwdPoXasuWfafjzu78GHt2Mdig,2437
116
116
  omlish/asyncs/bluelet/LICENSE,sha256=VHf3oPQihOHnWyIR8LcXX0dpONa1lgyJnjWC2qVuRR0,559
117
117
  omlish/asyncs/bluelet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -195,7 +195,7 @@ omlish/configs/processing/names.py,sha256=weHmaTclzgM9lUn3aBtw-kwZ3mc2N-CZlFg3Kd
195
195
  omlish/configs/processing/rewriting.py,sha256=v7PfHtuTn5v_5Y6Au7oMN2Z0nxAMy1iYyO5CXnTvZhs,4226
196
196
  omlish/configs/processing/strings.py,sha256=qFS2oh6z02IaM_q4lTKLdufzkJqAJ6J-Qjrz5S-QJoM,826
197
197
  omlish/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
- omlish/daemons/daemon.py,sha256=3Wkvu8M_EaCKSpKI5UN5OayRXV0oVdF62tBss9_hlr0,3479
198
+ omlish/daemons/daemon.py,sha256=pUwomapIrUwXLnov9jlA5EU1vp4qEYcIiUxKD3SQyVo,3479
199
199
  omlish/daemons/launching.py,sha256=sNOYW939IGI4ZlLQ0bKxzXj6EyeOiwV7Upqhd5XfoHc,3747
200
200
  omlish/daemons/reparent.py,sha256=7uJ9oPGt9Ud7uA8bDl_SHcuqjcsmXa3kkjp9jf29wOw,585
201
201
  omlish/daemons/services.py,sha256=YYp2SMkJ71WgzOcYSXjWHeAyKKxu3j1dfuJvWkl0Dgw,3492
@@ -321,8 +321,8 @@ omlish/formats/xml.py,sha256=VJfqHR60dhAtjeG8WXFMozFqesTBSGvv264d67eDFXc,3514
321
321
  omlish/formats/yaml.py,sha256=jGPQlTE0vSV-p0O7TJRNlf6o1uq4gx8PrHZe1ApJ_o8,7386
322
322
  omlish/formats/edn/__init__.py,sha256=H3q5B-dibXvQV8pmuWizTo6Xk75M7M0M7VPCLt86rpo,195
323
323
  omlish/formats/edn/codec.py,sha256=k6-Ra3P3Rlv6JA69-jPLI4nCe5XVes_QJbcsj5DYzMM,454
324
- omlish/formats/edn/lexing.py,sha256=plwbFwHLOmrr5_QhmzvMTmTK55Ot0DxRgKY6ASSl_-Y,6966
325
- omlish/formats/edn/parsing.py,sha256=rtoOnDo8TuTo_GAhrNoVHu7Ys7CboALdgOF2PevAJYk,10152
324
+ omlish/formats/edn/lexing.py,sha256=LaIaGql9NtRlgi6bs4XhZ-wtabiUs99PoYN7_yAKMNE,6892
325
+ omlish/formats/edn/parsing.py,sha256=Y1CBxtdlbnn5zwU5nlxX3uw17u2POST8tabtVV1q0X4,9835
326
326
  omlish/formats/edn/values.py,sha256=jf0g88KJIMALxcuH51SoaMWg1HqTUqc1ugldmyyXWoc,3707
327
327
  omlish/formats/ini/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
328
  omlish/formats/ini/codec.py,sha256=omuFg0kiDksv8rRlWd_v32ebzEcKlgmiPgGID3bRi2M,631
@@ -345,8 +345,8 @@ omlish/formats/json/backends/ujson.py,sha256=BNJCU4kluGHdqTUKLJEuHhE2m2TmqR7HEN2
345
345
  omlish/formats/json/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
346
346
  omlish/formats/json/stream/building.py,sha256=SGbExmaerqOEiNSom2AERlpyXTj4dpM0QbMW-2WWM2o,2550
347
347
  omlish/formats/json/stream/errors.py,sha256=c8M8UAYmIZ-vWZLeKD2jMj4EDCJbr9QR8Jq_DyHjujQ,43
348
- omlish/formats/json/stream/lexing.py,sha256=ItsWvtl5SZH-HwQtPy8Cpf4nszqDzvUTdIOEmSRiZ-E,6807
349
- omlish/formats/json/stream/parsing.py,sha256=yvH5Llql3Ri_1xDhi1s9CKL6XLJVSQ8vYJ_dz3KAX-4,6223
348
+ omlish/formats/json/stream/lexing.py,sha256=0XbkpKm4rmn5DLRyqnVpyvBXMi_MjVEI1k6-TIgQWRM,7208
349
+ omlish/formats/json/stream/parsing.py,sha256=AXsg0N5s0jEvdfEQrzprplZDNkB62l6Q3Pv8rIx3984,6454
350
350
  omlish/formats/json/stream/rendering.py,sha256=uuJc__MR0G5kypYMAAudBNjBfiIzA_GGli-DWT90428,3730
351
351
  omlish/formats/json/stream/utils.py,sha256=UhBRuWbb25wrdQWl8Ttq7xGRLoa329TvNdecGCZxgzg,1197
352
352
  omlish/formats/json5/Json5.g4,sha256=ZUmgJPvj8lSMUD_v3wijp10ZQExYB5mu5Q089dYEJSU,2389
@@ -470,7 +470,7 @@ omlish/iterators/iterators.py,sha256=RxW35yQ5ed8vBQ22IqpDXFx-i5JiLQdp7-pkMZXhJJ8
470
470
  omlish/iterators/recipes.py,sha256=wOwOZg-zWG9Zc3wcAxJFSe2rtavVBYwZOfG09qYEx_4,472
471
471
  omlish/iterators/tools.py,sha256=c4hArZEVV8y9_dFfmRwakusv1cWJLT4MkTkGRjnGN5U,2556
472
472
  omlish/iterators/unique.py,sha256=Nw0pSaNEcHAkve0ugfLPvJcirDOn9ECyC5wIL8JlJKI,1395
473
- omlish/lang/__init__.py,sha256=ldzz3bIIkYhUd-Xmzn62NTOJ6av09vWfJSudkQJvz3M,5862
473
+ omlish/lang/__init__.py,sha256=yJvuoESt6-nZckXYcHctZYEnHCRCjQ2SkRV3ctUljPI,5856
474
474
  omlish/lang/attrs.py,sha256=i7euRF81uNF8QDmUVXSK_BtqLGshaMi4VVdUnMjiMwg,5050
475
475
  omlish/lang/casing.py,sha256=cFUlbDdXLhwnWwcYx4qnM5c4zGX7hIRUfcjiZbxUD28,4636
476
476
  omlish/lang/clsdct.py,sha256=HAGIvBSbCefzRjXriwYSBLO7QHKRv2UsE78jixOb-fA,1828
@@ -481,7 +481,7 @@ omlish/lang/datetimes.py,sha256=mrTtA67JYpfQwSlzdPcBtvm6dAyYM_dXNnlxFwFQH0M,228
481
481
  omlish/lang/descriptors.py,sha256=zBtgO9LjdSTGHNUgiIqswh78WOVoGH6KzS0NbgB1Wls,6572
482
482
  omlish/lang/enums.py,sha256=F9tflHfaAoV2MpyuhZzpfX9-H55M3zNa9hCszsngEo8,111
483
483
  omlish/lang/functions.py,sha256=51CoKtH_-CXUsKvtCexaR3OLZOtIwSdv4f4DtGBZdpA,6029
484
- omlish/lang/generators.py,sha256=5tbjVAywiZH6oAdj1sJLRMtIkC9y3rAkecLT7Z3m7_g,5251
484
+ omlish/lang/generators.py,sha256=yd3ebG2LlA3XQImP8rplt6tSToXwMxhlGRGCkdJ87ME,5230
485
485
  omlish/lang/imports.py,sha256=y9W9Y-d_cQ35QCLuSIPoa6vnEqSErFCz8b-34IH128U,10552
486
486
  omlish/lang/iterables.py,sha256=HOjcxOwyI5bBApDLsxRAGGhTTmw7fdZl2kEckxRVl-0,1994
487
487
  omlish/lang/maybes.py,sha256=pb1YrxmpXy-hWKmWR89GxXqZq1MoUD1uuTaTX30peh0,3697
@@ -529,7 +529,7 @@ omlish/lite/resources.py,sha256=YNSmX1Ohck1aoWRs55a-o5ChVbFJIQhtbqE-XwF55Oc,326
529
529
  omlish/lite/runtime.py,sha256=XQo408zxTdJdppUZqOWHyeUR50VlCpNIExNGHz4U6O4,459
530
530
  omlish/lite/secrets.py,sha256=3Mz3V2jf__XU9qNHcH56sBSw95L3U2UPL24bjvobG0c,816
531
531
  omlish/lite/strings.py,sha256=QGxT1Yh4oI8ycsfeobxnjEhvDob_GiAKLeIhZwo1j24,1986
532
- omlish/lite/timeouts.py,sha256=lhXo0zwpLM7nr2-AliBRui2BO9jh7B8ALxetwOq6hYI,4968
532
+ omlish/lite/timeouts.py,sha256=NX7gfW4FLYBcewMh4Dg_XlWqJkLY1Fom3z_OLSzuK7M,4969
533
533
  omlish/lite/timing.py,sha256=aVu3hEDB_jyTF_ryZI7iU-xg4q8CNwqpp9Apfru_iwY,196
534
534
  omlish/lite/types.py,sha256=fP5EMyBdEp2LmDxcHjUDtwAMdR06ISr9lKOL7smWfHM,140
535
535
  omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
@@ -627,7 +627,7 @@ omlish/os/pidfiles/__main__.py,sha256=AF8TwjK4xgHVnoLAP9dIWgKvT0vGhHJlfDW0tKZ7tx
627
627
  omlish/os/pidfiles/cli.py,sha256=2SSsP4O3VdpsDIMAkWgWSjh_YNIPzCD9l5LNN2qrIjo,2074
628
628
  omlish/os/pidfiles/manager.py,sha256=QphQxIENVVwvBWynLCNU31NwOfLkV43VoTVeYFn2Hac,2351
629
629
  omlish/os/pidfiles/pidfile.py,sha256=9tI5IMVwfPfnni0XMn4x5ptNQgm36n8tLeUNPf50UqU,4394
630
- omlish/os/pidfiles/pinning.py,sha256=v9RlJ4BnJZcaZZXiiRqbmzLluaSOkeeEb_WrbKEClBQ,6643
630
+ omlish/os/pidfiles/pinning.py,sha256=aQgCmvcAqN0lvI70GcSB2TKVX1G4vwbyN_AOHF3-eKE,6630
631
631
  omlish/reflect/__init__.py,sha256=64eHbD6zL6Fhp7FfXb8n9ZHywlODjBN3rm1LMvZ081A,822
632
632
  omlish/reflect/inspect.py,sha256=WCo2YpBYauKw6k758FLlZ_H4Q05rgVPs96fEv9w6zHQ,1538
633
633
  omlish/reflect/ops.py,sha256=RJ6jzrM4ieFsXzWyNXWV43O_WgzEaUvlHSc5N2ezW2A,2044
@@ -649,8 +649,8 @@ omlish/sockets/addresses.py,sha256=vbVeQBkzI513H4vRv-JS89QtRbr9U8v5zqkm3oODl_s,1
649
649
  omlish/sockets/bind.py,sha256=J1SfFFFnVf3H5nqESDX2NGEY8DmjyIMUXZciZM33zQY,8003
650
650
  omlish/sockets/handlers.py,sha256=Gj6xZoo4vommge8XvkehYw3B7O4aql2P4qzZIIa0p24,462
651
651
  omlish/sockets/io.py,sha256=lfhTkB7NnAIx9kuQhAkwgsEUXY78Mp1_WtYrIQNS_k8,1408
652
- omlish/sockets/ports.py,sha256=Wm4mRFFz5MdD8KbdaEfT1c4PbJnsuK_iyJlZJE_-8jo,1402
653
- omlish/sockets/wait.py,sha256=aznyOzGa9oNBc31xnyk1S7TylO8hGx7vFyOVsdY3zFE,1585
652
+ omlish/sockets/ports.py,sha256=NQYVvd8IBbQkPl8TnBtupTiv-Htqhc6QBIXJJ6GprnQ,1389
653
+ omlish/sockets/wait.py,sha256=OtLbcoLYzksl4GU0TNbQpzzVWGxp2iRLgo2cGZZfFFM,1559
654
654
  omlish/sockets/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
655
655
  omlish/sockets/server/handlers.py,sha256=PPsb1X5oU9dN8jfztaMGsRiqWTyEANT-1aSLbS6bUVg,3867
656
656
  omlish/sockets/server/server.py,sha256=FkaishIxJuU4it9tTI7wzlGqJYzFGXzDrd_HgV0jAmU,6253
@@ -688,10 +688,11 @@ omlish/specs/jmespath/lexer.py,sha256=WGxkwQe_dcHWcJcGg9q6K-8_Q0oRdWkw09dYGFNTHb
688
688
  omlish/specs/jmespath/parser.py,sha256=yfkydotVR4LBhrUTsptL_kLYDoGZrRN9zSEs_76kvZM,24441
689
689
  omlish/specs/jmespath/scope.py,sha256=UyDsl9rv_c8DCjJBuVIA2ESu1jrgYvuwEKiaJDQKnT0,1590
690
690
  omlish/specs/jmespath/visitor.py,sha256=HVro_6aBGL0CMBy8NRy6vJzWgwsHGB1qJXldX8H7wSg,16592
691
- omlish/specs/jsonrpc/__init__.py,sha256=QQwr-jkgvwr1ZMlNwl5W1TuHcxx8RuzQVFwWwNhp5sM,515
691
+ omlish/specs/jsonrpc/__init__.py,sha256=ugIdqHXWZjSy1R1SkwoxstO2GCHEF4W95Ogl3_5_DV4,544
692
+ omlish/specs/jsonrpc/conns.py,sha256=zvWnBHuSoGnvbGVk72Usp4IFsLscrzPozqR2hmFjnDI,7029
692
693
  omlish/specs/jsonrpc/errors.py,sha256=-Zgmlo6bV6J8w5f8h9axQgLquIFBHDgIwcpufEH5NsE,707
693
694
  omlish/specs/jsonrpc/marshal.py,sha256=HM736piPGnBZrg8CMLDX-L5fZpegyF6l6JUjzLoSDtk,1852
694
- omlish/specs/jsonrpc/types.py,sha256=emEiTPWsjsYy0jCC3It1bUEcu9nHp5y7-j73U1D6vl4,2700
695
+ omlish/specs/jsonrpc/types.py,sha256=Se9ecG-_k-kY_Qlt9QD2t3y26oY4sXTcskp6XZfVans,3054
695
696
  omlish/specs/jsonschema/__init__.py,sha256=55P7Yg2MprqDyaifac2ExNzK6blTZuDP4ejrUXZWpt8,1129
696
697
  omlish/specs/jsonschema/types.py,sha256=_H7ma99hD3_Xu42BFGHOXRI5p79tY8WBX8QE36k7lbw,472
697
698
  omlish/specs/jsonschema/keywords/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -786,11 +787,11 @@ omlish/sql/tabledefs/lower.py,sha256=YQf8gl1kxD5Fm-vOxV6G0Feh_D9PP1pYwz_vz6XjTPQ
786
787
  omlish/sql/tabledefs/marshal.py,sha256=x8XG0fJLQ6hlwyu957xqnUhr71ouKiRGBWVueZUEMZU,470
787
788
  omlish/sql/tabledefs/tabledefs.py,sha256=lIhvlt0pk6G7RZAtDFsFXm5j0l9BvRfnP7vNGeydHtE,816
788
789
  omlish/subprocesses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
789
- omlish/subprocesses/async_.py,sha256=hPQTWFa3k5CE_s9p1JTY4KdTPOsqLJtq3lGMRznrVpY,2373
790
+ omlish/subprocesses/async_.py,sha256=st7452hRImm6dCwD-Cf293VPeB50xxc8sk_EoRNe_fY,2360
790
791
  omlish/subprocesses/base.py,sha256=W6El-PUKKF9KLAks5LB6kzqs_n3FfkblJ-JOv6NFQbY,6133
791
792
  omlish/subprocesses/editor.py,sha256=tVU1EQsEhCM242HheylQvEsqaAZYnT61kMhlzZcdk5U,2628
792
- omlish/subprocesses/run.py,sha256=3jwSnQJvFMDMHmJvtAkrrK5D-i7_8cw12vX84EWTuJo,3668
793
- omlish/subprocesses/sync.py,sha256=HKmKM99_Y7tkJRg_n5onXrw41IZt5M5fqU0281LY-mo,3671
793
+ omlish/subprocesses/run.py,sha256=ViFvLJYQHAExk0novi8wCx4xk7T4WPH6CTT3P55B6-4,3642
794
+ omlish/subprocesses/sync.py,sha256=Lmr6LYQrH37wVnlYRQc-fk4iENn2zFGxJmiU8-biUEQ,3658
794
795
  omlish/subprocesses/utils.py,sha256=MJb6hvKhZceTmBeFVqlc5oM7rDxWkUzSzK9nKvbIvM8,396
795
796
  omlish/subprocesses/wrap.py,sha256=HMvCZrO2H227oGNN03KjB3FI-M5bAICqp19W8oG2f5M,763
796
797
  omlish/term/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -852,9 +853,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
852
853
  omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
853
854
  omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
854
855
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
855
- omlish-0.0.0.dev307.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
856
- omlish-0.0.0.dev307.dist-info/METADATA,sha256=UgKZG7ukJ09NYqteFnD8DCdWblMH5cosVoyewHJxBTY,4416
857
- omlish-0.0.0.dev307.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
858
- omlish-0.0.0.dev307.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
859
- omlish-0.0.0.dev307.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
860
- omlish-0.0.0.dev307.dist-info/RECORD,,
856
+ omlish-0.0.0.dev309.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
857
+ omlish-0.0.0.dev309.dist-info/METADATA,sha256=whxbYMrVb5r3hPkfUNldkiBM2-N5weht_JZVVwC7rkI,4416
858
+ omlish-0.0.0.dev309.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
859
+ omlish-0.0.0.dev309.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
860
+ omlish-0.0.0.dev309.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
861
+ omlish-0.0.0.dev309.dist-info/RECORD,,