omlish 0.0.0.dev472__py3-none-any.whl → 0.0.0.dev473__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.dev472'
2
- __revision__ = '78c62ff9afe49d9f0ae12b27ff595b2079e9c082'
1
+ __version__ = '0.0.0.dev473'
2
+ __revision__ = '9f130fc70f42b7e2117fc6530397d1bd295b37e6'
3
3
 
4
4
 
5
5
  #
@@ -8,7 +8,9 @@ with _lang.auto_proxy_init(globals()):
8
8
 
9
9
  from .attrregistry import ( # noqa
10
10
  AttrRegistry,
11
+
11
12
  AttrRegistryCache,
13
+ SimpleAttrRegistryCache,
12
14
  )
13
15
 
14
16
  from .bimap import ( # noqa
@@ -17,6 +19,8 @@ with _lang.auto_proxy_init(globals()):
17
19
  make_bi_map,
18
20
  )
19
21
 
22
+ from . import cache # noqa
23
+
20
24
  from .coerce import ( # noqa
21
25
  abs_set,
22
26
  abs_set_of,
@@ -1,3 +1,7 @@
1
+ """
2
+ TODO:
3
+ - lock?
4
+ """
1
5
  import dataclasses as dc
2
6
  import typing as ta
3
7
  import weakref
@@ -50,13 +54,32 @@ class AttrRegistry(ta.Generic[K, V]):
50
54
  def add_invalidate_callback(self, callback: ta.Callable[[], None]) -> None:
51
55
  self._invalidate_callbacks.append(callback)
52
56
 
57
+ @ta.overload
53
58
  def register(self, obj: K, val: V) -> None:
54
- check.not_in(obj, self._objs)
59
+ ...
55
60
 
56
- self._objs[obj] = val
61
+ @ta.overload
62
+ def register(self, val: V) -> ta.Callable[[T], T]:
63
+ ...
57
64
 
58
- for iv in self._invalidate_callbacks:
59
- iv()
65
+ def register(self, *args):
66
+ def inner(obj, val):
67
+ check.not_in(obj, self._objs)
68
+
69
+ self._objs[obj] = val
70
+
71
+ for iv in self._invalidate_callbacks:
72
+ iv()
73
+
74
+ return obj
75
+
76
+ if len(args) == 1:
77
+ return lambda obj: inner(obj, args[0])
78
+ elif len(args) == 2:
79
+ inner = inner(*args)
80
+ return None
81
+ else:
82
+ raise TypeError(args)
60
83
 
61
84
  def _lookup(self, obj: ta.Any) -> lang.Maybe[V]:
62
85
  if not self._identity:
@@ -180,3 +203,8 @@ class AttrRegistryCache(ta.Generic[K, V, T]):
180
203
  out = self._prepare(instance_cls, collected)
181
204
  self._cache[weakref.ref(instance_cls, self._cache_remove)] = out
182
205
  return out
206
+
207
+
208
+ class SimpleAttrRegistryCache(AttrRegistryCache[K, V, dict[str, tuple[K, V]]], ta.Generic[K, V]):
209
+ def __init__(self, registry: AttrRegistry[K, V]) -> None:
210
+ super().__init__(registry, lambda _, dct: dct)
@@ -1,12 +1,18 @@
1
- from .dispatch import ( # noqa
2
- Dispatcher,
3
- )
4
-
5
- from .functions import ( # noqa
6
- function,
7
- )
8
-
9
- from .methods import ( # noqa
10
- install_method,
11
- method,
12
- )
1
+ from .. import lang as _lang
2
+
3
+
4
+ with _lang.auto_proxy_init(globals()):
5
+ ##
6
+
7
+ from .dispatch import ( # noqa
8
+ Dispatcher,
9
+ )
10
+
11
+ from .functions import ( # noqa
12
+ function,
13
+ )
14
+
15
+ from .methods import ( # noqa
16
+ install_method,
17
+ method,
18
+ )
@@ -1,3 +1,16 @@
1
+ """
2
+ A generator powered, configurable, mostly fully streaming JSON parser.
3
+
4
+ Regarding the 'streamyness' of the subsystems:
5
+ - Lexing only buffers for string and number literals.
6
+ - Parsing maintains only a stack that scales by nesting depth.
7
+ - Building values will obviously hold everything under the topmost object it's building until it's finished.
8
+
9
+ It's reasonably optimized, but performance is not a primary or even secondary goal: its goal is flexibility. If speed
10
+ matters use a native library.
11
+ """
12
+
13
+
1
14
  from .building import ( # noqa
2
15
  JsonValueBuilder,
3
16
  )
@@ -3,9 +3,9 @@
3
3
  import abc
4
4
  import contextlib
5
5
  import dataclasses as dc
6
- import io
7
6
  import typing as ta
8
7
 
8
+ from ...io.readers import AsyncBufferedBytesReader
9
9
  from ...lite.abstract import Abstract
10
10
  from ...lite.dataclasses import dataclass_shallow_asdict
11
11
  from .base import BaseHttpClient
@@ -27,25 +27,19 @@ AsyncHttpClientT = ta.TypeVar('AsyncHttpClientT', bound='AsyncHttpClient')
27
27
  @ta.final
28
28
  @dc.dataclass(frozen=True) # kw_only=True
29
29
  class AsyncStreamHttpResponse(BaseHttpResponse):
30
- class Stream(ta.Protocol):
31
- def read1(self, /, n: int = -1) -> ta.Awaitable[bytes]: ...
30
+ _stream: ta.Optional[AsyncBufferedBytesReader] = None
32
31
 
33
- @ta.final
34
- class _NullStream:
35
- def read1(self, /, n: int = -1) -> ta.Awaitable[bytes]:
36
- raise TypeError
37
-
38
- stream: Stream = _NullStream()
32
+ @property
33
+ def stream(self) -> 'AsyncBufferedBytesReader':
34
+ if (st := self._stream) is None:
35
+ raise TypeError('No data')
36
+ return st
39
37
 
40
38
  @property
41
39
  def has_data(self) -> bool:
42
- return not isinstance(self.stream, AsyncStreamHttpResponse._NullStream)
40
+ return self._stream is not None
43
41
 
44
- async def read_all(self) -> bytes:
45
- buf = io.BytesIO()
46
- while (b := await self.stream.read1()):
47
- buf.write(b)
48
- return buf.getvalue()
42
+ #
49
43
 
50
44
  _closer: ta.Optional[ta.Callable[[], ta.Awaitable[None]]] = None
51
45
 
@@ -96,8 +90,8 @@ async def async_read_http_client_response(resp: BaseHttpResponse) -> HttpRespons
96
90
 
97
91
  elif isinstance(resp, AsyncStreamHttpResponse):
98
92
  return HttpResponse(**{
99
- **{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('stream', '_closer')},
100
- **({'data': await resp.read_all()} if resp.has_data else {}),
93
+ **{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('_stream', '_closer')},
94
+ **({'data': await resp.stream.readall()} if resp.has_data else {}),
101
95
  })
102
96
 
103
97
  else:
@@ -5,6 +5,7 @@ import socket
5
5
  import typing as ta
6
6
  import urllib.parse
7
7
 
8
+ from ....io.buffers import ReadableListBuffer
8
9
  from ....lite.check import check
9
10
  from ...coro.client.connection import CoroHttpClientConnection
10
11
  from ...coro.client.response import CoroHttpClientResponse
@@ -92,7 +93,7 @@ class CoroHttpClient(HttpClient):
92
93
  headers=HttpHeaders(resp._state.headers.items()), # noqa
93
94
  request=self._req,
94
95
  underlying=self,
95
- stream=self,
96
+ _stream=ReadableListBuffer().new_buffered_reader(self),
96
97
  _closer=self.close,
97
98
  )
