omlish 0.0.0.dev276__py3-none-any.whl → 0.0.0.dev277__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.dev276'
2
- __revision__ = 'b59ea0791ca796acd261e3bd7d25125492a027e4'
1
+ __version__ = '0.0.0.dev277'
2
+ __revision__ = 'b8087c9a0ca9d46a8ce3bfc58ba6bef8d3370196'
3
3
 
4
4
 
5
5
  #
omlish/http/all.py CHANGED
@@ -1,13 +1,18 @@
1
1
  from . import consts # noqa
2
2
 
3
3
  from .clients import ( # noqa
4
+ BaseHttpResponse,
4
5
  HttpClient,
5
6
  HttpClientError,
6
7
  HttpRequest,
7
8
  HttpResponse,
8
9
  HttpxHttpClient,
10
+ StreamHttpResponse,
9
11
  UrllibHttpClient,
10
12
  client,
13
+ close_response,
14
+ closing_response,
15
+ read_response,
11
16
  request,
12
17
  )
13
18
 
omlish/http/clients.py CHANGED
@@ -1,12 +1,14 @@
1
1
  """
2
2
  TODO:
3
+ - stream
4
+ - chunk size - httpx interface is awful, punch through?
3
5
  - httpx catch
4
- - check=False
5
6
  - return non-200 HttpResponses
6
7
  - async
7
- - stream
8
8
  """
9
9
  import abc
10
+ import contextlib
11
+ import functools
10
12
  import http.client
11
13
  import typing as ta
12
14
  import urllib.error
@@ -25,6 +27,9 @@ else:
25
27
  httpx = lang.proxy_import('httpx')
26
28
 
27
29
 
30
+ BaseHttpResponseT = ta.TypeVar('BaseHttpResponseT', bound='BaseHttpResponse')
31
+
32
+
28
33
  ##
29
34
 
30
35
 
@@ -65,23 +70,91 @@ class HttpRequest(lang.Final):
65
70
  return HttpHeaders(self.headers) if self.headers is not None else None
66
71
 
67
72
 
73
+ #
74
+
75
+
68
76
  @dc.dataclass(frozen=True, kw_only=True)
69
- class HttpResponse(lang.Final):
77
+ class BaseHttpResponse(lang.Abstract):
70
78
  status: int
71
79
 
72
80
  headers: HttpHeaders | None = dc.xfield(None, repr=dc.truthy_repr)
73
- data: bytes | None = dc.xfield(None, repr_fn=lambda v: '...' if v is not None else None)
74
81
 
75
82
  request: HttpRequest
76
83
  underlying: ta.Any = dc.field(default=None, repr=False)
77
84
 
78
- #
79
-
80
85
  @property
81
86
  def is_success(self) -> bool:
82
87
  return is_success_status(self.status)
83
88
 
84
89
 
90
+ @dc.dataclass(frozen=True, kw_only=True)
91
+ class HttpResponse(BaseHttpResponse, lang.Final):
92
+ data: bytes | None = dc.xfield(None, repr_fn=lambda v: '...' if v is not None else None)
93
+
94
+
95
+ @dc.dataclass(frozen=True, kw_only=True)
96
+ class StreamHttpResponse(BaseHttpResponse, lang.Final):
97
+ class Stream(ta.Protocol):
98
+ def read(self, /, n: int = -1) -> bytes: ...
99
+
100
+ stream: Stream
101
+
102
+ _closer: ta.Callable[[], None] | None = dc.field(default=None, repr=False)
103
+
104
+ def __enter__(self) -> ta.Self:
105
+ return self
106
+
107
+ def __exit__(self, exc_type, exc_val, exc_tb):
108
+ self.close()
109
+
110
+ def close(self) -> None:
111
+ if (c := self._closer) is not None:
112
+ c()
113
+
114
+
115
+ def close_response(resp: BaseHttpResponse) -> None:
116
+ if isinstance(resp, HttpResponse):
117
+ pass
118
+
119
+ elif isinstance(resp, StreamHttpResponse):
120
+ resp.close()
121
+
122
+ else:
123
+ raise TypeError(resp)
124
+
125
+
126
+ @contextlib.contextmanager
127
+ def closing_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
128
+ if isinstance(resp, HttpResponse):
129
+ yield resp # type: ignore
130
+ return
131
+
132
+ elif isinstance(resp, StreamHttpResponse):
133
+ with contextlib.closing(resp):
134
+ yield resp # type: ignore
135
+
136
+ else:
137
+ raise TypeError(resp)
138
+
139
+
140
+ def read_response(resp: BaseHttpResponse) -> HttpResponse:
141
+ if isinstance(resp, HttpResponse):
142
+ return resp
143
+
144
+ elif isinstance(resp, StreamHttpResponse):
145
+ data = resp.stream.read()
146
+ return HttpResponse(**{
147
+ **{k: v for k, v in dc.shallow_asdict(resp).items() if k not in ('stream', '_closer')},
148
+ 'data': data,
149
+ })
150
+
151
+ else:
152
+ raise TypeError(resp)
153
+
154
+
155
+ #
156
+
157
+
85
158
  class HttpClientError(Exception):
