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 +2 -2
- omlish/http/all.py +5 -0
- omlish/http/clients.py +180 -45
- omlish/http/sse.py +3 -0
- omlish/io/buffers.py +60 -11
- omlish/typedvalues/__init__.py +4 -0
- omlish/typedvalues/collection.py +7 -0
- omlish/typedvalues/marshal.py +10 -26
- omlish/typedvalues/reflect.py +30 -0
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/RECORD +15 -14
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev276.dist-info → omlish-0.0.0.dev277.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
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
|
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
|
-
|
185
|
+
with closing_response(self.stream_request(
|
186
|
+
req,
|
187
|
+
check=check,
|
188
|
+
)) as resp:
|
189
|
+
return read_response(resp)
|
110
190
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
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
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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=
|
259
|
+
underlying=e,
|
260
|
+
stream=e,
|
261
|
+
_closer=e.close,
|
162
262
|
)
|
163
263
|
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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=
|
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
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
|
-
|
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
|
-
|
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))
|
67
|
-
|
68
|
-
|
69
|
-
|
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 (
|
142
|
+
if (pt := self._find_delim(data, i)) is None:
|
101
143
|
break
|
102
144
|
|
103
|
-
|
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,
|
omlish/typedvalues/__init__.py
CHANGED
omlish/typedvalues/collection.py
CHANGED
@@ -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:
|
omlish/typedvalues/marshal.py
CHANGED
@@ -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
|
-
|
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
|
-
|
104
|
-
for
|
105
|
-
|
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(
|
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
|
-
|
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(
|
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
|
-
|
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),
|
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,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=pjGUyLHaoWpPqRP3jz2u1fC1qoRc2lvrEcpU_Ax2tdg,8253
|
2
|
-
omlish/__about__.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
787
|
-
omlish-0.0.0.
|
788
|
-
omlish-0.0.0.
|
789
|
-
omlish-0.0.0.
|
790
|
-
omlish-0.0.0.
|
791
|
-
omlish-0.0.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|