98
99
 
@@ -33,6 +33,12 @@ class ExecutorAsyncHttpClient(AsyncHttpClient):
33
33
  async def read1(self, /, n: int = -1) -> bytes:
34
34
  return await self.owner._run_in_executor(self.resp.stream.read1, n) # noqa
35
35
 
36
+ async def read(self, /, n: int = -1) -> bytes:
37
+ return await self.owner._run_in_executor(self.resp.stream.read, n) # noqa
38
+
39
+ async def readall(self) -> bytes:
40
+ return await self.owner._run_in_executor(self.resp.stream.readall) # noqa
41
+
36
42
  async def close(self) -> None:
37
43
  return await self.owner._run_in_executor(self.resp.close) # noqa
38
44
 
@@ -44,7 +50,7 @@ class ExecutorAsyncHttpClient(AsyncHttpClient):
44
50
  request=req,
45
51
  underlying=resp,
46
52
  **(dict( # type: ignore
47
- stream=(adapter := self._StreamAdapter(self, resp)),
53
+ _stream=(adapter := self._StreamAdapter(self, resp)),
48
54
  _closer=adapter.close,
49
55
  ) if resp.has_data else {}),
50
56
  )
@@ -32,27 +32,12 @@ class HttpxHttpClient(HttpClient):
32
32
  @dc.dataclass(frozen=True)
33
33
  class _StreamAdapter:
34
34
  it: ta.Iterator[bytes]
35
- buf: ReadableListBuffer = dc.field(default_factory=ReadableListBuffer)
36
35
 
37
36
  def read1(self, /, n: int = -1) -> bytes:
38
- if n < 0:
39
- if (b := self.buf.read(n)) is not None:
40
- return b
41
- try:
42
- return next(self.it)
43
- except StopIteration:
44
- return b''
45
-
46
- else:
47
- while len(self.buf) < n:
48
- try:
49
- b = next(self.it)
50
- except StopIteration:
51
- b = b''
52
- if not b:
53
- return self.buf.read() or b''
54
- self.buf.feed(b)
55
- return self.buf.read(n) or b''
37
+ try:
38
+ return next(self.it)
39
+ except StopIteration:
40
+ return b''
56
41
 
57
42
  def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
58
43
  try:
@@ -76,7 +61,7 @@ class HttpxHttpClient(HttpClient):
76
61
  headers=HttpHeaders(resp.headers.raw),
77
62
  request=req,
78
63
  underlying=resp,
79
- stream=self._StreamAdapter(resp.iter_bytes()),
64
+ _stream=ReadableListBuffer().new_buffered_reader(self._StreamAdapter(resp.iter_bytes())),
80
65
  _closer=resp_close, # type: ignore
81
66
  )
82
67
 
@@ -84,7 +69,7 @@ class HttpxHttpClient(HttpClient):
84
69
  resp_close()
85
70
  raise HttpClientError from e
86
71
 
87
- except Exception:
72
+ except BaseException:
88
73
  resp_close()
89
74
  raise
90
75
 
@@ -96,27 +81,12 @@ class HttpxAsyncHttpClient(AsyncHttpClient):
96
81
  @dc.dataclass(frozen=True)
97
82
  class _StreamAdapter:
98
83
  it: ta.AsyncIterator[bytes]
99
- buf: ReadableListBuffer = dc.field(default_factory=ReadableListBuffer)
100
84
 
101
85
  async def read1(self, /, n: int = -1) -> bytes:
102
- if n < 0:
103
- if (b := self.buf.read(n)) is not None:
104
- return b
105
- try:
106
- return await anext(self.it)
107
- except StopAsyncIteration:
108
- return b''
109
-
110
- else:
111
- while len(self.buf) < n:
112
- try:
113
- b = await anext(self.it)
114
- except StopAsyncIteration:
115
- b = b''
116
- if not b:
117
- return self.buf.read() or b''
118
- self.buf.feed(b)
119
- return self.buf.read(n) or b''
86
+ try:
87
+ return await anext(self.it)
88
+ except StopAsyncIteration:
89
+ return b''
120
90
 
121
91
  async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
122
92
  es = contextlib.AsyncExitStack()
@@ -132,12 +102,37 @@ class HttpxAsyncHttpClient(AsyncHttpClient):
132
102
  timeout=req.timeout_s,
133
103
  ))
134
104
 
105
+ it = resp.aiter_bytes()
106
+
107
+ # FIXME:
108
+ # - https://github.com/encode/httpx/discussions/2963
109
+ # - Exception ignored in: <async_generator object HTTP11ConnectionByteStream.__aiter__ at 0x1325a7540>
110
+ # - RuntimeError: async generator ignored GeneratorExit
111
+ # - Traced to:
112
+ # - HTTP11ConnectionByteStream.aclose -> await self._connection._response_closed()
113
+ # - AsyncHTTP11Connection._response_closed -> async with self._state_lock
114
+ # - anyio._backends._asyncio.Lock.acquire -> await AsyncIOBackend.cancel_shielded_checkpoint() -> await sleep(0) # noqa
115
+ # - Might have something to do with pycharm/pydevd's nested asyncio, doesn't seem to happen under trio ever
116
+ # or asyncio outside debugger.
117
+ @es.push_async_callback
118
+ async def close_it() -> None:
119
+ try:
120
+ # print(f'close_it.begin: {it=}', file=sys.stderr)
121
+ await it.aclose() # type: ignore[attr-defined]
122
+ # print(f'close_it.end: {it=}', file=sys.stderr)
123
+ except BaseException as be: # noqa
124
+ # print(f'close_it.__exit__: {it=} {be=}', file=sys.stderr)
125
+ raise
126
+ finally:
127
+ # print(f'close_it.finally: {it=}', file=sys.stderr)
128
+ pass
129
+
135
130
  return AsyncStreamHttpResponse(
136
131
  status=resp.status_code,
137
132
  headers=HttpHeaders(resp.headers.raw),
138
133
  request=req,
139
134
  underlying=resp,
140
- stream=self._StreamAdapter(resp.aiter_bytes()),
135
+ _stream=ReadableListBuffer().new_async_buffered_reader(self._StreamAdapter(it)),
141
136
  _closer=es.aclose,
142
137
  )
143
138
 
@@ -145,6 +140,6 @@ class HttpxAsyncHttpClient(AsyncHttpClient):
145
140
  await es.aclose()
146
141
  raise HttpClientError from e
147
142
 
148
- except Exception:
143
+ except BaseException:
149
144
  await es.aclose()
150
145
  raise
@@ -3,9 +3,9 @@
3
3
  import abc
4
4
  import contextlib
5
5
  import dataclasses as dc
6
- import io
7
6
  import typing as ta
8
7
 
8
+ from ...io.readers import BufferedBytesReader
9
9
  from ...lite.abstract import Abstract
10
10
  from ...lite.dataclasses import dataclass_shallow_asdict
11
11
  from .base import BaseHttpClient
@@ -27,25 +27,19 @@ HttpClientT = ta.TypeVar('HttpClientT', bound='HttpClient')
27
27
  @ta.final
28
28
  @dc.dataclass(frozen=True) # kw_only=True
29
29
  class StreamHttpResponse(BaseHttpResponse):