86
159
  @property
87
160
  def cause(self) -> BaseException | None:
@@ -93,6 +166,9 @@ class HttpStatusError(HttpClientError):
93
166
  response: HttpResponse
94
167
 
95
168
 
169
+ #
170
+
171
+
96
172
  class HttpClient(lang.Abstract):
97
173
  def __enter__(self) -> ta.Self:
98
174
  return self
@@ -106,19 +182,36 @@ class HttpClient(lang.Abstract):
106
182
  *,
107
183
  check: bool = False,
108
184
  ) -> HttpResponse:
109
- resp = self._request(req)
185
+ with closing_response(self.stream_request(
186
+ req,
187
+ check=check,
188
+ )) as resp:
189
+ return read_response(resp)
110
190
 
111
- if check and not resp.is_success:
112
- if isinstance(resp.underlying, Exception):
113
- cause = resp.underlying
114
- else:
115
- cause = None
116
- raise HttpStatusError(resp) from cause
191
+ def stream_request(
192
+ self,
193
+ req: HttpRequest,
194
+ *,
195
+ check: bool = False,
196
+ ) -> StreamHttpResponse:
197
+ resp = self._stream_request(req)
198
+
199
+ try:
200
+ if check and not resp.is_success:
201
+ if isinstance(resp.underlying, Exception):
202
+ cause = resp.underlying
203
+ else:
204
+ cause = None
205
+ raise HttpStatusError(read_response(resp)) from cause # noqa
206
+
207
+ except Exception:
208
+ close_response(resp)
209
+ raise
117
210
 
118
211
  return resp
119
212
 
120
213
  @abc.abstractmethod
121
- def _request(self, req: HttpRequest) -> HttpResponse:
214
+ def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
122
215
  raise NotImplementedError
123
216
 
124
217
 
@@ -126,7 +219,7 @@ class HttpClient(lang.Abstract):
126
219
 
127
220
 
128
221
  class UrllibHttpClient(HttpClient):
129
- def _request(self, req: HttpRequest) -> HttpResponse:
222
+ def _build_request(self, req: HttpRequest) -> urllib.request.Request:
130
223
  d: ta.Any
131
224
  if (d := req.data) is not None:
132
225
  if isinstance(d, str):
@@ -143,44 +236,73 @@ class UrllibHttpClient(HttpClient):
143
236
  for k, v in hs.strict_dct.items():
144
237
  h[k.decode('ascii')] = v.decode('ascii')
145
238
 
239
+ return urllib.request.Request( # noqa
240
+ req.url,
241
+ method=req.method_or_default,
242
+ headers=h,
243
+ data=d,
244
+ )
245
+
246
+ def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
146
247
  try:
147
- with urllib.request.urlopen( # noqa
148
- urllib.request.Request( # noqa
149
- req.url,
150
- method=req.method_or_default,
151
- headers=h,
152
- data=d,
153
- ),
154
- timeout=req.timeout_s,
155
- ) as resp:
156
- return HttpResponse(
157
- status=resp.status,
158
- headers=HttpHeaders(resp.headers.items()),
159
- data=resp.read(),
248
+ resp = urllib.request.urlopen( # noqa
249
+ self._build_request(req),
250
+ timeout=req.timeout_s,
251
+ )
252
+
253
+ except urllib.error.HTTPError as e:
254
+ try:
255
+ return StreamHttpResponse(
256
+ status=e.code,
257
+ headers=HttpHeaders(e.headers.items()),
160
258
  request=req,
161
- underlying=resp,
259
+ underlying=e,
260
+ stream=e,
261
+ _closer=e.close,
162
262
  )
163
263
 
164
- except urllib.error.HTTPError as e:
165
- return HttpResponse(
166
- status=e.code,
167
- headers=HttpHeaders(e.headers.items()),
168
- data=e.read(),
169
- request=req,
170
- underlying=e,
171
- )
264
+ except Exception:
265
+ e.close()
266
+ raise
172
267
 
173
268
  except (urllib.error.URLError, http.client.HTTPException) as e:
174
269
  raise HttpClientError from e
175
270
 
271
+ try:
272
+ return StreamHttpResponse(
273
+ status=resp.status,
274
+ headers=HttpHeaders(resp.headers.items()),
275
+ request=req,
276
+ underlying=resp,
277
+ stream=resp,
278
+ _closer=resp.close,
279
+ )
280
+
281
+ except Exception: # noqa
282
+ resp.close()
283
+ raise
284
+
176
285
 
177
286
  ##
178
287
 
179
288
 
180
289
  class HttpxHttpClient(HttpClient):
181
- def _request(self, req: HttpRequest) -> HttpResponse:
290
+ @dc.dataclass(frozen=True)
291
+ class _StreamAdapter:
292
+ it: ta.Iterator[bytes]
293
+
294
+ def read(self, /, n: int = -1) -> bytes:
295
+ if n < 0:
296
+ return b''.join(self.it)
297
+ else:
298
+ try:
299
+ return next(self.it)
300
+ except StopIteration:
301
+ return b''
302
+
303
+ def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
182
304
  try:
183
- response = httpx.request(
305
+ resp_cm = httpx.stream(
184
306
  method=req.method_or_default,
185
307
  url=req.url,
186
308
  headers=req.headers_ or None, # type: ignore
@@ -188,17 +310,30 @@ class HttpxHttpClient(HttpClient):
188
310
  timeout=req.timeout_s,
189
311
  )
190
312
 
191
- return HttpResponse(
192
- status=response.status_code,
193
- headers=HttpHeaders(response.headers.raw),
194
- data=response.content,
313
+ except httpx.HTTPError as e:
314
+ raise HttpClientError from e
315
+
316
+ resp_close = functools.partial(resp_cm.__exit__, None, None, None)
317
+
318
+ try:
319
+ resp = resp_cm.__enter__()
320
+ return StreamHttpResponse(
321
+ status=resp.status_code,
322
+ headers=HttpHeaders(resp.headers.raw),
195
323
  request=req,
196
- underlying=response,
324
+ underlying=resp,
325
+ stream=self._StreamAdapter(resp.iter_bytes()),
326
+ _closer=resp_close, # type: ignore
197
327
  )
198
328
 
199
329
  except httpx.HTTPError as e:
330
+ resp_close()
200
331
  raise HttpClientError from e
201
332
 
333
+ except Exception:
334
+ resp_close()
335
+ raise
336
+
202
337
 
203
338
  ##
204
339
 
@@ -222,7 +357,7 @@ def request(
222
357
 
223
358
  check: bool = False,
224
359
 
225
- client: HttpClient | None = None,
360
+ client: HttpClient | None = None, # noqa
226
361
 
227
362
  **kwargs: ta.Any,
228
363
  ) -> HttpResponse:
omlish/http/sse.py CHANGED
@@ -12,6 +12,9 @@ from .. import dataclasses as dc
12
12
  from .. import lang
13
13
 
14
14
 
15
+ ##
16
+
17
+
15
18
  class SseDecoderOutput(lang.Abstract):
16
19
  pass
17
20
 
omlish/io/buffers.py CHANGED
@@ -1,4 +1,4 @@
1
- # ruff: noqa: UP007
1
+ # ruff: noqa: UP006 UP007
2
2
  # @omlish-lite
3
3
  import io
4
4
  import typing as ta
@@ -7,9 +7,16 @@ from ..lite.check import check
7
7
  from ..lite.strings import attr_repr
8
8
 
9
9
 
10
+ ##
11
+
12
+
10
13
  class DelimitingBuffer:
11
14
  """
12
15
  https://github.com/python-trio/trio/issues/796 :|
16
+
17
+ FIXME: when given overlapping delimiters like [b'\r', b'\r\n'], *should* refuse to output a line ending in '\r'
18
+ without knowing it will not be followed by '\n'. does not currently do this - currently only picks longest
19
+ delimiter present in the buffer. does this need a prefix-trie? is this borderline parsing?
13
20
  """
14
21
 
15
22
  #
@@ -31,19 +38,37 @@ class DelimitingBuffer:
31
38
 
32
39
  def __init__(
33
40
  self,
34
- delimiters: ta.Iterable[int] = DEFAULT_DELIMITERS,
41
+ delimiters: ta.Iterable[ta.Union[int, bytes]] = DEFAULT_DELIMITERS,
35
42
  *,
36
43
  keep_ends: bool = False,
37
44
  max_size: ta.Optional[int] = None,
38
45
  ) -> None:
39
46
  super().__init__()
40
47
 
41
- self._delimiters = frozenset(check.isinstance(d, int) for d in delimiters)
48
+ ds: ta.Set[bytes] = set()
49
+ for d in delimiters:
50
+ if isinstance(d, int):
51
+ d = bytes([d])
52
+ ds.add(check.isinstance(d, bytes))
53
+
54
+ self._delimiters: ta.FrozenSet[bytes] = frozenset(ds)
42
55
  self._keep_ends = keep_ends
43
56
  self._max_size = max_size
44
57
 
45
58
  self._buf: ta.Optional[io.BytesIO] = io.BytesIO()
46
59
 
60
+ ddl = {}
61
+ dl = sorted(self._delimiters, key=lambda d: -len(d))
62
+ for i, d in enumerate(dl):
63
+ for j, d2 in enumerate(dl):
64
+ if len(d2) < len(d):
65
+ break
66
+ if i == j or not d2.startswith(d):
67
+ continue
68
+ ddl[d] = len(d2)
69
+ break
70
+ self._delimiter_disambiguation_lens: ta.Dict[bytes, int] = ddl
71
+
47
72
  #
48
73
 
49
74
  @property
@@ -60,13 +85,30 @@ class DelimitingBuffer:
60
85
  raise self.ClosedError(self)
61
86
  return buf.getvalue()
62
87
 
63
- def _find_delim(self, data: ta.Union[bytes, bytearray], i: int) -> ta.Optional[int]:
64
- r = None # type: int | None
88
+ def _find_delim(self, data: ta.Union[bytes, bytearray], i: int) -> ta.Optional[ta.Tuple[int, int]]:
89
+ rp = None # type: int | None
90
+ rl = None # type: int | None
91
+ rd = None # type: bytes | None
92
+
65
93
  for d in self._delimiters:
66
- if (p := data.find(d, i)) >= 0:
67
- if r is None or p < r:
68
- r = p
69
- return r
94
+ if (p := data.find(d, i)) < 0:
95
+ continue
96
+
97
+ dl = len(d)
98
+
99
+ if rp is None or p < rp:
100
+ rp, rl, rd = p, dl, d
101
+ elif rp == p and rl < dl: # type: ignore
102
+ rl, rd = dl, d # noqa
103
+
104
+ if rp is None:
105
+ return None
106
+
107
+ # FIXME:
108
+ # if (ddl := self._delimiter_disambiguation_lens.get(rd)) is not None:
109
+ # raise NotImplementedError
110
+
111
+ return rp, rl # type: ignore
70
112
 
71
113
  def _append_and_reset(self, chunk: bytes) -> bytes:
72
114
  buf = check.not_none(self._buf)
@@ -97,10 +139,11 @@ class DelimitingBuffer:
97
139
  l = len(data)
98
140
  i = 0
99
141
  while i < l:
100
- if (p := self._find_delim(data, i)) is None:
142
+ if (pt := self._find_delim(data, i)) is None:
101
143
  break
102
144
 
103
- n = p + 1
145
+ p, pl = pt
146
+ n = p + pl
104
147
  if self._keep_ends:
105
148
  p = n
106
149
 
@@ -128,6 +171,9 @@ class DelimitingBuffer:
128
171
  i = p
129
172
 
130
173
 
174
+ ##
175
+
176
+
131
177
  class ReadableListBuffer:
132
178
  # FIXME: merge with PrependableGeneratorReader
133
179
 
@@ -199,6 +245,9 @@ class ReadableListBuffer:
199
245
  return r if isinstance(r, bytes) else None
200
246
 
201
247
 
248
+ ##
249
+
250
+
202
251
  class IncrementalWriteBuffer:
203
252
  def __init__(
204
253
  self,
@@ -16,6 +16,10 @@ from .holder import ( # noqa
16
16
  TypedValueHolder,
17
17
  )
18
18
 
19
+ from .reflect import ( # noqa
20
+ reflect_typed_values_impls,
21
+ )
22
+
19
23
  from .values import ( # noqa
20
24
  TypedValue,
21
25
 
@@ -129,6 +129,13 @@ class TypedValues(
129
129
 
130
130
  #
131
131
 
132
+ def check_all_isinstance(self, ty: type | tuple[type, ...]) -> ta.Self:
133
+ for tv in self._tup:
134
+ check.isinstance(tv, ty)
135
+ return self
136
+
137
+ #
138
+
132
139
  _hash: int
133
140
 
134
141
  def __hash__(self) -> int:
@@ -1,5 +1,3 @@
1
- import typing as ta
2
-
3
1
  from omlish import check
4
2
  from omlish import dataclasses as dc
5
3
  from omlish import lang
@@ -8,6 +6,7 @@ from omlish import reflect as rfl
8
6
  from omlish.funcs import match as mfs
9
7
 
10
8
  from .collection import TypedValues
9
+ from .reflect import reflect_typed_values_impls
11
10
  from .values import ScalarTypedValue
12
11
  from .values import TypedValue
13
12
 
@@ -83,50 +82,35 @@ def _build_typed_values_impls(rty: rfl.Type) -> msh.Impls:
83
82
  gty = check.isinstance(rty, rfl.Generic)
84
83
  check.is_(gty.cls, TypedValues)
85
84
 
86
- opt_cls_set: set[type[TypedValue]] = set()
87
-
88
- todo = [check.single(gty.args)]
89
- seen = set()
90
- while todo:
91
- cur = todo.pop()
92
- if cur in seen:
93
- continue
94
- seen.add(cur)
95
-
96
- if isinstance(cur, rfl.Union):
97
- todo.extend(cur.args)
98
- elif isinstance(cur, ta.TypeVar):
99
- todo.append(rfl.get_type_var_bound(cur))
100
- else:
101
- opt_cls_set.add(check.issubclass(check.isinstance(cur, type), TypedValue))
85
+ tv_cls_set = reflect_typed_values_impls(check.single(gty.args))
102
86
 
103
- opt_impls: list[msh.Impl] = []
104
- for opt_cls in opt_cls_set:
105
- opt_impls.extend(_build_typed_value_poly(opt_cls).impls)
87
+ tv_impls: list[msh.Impl] = []
88
+ for tv_cls in tv_cls_set:
89
+ tv_impls.extend(_build_typed_value_poly(tv_cls).impls)
106
90
 
107
- return msh.Impls(opt_impls)
91
+ return msh.Impls(tv_impls)
108
92
 
109
93
 
110
94
  class TypedValuesMarshalerFactory(msh.MarshalerFactoryMatchClass):
111
95
  @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and rty.cls is TypedValues)
112
96
  def _build(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
113
- opt_m = msh.make_polymorphism_marshaler(
97
+ tv_m = msh.make_polymorphism_marshaler(
114
98
  msh.Impls(_build_typed_values_impls(rty)),
115
99
  msh.WrapperTypeTagging(),
116
100
  ctx,
117
101
  )
118
- return msh.IterableMarshaler(opt_m)
102
+ return msh.IterableMarshaler(tv_m)
119
103
 
120
104
 
121
105
  class TypedValuesUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
122
106
  @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and rty.cls is TypedValues)
123
107
  def _build(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
124
- opt_u = msh.make_polymorphism_unmarshaler(
108
+ tv_u = msh.make_polymorphism_unmarshaler(
125
109
  msh.Impls(_build_typed_values_impls(rty)),
126
110
  msh.WrapperTypeTagging(),
127
111
  ctx,
128
112
  )
129
- return msh.IterableUnmarshaler(lambda it: TypedValues(*it), opt_u) # noqa
113
+ return msh.IterableUnmarshaler(lambda it: TypedValues(*it), tv_u) # noqa
130
114
 
131
115
 
132
116
  ##
@@ -0,0 +1,30 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import reflect as rfl
5
+
6
+ from .values import TypedValue
7
+
8
+
9
+ ##
10
+
11
+
12
+ def reflect_typed_values_impls(rty: rfl.Type) -> set[type[TypedValue]]:
13
+ tv_cls_set: set[type[TypedValue]] = set()
14
+
15
+ todo = [rty]
16
+ seen = set()
17
+ while todo:
18
+ cur = todo.pop()
19
+ if cur in seen:
20
+ continue
21
+ seen.add(cur)
22
+
23
+ if isinstance(cur, rfl.Union):
24
+ todo.extend(cur.args)
25
+ elif isinstance(cur, ta.TypeVar):
26
+ todo.append(rfl.get_type_var_bound(cur))
27
+ else:
28
+ tv_cls_set.add(check.issubclass(check.isinstance(cur, type), TypedValue))
29
+
30
+ return tv_cls_set
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev276
3
+ Version: 0.0.0.dev277
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=pjGUyLHaoWpPqRP3jz2u1fC1qoRc2lvrEcpU_Ax2tdg,8253
2
- omlish/__about__.py,sha256=FoTicGOCebKQ5tQVQjrcXhtN_4ZJBQMrDN3RO12ykvQ,3380
2
+ omlish/__about__.py,sha256=LsJ6JjvAcEVPpuFaB33e_08fA_C2VKyJfcYRyZuU2RM,3380
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
@@ -329,9 +329,9 @@ omlish/graphs/dot/make.py,sha256=RN30gHfJPiXx5Q51kbDdhVJYf59Fr84Lz9J-mXRt9sI,360
329
329
  omlish/graphs/dot/rendering.py,sha256=eN1NufiUaKFeRxRTOfIT-oLTg1YQCuPybBx3AmrhdBw,3629
330
330
  omlish/graphs/dot/utils.py,sha256=_FMwn77WfiiAfLsRTOKWm4IYbNv5kQN22YJ5psw6CWg,801
331
331
  omlish/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
332
- omlish/http/all.py,sha256=OqCovZi_jv1Mnk975idaXA8FCGy4laoQIvNZ3hdKpRQ,722
332
+ omlish/http/all.py,sha256=4dSBbitsrQIadivSo2rBLg8dgdmC0efpboylGUZgKKo,829
333
333
  omlish/http/asgi.py,sha256=wXhBZ21bEl32Kv9yBrRwUR_7pHEgVtHP8ZZwbasQ6-4,3307
334
- omlish/http/clients.py,sha256=WRtCNIt9Y790xpem69HiXJY9W-vPlpGPKZwpHusa2EE,6280
334
+ omlish/http/clients.py,sha256=gbR4Fl2C1fYpcfDEg_W9De3ImRARKFkRHoQkkG1AwRs,9558
335
335
  omlish/http/consts.py,sha256=7BJ4D1MdIvqBcepkgCfBFHolgTwbOlqsOEiee_IjxOA,2289
336
336
  omlish/http/cookies.py,sha256=uuOYlHR6e2SC3GM41V0aozK10nef9tYg83Scqpn5-HM,6351
337
337
  omlish/http/dates.py,sha256=Otgp8wRxPgNGyzx8LFowu1vC4EKJYARCiAwLFncpfHM,2875
@@ -343,7 +343,7 @@ omlish/http/jwt.py,sha256=6Rigk1WrJ059DY4jDIKnxjnChWb7aFdermj2AI2DSvk,4346
343
343
  omlish/http/multipart.py,sha256=R9ycpHsXRcmh0uoc43aYb7BdWL-8kSQHe7J-M81aQZM,2240
344
344
  omlish/http/parsing.py,sha256=Kz1n-2SwqSbRtPk1nlImf655c2YtGLW4WlTI82o3c1w,14356
345
345
  omlish/http/sessions.py,sha256=TfTJ_j-6c9PelG_RmijEwozfaVm3O7YzgtFvp8VzQqM,4799
346
- omlish/http/sse.py,sha256=MDs9RvxQXoQliImcc6qK1ERajEYM7Q1l8xmr-9ceNBc,2315
346
+ omlish/http/sse.py,sha256=NwJnQj-hFXAkadXKhUuHSnbXHwDVJjhzfdkkHQ-prQo,2320
347
347
  omlish/http/versions.py,sha256=wSiOXPiClVjkVgSU_VmxkoD1SUYGaoPbP0U5Aw-Ufg8,409
348
348
  omlish/http/wsgi.py,sha256=czZsVUX-l2YTlMrUjKN49wRoP4rVpS0qpeBn4O5BoMY,948
349
349
  omlish/http/coro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -383,7 +383,7 @@ omlish/inject/impl/proxy.py,sha256=gyCME_W48Zrl7QJMKiIPwGpSctf5fhvh4ZldgtA9MEE,1
383
383
  omlish/inject/impl/scopes.py,sha256=_gIRFsFBLDVAUIrw9burDu4ph9TtgI-ksZWnHaKenKI,5925
384
384
  omlish/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
385
385
  omlish/io/abc.py,sha256=M40QB2udYpCEqmlxCcHv6FlJYJY6ymmJQBlaklYv0U8,1256
386
- omlish/io/buffers.py,sha256=Pu8CZUfiTJO_qq7cuPYAnNIag_7LJfr4ODaICsFZ6IU,5855
386
+ omlish/io/buffers.py,sha256=9PcGefTq4Lj-qGQK4-fI67SLRBHZ-OkiY3x2pU38-e0,7279
387
387
  omlish/io/fileno.py,sha256=QiVuRfqJRqP1aoLS82AVHOo_rt0lijZHfM21s42uaTo,174
388
388
  omlish/io/pyio.py,sha256=q4RBFVpBE5PYjnGPGT-_4pcZb7dFJmLJ4LtI8OoDRQY,95433
389
389
  omlish/io/trampoline.py,sha256=oUKTQg1F5xQS1431Kt7MbK-NZpX509ubcXU-s86xJr8,7171
@@ -776,16 +776,17 @@ omlish/text/parts.py,sha256=Q9NvoyEGQKIWgiPD4D_Qc66cWAuyEKE033dT9m7c3Wk,6662
776
776
  omlish/text/random.py,sha256=8feS5JE_tSjYlMl-lp0j93kCfzBae9AM2cXlRLebXMA,199
777
777
  omlish/text/go/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
778
778
  omlish/text/go/quoting.py,sha256=N9EYdnFdEX_A8fOviH-1w4jwV3XOQ7VU2WsoUNubYVY,9137
779
- omlish/typedvalues/__init__.py,sha256=YalNleXS2vQX_3dOIMwfb4n_uBkENTT4QXHnsHatGGM,496
779
+ omlish/typedvalues/__init__.py,sha256=c3IQmRneMmH6JRcafprqmBILWD89b-IyIll6MgahGCI,562
780
780
  omlish/typedvalues/accessor.py,sha256=0k21N-CkjGaY6zCwugsRfOC_CDkqk7wNz4oxO1_6EEA,2919
781
- omlish/typedvalues/collection.py,sha256=jsXSggmMMvGATcJgQkUXt5Guwq8aquw73_OIC-e6U0I,5300
781
+ omlish/typedvalues/collection.py,sha256=OUFL1Ggj5ozxO4C3eRPfDPzGwNitpRuoSJPc_QbmMNI,5470
782
782
  omlish/typedvalues/generic.py,sha256=byWG_gMXhNelckUwdmOoJE9FKkL71Q4BSi4ZLyy0XZ0,788
783
783
  omlish/typedvalues/holder.py,sha256=4SwRezsmuDDEO5gENGx8kTm30pblF5UktoEAu02i-Gk,1554
784
- omlish/typedvalues/marshal.py,sha256=H1gQuFHHgFR5qTs0lM7VXK41BivSbOXC9I2XUpo5SXU,4603
784
+ omlish/typedvalues/marshal.py,sha256=eWMrmuzPk3pX5AlILc5YBvuJBUHRQA_vwkxRm5aHiGs,4209
785
+ omlish/typedvalues/reflect.py,sha256=JO3gMpJm1XAFGAlXglE_U1MCXFV-L_geImyb3CGgFIU,672
785
786
  omlish/typedvalues/values.py,sha256=Acyf6xSdNHxrkRXLXrFqJouk35YOveso1VqTbyPwQW4,1223
786
- omlish-0.0.0.dev276.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
787
- omlish-0.0.0.dev276.dist-info/METADATA,sha256=_xEeojOHXbc_azzKB0NydBuLd1aMhKO1l65YCPUaqNE,4198
788
- omlish-0.0.0.dev276.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
789
- omlish-0.0.0.dev276.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
790
- omlish-0.0.0.dev276.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
791
- omlish-0.0.0.dev276.dist-info/RECORD,,
787
+ omlish-0.0.0.dev277.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
788
+ omlish-0.0.0.dev277.dist-info/METADATA,sha256=WJ5keRK7rdyZD3UZsZQt2ybc4foSZ3L1GSJLZI6zJXY,4198
789
+ omlish-0.0.0.dev277.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
790
+ omlish-0.0.0.dev277.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
791
+ omlish-0.0.0.dev277.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
792
+ omlish-0.0.0.dev277.dist-info/RECORD,,