30
- class Stream(ta.Protocol):
31
- def read1(self, /, n: int = -1) -> bytes: ...
30
+ _stream: ta.Optional[BufferedBytesReader] = None
32
31
 
33
- @ta.final
34
- class _NullStream:
35
- def read1(self, /, n: int = -1) -> bytes:
36
- raise TypeError
37
-
38
- stream: Stream = _NullStream()
32
+ @property
33
+ def stream(self) -> 'BufferedBytesReader':
34
+ if (st := self._stream) is None:
35
+ raise TypeError('No data')
36
+ return st
39
37
 
40
38
  @property
41
39
  def has_data(self) -> bool:
42
- return not isinstance(self.stream, StreamHttpResponse._NullStream)
40
+ return self._stream is not None
43
41
 
44
- def read_all(self) -> bytes:
45
- buf = io.BytesIO()
46
- while (b := self.stream.read1()):
47
- buf.write(b)
48
- return buf.getvalue()
42
+ #
49
43
 
50
44
  _closer: ta.Optional[ta.Callable[[], None]] = None
51
45
 
@@ -94,8 +88,8 @@ def read_http_client_response(resp: BaseHttpResponse) -> HttpResponse:
94
88
 
95
89
  elif isinstance(resp, StreamHttpResponse):
96
90
  return HttpResponse(**{
97
- **{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('stream', '_closer')},
98
- **({'data': resp.read_all()} if resp.has_data else {}),
91
+ **{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('_stream', '_closer')},
92
+ **({'data': resp.stream.readall()} if resp.has_data else {}),
99
93
  })
100
94
 
101
95
  else:
@@ -26,6 +26,12 @@ class SyncAsyncHttpClient(AsyncHttpClient):
26
26
  async def read1(self, /, n: int = -1) -> bytes:
27
27
  return self.ul.stream.read1(n)
28
28
 
29
+ async def read(self, /, n: int = -1) -> bytes:
30
+ return self.ul.stream.read(n)
31
+
32
+ async def readall(self) -> bytes:
33
+ return self.ul.stream.readall()
34
+
29
35
  async def close(self) -> None:
30
36
  self.ul.close()
31
37
 
@@ -37,7 +43,7 @@ class SyncAsyncHttpClient(AsyncHttpClient):
37
43
  request=req,
38
44
  underlying=resp,
39
45
  **(dict( # type: ignore
40
- stream=(adapter := self._StreamAdapter(resp)),
46
+ _stream=(adapter := self._StreamAdapter(resp)),
41
47
  _closer=adapter.close,
42
48
  ) if resp.has_data else {}),
43
49
  )
@@ -5,6 +5,7 @@ import typing as ta
5
5
  import urllib.error
6
6
  import urllib.request
7
7
 
8
+ from ...io.buffers import ReadableListBuffer
8
9
  from ..headers import HttpHeaders
9
10
  from .base import DEFAULT_ENCODING
10
11
  from .base import HttpClientContext
@@ -72,7 +73,7 @@ class UrllibHttpClient(HttpClient):
72
73
  headers=HttpHeaders(resp.headers.items()),
73
74
  request=req,
74
75
  underlying=resp,
75
- stream=resp,
76
+ _stream=ReadableListBuffer().new_buffered_reader(resp),
76
77
  _closer=resp.close,
77
78
  )
78
79
 
omlish/io/buffers.py CHANGED
@@ -1,10 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007 UP043 UP045
2
2
  # @omlish-lite
3
+ """
4
+ TODO:
5
+ - overhaul and just coro-ify pyio?
6
+ """
3
7
  import io
4
8
  import typing as ta
5
9
 
6
10
  from ..lite.attrops import attr_repr
7
11
  from ..lite.check import check
12
+ from .readers import AsyncBufferedBytesReader
13
+ from .readers import AsyncRawBytesReader
14
+ from .readers import BufferedBytesReader
15
+ from .readers import RawBytesReader
8
16
 
9
17
 
10
18
  ##
@@ -183,6 +191,9 @@ class ReadableListBuffer:
183
191
 
184
192
  self._lst: list[bytes] = []
185
193
 
194
+ def __bool__(self) -> ta.NoReturn:
195
+ raise TypeError("Use 'buf is not None' or 'len(buf)'.")
196
+
186
197
  def __len__(self) -> int:
187
198
  return sum(map(len, self._lst))
188
199
 
@@ -249,6 +260,110 @@ class ReadableListBuffer:
249
260
  r = self.read_until_(delim)
250
261
  return r if isinstance(r, bytes) else None
251
262
 
263
+ #
264
+
265
+ DEFAULT_BUFFERED_READER_CHUNK_SIZE: ta.ClassVar[int] = -1
266
+
267
+ @ta.final
268
+ class _BufferedBytesReader(BufferedBytesReader):
269
+ def __init__(
270
+ self,
271
+ raw: RawBytesReader,
272
+ buf: 'ReadableListBuffer',
273
+ *,
274
+ chunk_size: ta.Optional[int] = None,
275
+ ) -> None:
276
+ self._raw = raw
277
+ self._buf = buf
278
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
279
+
280
+ def read1(self, /, n: int = -1) -> bytes:
281
+ if n < 0:
282
+ n = self._chunk_size
283
+ if not n:
284
+ return b''
285
+ if 0 < n <= len(self._buf):
286
+ return self._buf.read(n) or b''
287
+ return self._raw.read1(n)
288
+
289
+ def read(self, /, n: int = -1) -> bytes:
290
+ if n < 0:
291
+ return self.readall()
292
+ while len(self._buf) < n:
293
+ if not (b := self._raw.read1(n)):
294
+ break
295
+ self._buf.feed(b)
296
+ return self._buf.read(n) or b''
297
+
298
+ def readall(self) -> bytes:
299
+ buf = io.BytesIO()
300
+ buf.write(self._buf.read() or b'')
301
+ while (b := self._raw.read1(self._chunk_size)):
302
+ buf.write(b)
303
+ return buf.getvalue()
304
+
305
+ def new_buffered_reader(
306
+ self,
307
+ raw: RawBytesReader,
308
+ *,
309
+ chunk_size: ta.Optional[int] = None,
310
+ ) -> BufferedBytesReader:
311
+ return self._BufferedBytesReader(
312
+ raw,
313
+ self,
314
+ chunk_size=chunk_size,
315
+ )
316
+
317
+ @ta.final
318
+ class _AsyncBufferedBytesReader(AsyncBufferedBytesReader):
319
+ def __init__(
320
+ self,
321
+ raw: AsyncRawBytesReader,
322
+ buf: 'ReadableListBuffer',
323
+ *,
324
+ chunk_size: ta.Optional[int] = None,
325
+ ) -> None:
326
+ self._raw = raw
327
+ self._buf = buf
328
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
329
+
330
+ async def read1(self, /, n: int = -1) -> bytes:
331
+ if n < 0:
332
+ n = self._chunk_size
333
+ if not n:
334
+ return b''
335
+ if 0 < n <= len(self._buf):
336
+ return self._buf.read(n) or b''
337
+ return await self._raw.read1(n)
338
+
339
+ async def read(self, /, n: int = -1) -> bytes:
340
+ if n < 0:
341
+ return await self.readall()
342
+ while len(self._buf) < n:
343
+ if not (b := await self._raw.read1(n)):
344
+ break
345
+ self._buf.feed(b)
346
+ return self._buf.read(n) or b''
347
+
348
+ async def readall(self) -> bytes:
349
+ buf = io.BytesIO()
350
+ buf.write(self._buf.read() or b'')
351
+ while b := await self._raw.read1(self._chunk_size):
352
+ buf.write(b)
353
+ return buf.getvalue()
354
+
355
+ def new_async_buffered_reader(
356
+ self,
357
+ raw: AsyncRawBytesReader,
358
+ *,
359
+ chunk_size: ta.Optional[int] = None,
360
+ ) -> AsyncBufferedBytesReader:
361
+ return self._AsyncBufferedBytesReader(
362
+ raw,
363
+ self,
364
+ chunk_size=chunk_size,
365
+ )
366
+
252
367
 
253
368
  ##
254
369
 
omlish/io/readers.py ADDED
@@ -0,0 +1,29 @@
1
+ # ruff: noqa: UP045
2
+ # @omlish-lite
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ class RawBytesReader(ta.Protocol):
10
+ def read1(self, /, n: int = -1) -> bytes: ...
11
+
12
+
13
+ class BufferedBytesReader(RawBytesReader, ta.Protocol):
14
+ def read(self, /, n: int = -1) -> bytes: ...
15
+
16
+ def readall(self) -> bytes: ...
17
+
18
+
19
+ #
20
+
21
+
22
+ class AsyncRawBytesReader(ta.Protocol):
23
+ def read1(self, /, n: int = -1) -> ta.Awaitable[bytes]: ...
24
+
25
+
26
+ class AsyncBufferedBytesReader(AsyncRawBytesReader, ta.Protocol):
27
+ def read(self, /, n: int = -1) -> ta.Awaitable[bytes]: ...
28
+
29
+ def readall(self) -> ta.Awaitable[bytes]: ...
@@ -54,7 +54,7 @@ class ExitStacked:
54
54
  es.__enter__()
55
55
  try:
56
56
  self._enter_contexts()
57
- except Exception: # noqa
57
+ except BaseException: # noqa
58
58
  es.__exit__(*sys.exc_info())
59
59
  raise
60
60
  return self
@@ -65,7 +65,7 @@ class ExitStacked:
65
65
  return None
66
66
  try:
67
67
  self._exit_contexts()
68
- except Exception: # noqa
68
+ except BaseException: # noqa
69
69
  es.__exit__(*sys.exc_info())
70
70
  raise
71
71
  return es.__exit__(exc_type, exc_val, exc_tb)
@@ -113,7 +113,7 @@ class AsyncExitStacked:
113
113
  await es.__aenter__()
114
114
  try:
115
115
  await self._async_enter_contexts()
116
- except Exception: # noqa
116
+ except BaseException: # noqa
117
117
  await es.__aexit__(*sys.exc_info())
118
118
  raise
119
119
  return self
@@ -124,7 +124,7 @@ class AsyncExitStacked:
124
124
  return None
125
125
  try:
126
126
  await self._async_exit_contexts()
127
- except Exception: # noqa
127
+ except BaseException: # noqa
128
128
  await es.__aexit__(*sys.exc_info())
129
129
  raise
130
130
  return await es.__aexit__(exc_type, exc_val, exc_tb)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev472
3
+ Version: 0.0.0.dev473
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.omlish-manifests.json,sha256=FLw7xkPiSXuImZgqSP8BwrEib2R1doSzUPLUkc-QUIA,8410
2
- omlish/__about__.py,sha256=4-bUUNkRfpsKN_j34JrHTQA3yrraoqE3_gcf_eEzYFg,3611
2
+ omlish/__about__.py,sha256=BoG13E5l8VG76ZQEVeoy6T90tE-JO2_DCUi_285ZWU0,3611
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ZNIMl1kwg3qdei4DiUrJPQe5M81S1e76N-GuNSwLBAE,8683
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -67,9 +67,9 @@ omlish/codecs/funcs.py,sha256=or0Jogczuzk7csDTRl-HURMEjl8LXXqxxXYK45xcM5w,855
67
67
  omlish/codecs/registry.py,sha256=y7gjhBY1tTTHZmaK4X02zL5HD_25AAiZ8k27Op69Ag0,4006
68
68
  omlish/codecs/standard.py,sha256=eiZ4u9ep0XrA4Z_D1zJI0vmWyuN8HLrX4Se_r_Cq_ZM,60
69
69
  omlish/codecs/text.py,sha256=P9-xpdiQE3nbI5CzEentf07FBHPzD7_SwOtUQUJ01So,5710
70
- omlish/collections/__init__.py,sha256=cSNZ670-GaNW_sCazmLi41HPx5tfzMvkHCvPoyebTEY,2934
70
+ omlish/collections/__init__.py,sha256=zlyn2_SR1QaRomuqziEmONqSdgcalHjjULA66GiKOyk,3001
71
71
  omlish/collections/abc.py,sha256=p9zhL5oNV5WPyWmMn34fWfkuxPQAjOtL7WQA-Xsyhwk,2628
72
- omlish/collections/attrregistry.py,sha256=SPvIt8rT8nWvEBqf35I8wPgBU55m0u2ssSmNcYHm_FM,5563
72
+ omlish/collections/attrregistry.py,sha256=IvepCE4EcU0zsjPRC-A9LxXjMqkK8i2LmlekVHHSsaY,6231
73
73
  omlish/collections/bimap.py,sha256=3szDCscPJlFRtkpyVQNWneg4s50mr6Rd0jdTzVEIcnE,1661
74
74
  omlish/collections/coerce.py,sha256=tAls15v_7p5bUN33R7Zbko87KW5toWHl9fRialCqyNY,7030
75
75
  omlish/collections/frozen.py,sha256=drarjcMFpwKhBM01b1Gh6XDcaRaUgpyuxEiqppaKRkU,4220
@@ -217,7 +217,7 @@ omlish/diag/replserver/__init__.py,sha256=uLo6V2aQ29v9z3IMELlPDSlG3_2iOT4-_X8Vni
217
217
  omlish/diag/replserver/__main__.py,sha256=LmU41lQ58bm1h4Mx7S8zhE_uEBSC6kPcp9mn5JRpulA,32
218
218
  omlish/diag/replserver/console.py,sha256=MxDLslAaYfC-l0rfGO-SnYAJ__8RWBJQ5FxGi29czYU,7438
219
219
  omlish/diag/replserver/server.py,sha256=zTeyaARK22KwaqCOzG8H8Rk009en7gBxOBaJcRB1jbM,5402
220
- omlish/dispatch/__init__.py,sha256=UwVT6SSk5HKk9xF9vc_UJD8BDSIQWSw98Ldx-G-6PC0,166
220
+ omlish/dispatch/__init__.py,sha256=OlebxKvMJs2nT36p3xCB4arA3Ln0pnQvfwBaUJ5SdiE,284
221
221
  omlish/dispatch/dispatch.py,sha256=TzWihCt9Zr8wfIwVpKHVOOrYFVKpDXRevxYomtEfqYc,3176
222
222
  omlish/dispatch/functions.py,sha256=cwNzGIg2ZIalEgn9I03cnJVbMTHjWloyDTaowlO3UPs,1524
223
223
  omlish/dispatch/impls.py,sha256=3kyLw5yiYXWCN1DiqQbD1czMVrM8v1mpQ8NyD0tUrnk,1870
@@ -270,7 +270,7 @@ omlish/formats/json/backends/jiter.py,sha256=8qv_XWGpcupPtVm6Z_egHio_iY1Kk8eqkvX
270
270
  omlish/formats/json/backends/orjson.py,sha256=X2jhKgUYYwaVuAppPQmfyncbOs2PJ7drqH5BioP-AYI,3831
271
271
  omlish/formats/json/backends/std.py,sha256=Hhiug5CkC1TXLFrE9rnMR2on7xP-RliSvfYA9ill_U0,3159
272
272
  omlish/formats/json/backends/ujson.py,sha256=U3iOlAURfiCdXbiNlXfIjDdtJDbDaLZsSuZriTUvbxs,2307
273
- omlish/formats/json/stream/__init__.py,sha256=LoYSkdUX3lkQ4g9JB_F6YOeexlOEporKW8bW8tv3YXg,859
273
+ omlish/formats/json/stream/__init__.py,sha256=YIRCs5ckY_y1v08OwGGtU4nXlo-ByO4IsibTlZZIsAM,1366
274
274
  omlish/formats/json/stream/building.py,sha256=QAQaTyXuw9vkfhvzWIh_DSlypD1-HgzO855Dgz3_wFM,2517
275
275
  omlish/formats/json/stream/errors.py,sha256=c8M8UAYmIZ-vWZLeKD2jMj4EDCJbr9QR8Jq_DyHjujQ,43
276
276
  omlish/formats/json/stream/lexing.py,sha256=FeO7sjCQ6HHBj7VJIwkSxMPUneG1KqDyN-zuACdE_rw,17384
@@ -323,17 +323,17 @@ omlish/http/urls.py,sha256=dZBeQwf5ogKwsD_uCEei5EG_SbnHk-MzpX2FYLsfTgM,1774
323
323
  omlish/http/versions.py,sha256=Lwk6FztKH7c5zuc7NFWxPVULuLeeQp5-NFJInY6r-Zo,420
324
324
  omlish/http/wsgi.py,sha256=1JpfrY2JrQ0wrEVE0oLdQMWZw8Zcx0b4_9f3VmH4JKA,1070
325
325
  omlish/http/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
326
- omlish/http/clients/asyncs.py,sha256=YJKin04lXSrhn8tgs2gMEo8Fy4jWkPhy6ZaWi1fVnko,4427
326
+ omlish/http/clients/asyncs.py,sha256=QrbavgXv4BuQFW0bRojy-dxa_jpxjPVLA7eyfo1zzns,4230
327
327
  omlish/http/clients/base.py,sha256=bm3Oy2wuunSO3Rd2dxCmhHoZcQ8X9B5pkzaudbjZKvI,2810
328
328
  omlish/http/clients/default.py,sha256=fnz-pvPKHc7Kgft4XRwOZ6ZUUtt5D6ljuvOGAA8mgQw,5639
329
- omlish/http/clients/executor.py,sha256=A9LLTR3O_-_hH6HAB3hvf5uBv9SwLL5YOW3YWVb20Js,1580
330
- omlish/http/clients/httpx.py,sha256=8CVA9nNVKSkKcEynu34GxcbfVaz3PBA_UBx3-NFKDrw,4448
329
+ omlish/http/clients/executor.py,sha256=yvjLs8XfJuHx0mDOC1nqKsQbQXqo2cldH8oQIBhfjOY,1854
330
+ omlish/http/clients/httpx.py,sha256=Yp0u_DAPImqJ1_jlAN9RKWbkUP6Y_lfGJWJjaFWBdJQ,4793
331
331
  omlish/http/clients/middleware.py,sha256=xnXlNN1MU4sUKtkhPzdpRQsRh9wpLf1r7OGKjqGzLWg,5058
332
- omlish/http/clients/sync.py,sha256=zs6XzFHKwKhKuBwb6hJFaYOcBY-4QVEsa0udFOoU0QM,4058
333
- omlish/http/clients/syncasync.py,sha256=veVSD-uqOzePAUzhXOoJvMF6fHLOkzXYbviQ9x18l1k,1215
334
- omlish/http/clients/urllib.py,sha256=BwxlDEiJQZA6sogcqTtuog6WEK2OhVNLR70F50lXA9I,2764
332
+ omlish/http/clients/sync.py,sha256=jVbqbUlruuaNFKUYQr55kVF6bwcIZgeaLyLclXM_T5w,3891
333
+ omlish/http/clients/syncasync.py,sha256=2VfApHxoz3dBilOJhxyaP9LnWWNdqn5G4Tss6CRruXM,1401
334
+ omlish/http/clients/urllib.py,sha256=c8TQITZYRo5RrdxaNhsE2QoeDw4mZW0wLzoIsWYTYwU,2852
335
335
  omlish/http/clients/coro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
336
- omlish/http/clients/coro/sync.py,sha256=JCWSCsDP7rFBRQSxzB7WmBBwRdAfLFRGI-F_4f0jiPQ,5673
336
+ omlish/http/clients/coro/sync.py,sha256=x5VoA31lPyxGMkuYAXgEziDfL_mhV_VP6iQ4HmpLjjE,5762
337
337
  omlish/http/coro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
338
338
  omlish/http/coro/io.py,sha256=d97Oa7KlgMP7BQWdhUbOv3sfA96uWyMtjJF7lJFwZC0,1090
339
339
  omlish/http/coro/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -390,9 +390,10 @@ omlish/inject/impl/scopes.py,sha256=sqtjHe6g0pAEOHqIARXGiIE9mKmGmlqh1FDpskV4TIw,
390
390
  omlish/inject/impl/sync.py,sha256=_aLaC-uVNHOePcfE1as61Ni7fuyHZpjEafIWBc7FvC0,1049
391
391
  omlish/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
392
392
  omlish/io/abc.py,sha256=M40QB2udYpCEqmlxCcHv6FlJYJY6ymmJQBlaklYv0U8,1256
393
- omlish/io/buffers.py,sha256=GuddTeEeaNU4Ep2NWEeS-8ZO05JF8guPCGeTBNQ3mek,7351
393
+ omlish/io/buffers.py,sha256=GueMHwOLGd6Q-UxLrGobYYNTm0fc5GBtltwzYgLxGIQ,10935
394
394
  omlish/io/fileno.py,sha256=_W3qxkIKpnabn1_7kgmKdx0IsPF3R334xWnF_TtkEj4,185
395
395
  omlish/io/pyio.py,sha256=xmHTV-sn7QZThWCVBo6lTM7dhgsQn7m2L0DqRwdF2-8,94509
396
+ omlish/io/readers.py,sha256=O_FcMW6wXSoKhyhM9dKFg9lVJ8mS-Q5I9xnFLfgJMKQ,583
396
397
  omlish/io/compress/__init__.py,sha256=fJFPT4ONfqxmsA4jR6qbMt2woIyyEgnc_qOWK9o1kII,247
397
398
  omlish/io/compress/abc.py,sha256=P9YoQX8XYoq2UfBsinKLUuwwqV1ODUIJzjTraRWGF1M,3090
398
399
  omlish/io/compress/adapters.py,sha256=LJHhjwMHXstoLyX_q0QhGoBAcqyYGWfzhzQbGBXHzHY,6148
@@ -486,7 +487,7 @@ omlish/lite/attrops.py,sha256=02DNBjOl27NjznkwolgPwtWpA1q3UPUuwOjHsgG4oLo,11381
486
487
  omlish/lite/cached.py,sha256=AF0PdB2k4h2dyNc5tzrmnNrb9zKnoMPQU3WA8KrhS_o,3108
487
488
  omlish/lite/check.py,sha256=ytCkwZoKfOlJqylL-AGm8C2WfsWJd2q3kFbnZCzX3_M,13844
488
489
  omlish/lite/configs.py,sha256=4-1uVxo-aNV7vMKa7PVNhM610eejG1WepB42-Dw2xQI,914
489
- omlish/lite/contextmanagers.py,sha256=usDzBoNqJi98AccVlwgZoMvoBXOe9wyx2MreZtWhJy4,5863
490
+ omlish/lite/contextmanagers.py,sha256=McvPC6VsQlUHzwAppHgmo0RviABPGOAEdDsGPn7dShw,5879
490
491
  omlish/lite/dataclasses.py,sha256=Kxr68nTinRAkdWFGeu8C2qLul8iw6bxpbqqp20yxZfg,5064
491
492
  omlish/lite/imports.py,sha256=GyEDKL-WuHtdOKIL-cc8aFd0-bHwZFDEjAB52ItabX0,1341
492
493
  omlish/lite/inject.py,sha256=BQgjBj2mzJgMimLam-loSpQzcb31-8NYPVRQgHVv3cQ,29159
@@ -842,9 +843,9 @@ omlish/typedvalues/marshal.py,sha256=2xqX6JllhtGpmeYkU7C-qzgU__0x-vd6CzYbAsocQlc
842
843
  omlish/typedvalues/of_.py,sha256=UXkxSj504WI2UrFlqdZJbu2hyDwBhL7XVrc2qdR02GQ,1309
843
844
  omlish/typedvalues/reflect.py,sha256=PAvKW6T4cW7u--iX80w3HWwZUS3SmIZ2_lQjT65uAyk,1026
844
845
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
845
- omlish-0.0.0.dev472.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
846
- omlish-0.0.0.dev472.dist-info/METADATA,sha256=YdE7vkMrfcNoa_DIKW5K9O1_DbzdmLKAYEdiUS3kw2Y,18999
847
- omlish-0.0.0.dev472.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
848
- omlish-0.0.0.dev472.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
849
- omlish-0.0.0.dev472.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
850
- omlish-0.0.0.dev472.dist-info/RECORD,,
846
+ omlish-0.0.0.dev473.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
847
+ omlish-0.0.0.dev473.dist-info/METADATA,sha256=-EYEBlROnYaFvqD5fxDP0hmJ1GTBnmWbV0EzbBxPACc,18999
848
+ omlish-0.0.0.dev473.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
849
+ omlish-0.0.0.dev473.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
850
+ omlish-0.0.0.dev473.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
851
+ omlish-0.0.0.dev473.dist-info/RECORD,,