soia-client 1.0.29__py3-none-any.whl → 1.1.0__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.
Potentially problematic release.
This version of soia-client might be problematic. Click here for more details.
- soia/__init__.py +3 -0
- soia/_impl/keep.py +20 -0
- soia/_impl/primitives.py +8 -4
- soia/_impl/structs.py +90 -21
- soia/_impl/timestamp.py +41 -4
- {soia_client-1.0.29.dist-info → soia_client-1.1.0.dist-info}/METADATA +1 -1
- {soia_client-1.0.29.dist-info → soia_client-1.1.0.dist-info}/RECORD +10 -9
- {soia_client-1.0.29.dist-info → soia_client-1.1.0.dist-info}/WHEEL +0 -0
- {soia_client-1.0.29.dist-info → soia_client-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {soia_client-1.0.29.dist-info → soia_client-1.1.0.dist-info}/top_level.txt +0 -0
soia/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import typing as _typing
|
|
2
2
|
|
|
3
|
+
from soia._impl.keep import KEEP, Keep
|
|
3
4
|
from soia._impl.keyed_items import KeyedItems
|
|
4
5
|
from soia._impl.method import Method
|
|
5
6
|
from soia._impl.serializer import Serializer
|
|
@@ -16,6 +17,8 @@ _: _typing.Final[_typing.Any] = None
|
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
"_",
|
|
20
|
+
"Keep",
|
|
21
|
+
"KEEP",
|
|
19
22
|
"KeyedItems",
|
|
20
23
|
"Method",
|
|
21
24
|
"RawServiceResponse",
|
soia/_impl/keep.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Final, cast, final
|
|
3
|
+
|
|
4
|
+
from soia._impl.never import Never
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@final
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Keep:
|
|
10
|
+
"""
|
|
11
|
+
Type of the KEEP constant, which indicates that a value should not be replaced.
|
|
12
|
+
|
|
13
|
+
Do not instantiate.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, never: Never):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
KEEP: Final = Keep(cast(Never, None))
|
soia/_impl/primitives.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
from collections.abc import Callable
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from typing import Any, Final, final
|
|
@@ -280,8 +281,6 @@ STRING_ADAPTER: Final[TypeAdapter] = _StringAdapter()
|
|
|
280
281
|
|
|
281
282
|
|
|
282
283
|
class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
283
|
-
_fromhex_fn: Final = bytes.fromhex
|
|
284
|
-
|
|
285
284
|
def default_expr(self) -> ExprLike:
|
|
286
285
|
return 'b""'
|
|
287
286
|
|
|
@@ -296,11 +295,16 @@ class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
|
296
295
|
in_expr: ExprLike,
|
|
297
296
|
readable: bool,
|
|
298
297
|
) -> Expr:
|
|
299
|
-
return Expr.join(
|
|
298
|
+
return Expr.join(
|
|
299
|
+
Expr.local("b64encode", base64.b64encode),
|
|
300
|
+
"(",
|
|
301
|
+
in_expr,
|
|
302
|
+
").decode('utf-8')",
|
|
303
|
+
)
|
|
300
304
|
|
|
301
305
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
302
306
|
return Expr.join(
|
|
303
|
-
Expr.local("
|
|
307
|
+
Expr.local("b64decode", base64.b64decode), "(", json_expr, ' or "")'
|
|
304
308
|
)
|
|
305
309
|
|
|
306
310
|
def get_type(self) -> reflection.Type:
|
soia/_impl/structs.py
CHANGED
|
@@ -14,6 +14,7 @@ from soia._impl.function_maker import (
|
|
|
14
14
|
Params,
|
|
15
15
|
make_function,
|
|
16
16
|
)
|
|
17
|
+
from soia._impl.keep import KEEP
|
|
17
18
|
from soia._impl.repr import repr_impl
|
|
18
19
|
from soia._impl.type_adapter import TypeAdapter
|
|
19
20
|
|
|
@@ -134,8 +135,12 @@ class StructAdapter(TypeAdapter):
|
|
|
134
135
|
simple_class=simple_class,
|
|
135
136
|
),
|
|
136
137
|
)
|
|
137
|
-
mutable_class.__init__ =
|
|
138
|
-
frozen_class.
|
|
138
|
+
mutable_class.__init__ = _make_mutable_class_init_fn(fields)
|
|
139
|
+
frozen_class.partial = _make_partial_static_factory_method(
|
|
140
|
+
fields,
|
|
141
|
+
frozen_class,
|
|
142
|
+
)
|
|
143
|
+
frozen_class.replace = _make_replace_method(fields, frozen_class)
|
|
139
144
|
|
|
140
145
|
frozen_class.__eq__ = _make_eq_fn(fields)
|
|
141
146
|
frozen_class.__hash__ = cast(Any, _make_hash_fn(fields, self.record_hash))
|
|
@@ -270,13 +275,7 @@ def _make_frozen_class_init_fn(
|
|
|
270
275
|
params: Params = ["_self"]
|
|
271
276
|
if fields:
|
|
272
277
|
params.append("*")
|
|
273
|
-
for field in fields
|
|
274
|
-
params.append(
|
|
275
|
-
Param(
|
|
276
|
-
name=field.field.attribute,
|
|
277
|
-
default=field.type.default_expr(),
|
|
278
|
-
)
|
|
279
|
-
)
|
|
278
|
+
params.extend(field.field.attribute for field in fields)
|
|
280
279
|
|
|
281
280
|
builder = BodyBuilder()
|
|
282
281
|
# Since __setattr__() was overridden to raise errors in order to make the class
|
|
@@ -384,10 +383,85 @@ def _make_mutable_class_init_fn(fields: Sequence[_Field]) -> Callable[..., None]
|
|
|
384
383
|
)
|
|
385
384
|
|
|
386
385
|
|
|
387
|
-
def
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
def _make_partial_static_factory_method(
|
|
387
|
+
fields: Sequence[_Field],
|
|
388
|
+
frozen_class: type,
|
|
389
|
+
) -> Callable[..., None]:
|
|
390
|
+
"""
|
|
391
|
+
Returns the implementation of the partial() method of the frozen class.
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
params: Params = []
|
|
395
|
+
if fields:
|
|
396
|
+
params.append("*")
|
|
397
|
+
params.extend(
|
|
398
|
+
Param(
|
|
399
|
+
name=field.field.attribute,
|
|
400
|
+
default=field.type.default_expr(),
|
|
401
|
+
)
|
|
402
|
+
for field in fields
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
builder = BodyBuilder()
|
|
406
|
+
builder.append_ln(
|
|
407
|
+
"return ",
|
|
408
|
+
Expr.local("Frozen", frozen_class),
|
|
409
|
+
"(",
|
|
410
|
+
", ".join(
|
|
411
|
+
f"{field.field.attribute}={field.field.attribute}" for field in fields
|
|
412
|
+
),
|
|
413
|
+
")",
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
return make_function(
|
|
417
|
+
name="partial",
|
|
418
|
+
params=params,
|
|
419
|
+
body=builder.build(),
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _make_replace_method(
|
|
424
|
+
fields: Sequence[_Field],
|
|
425
|
+
frozen_class: type,
|
|
426
|
+
) -> Callable[..., None]:
|
|
427
|
+
"""
|
|
428
|
+
Returns the implementation of the replace() method of the frozen class.
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
keep_local = Expr.local("KEEP", KEEP)
|
|
432
|
+
params: Params = []
|
|
433
|
+
if fields:
|
|
434
|
+
params.append("*")
|
|
435
|
+
params.extend(
|
|
436
|
+
Param(
|
|
437
|
+
name=field.field.attribute,
|
|
438
|
+
default=keep_local,
|
|
439
|
+
)
|
|
440
|
+
for field in fields
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
def field_to_arg_assigment(attr: str) -> LineSpan:
|
|
444
|
+
return LineSpan.join(
|
|
445
|
+
f"{attr}=self.{attr} if {attr} is ", keep_local, " else {attr}"
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
builder = BodyBuilder()
|
|
449
|
+
builder.append_ln(
|
|
450
|
+
"return ",
|
|
451
|
+
Expr.local("Frozen", frozen_class),
|
|
452
|
+
"(",
|
|
453
|
+
LineSpan.join(
|
|
454
|
+
*(field_to_arg_assigment(field.field.attribute) for field in fields),
|
|
455
|
+
separator=", ",
|
|
456
|
+
),
|
|
457
|
+
")",
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
return make_function(
|
|
461
|
+
name="partial",
|
|
462
|
+
params=params,
|
|
463
|
+
body=builder.build(),
|
|
464
|
+
)
|
|
391
465
|
|
|
392
466
|
|
|
393
467
|
def _make_to_mutable_fn(
|
|
@@ -549,14 +623,9 @@ def _make_repr_fn(fields: Sequence[_Field]) -> Callable[[Any], str]:
|
|
|
549
623
|
for field in fields:
|
|
550
624
|
attribute = field.field.attribute
|
|
551
625
|
# is_not_default_expr only works on a frozen expression.
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
)
|
|
556
|
-
builder.append_ln("if is_mutable or ", is_not_default_expr, ":")
|
|
557
|
-
builder.append_ln(" r = ", repr_local, f"(self.{attribute})")
|
|
558
|
-
builder.append_ln(f" assignments.append(f'{attribute}={{r.indented}}')")
|
|
559
|
-
builder.append_ln(" any_complex = any_complex or r.complex")
|
|
626
|
+
builder.append_ln("r = ", repr_local, f"(self.{attribute})")
|
|
627
|
+
builder.append_ln(f"assignments.append(f'{attribute}={{r.indented}}')")
|
|
628
|
+
builder.append_ln("any_complex = any_complex or r.complex")
|
|
560
629
|
builder.append_ln("if len(assignments) <= 1 and not any_complex:")
|
|
561
630
|
builder.append_ln(" body = ''.join(assignments)")
|
|
562
631
|
builder.append_ln("else:")
|
soia/_impl/timestamp.py
CHANGED
|
@@ -5,6 +5,14 @@ from typing import Any, Final, Union, cast, final, overload
|
|
|
5
5
|
|
|
6
6
|
@final
|
|
7
7
|
class Timestamp:
|
|
8
|
+
"""
|
|
9
|
+
A number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
|
|
10
|
+
|
|
11
|
+
Does not contain any timezone information.
|
|
12
|
+
Convertible to and from datetime objects.
|
|
13
|
+
Immutable.
|
|
14
|
+
"""
|
|
15
|
+
|
|
8
16
|
__slots__ = ("unix_millis",)
|
|
9
17
|
|
|
10
18
|
unix_millis: int
|
|
@@ -26,7 +34,14 @@ class Timestamp:
|
|
|
26
34
|
|
|
27
35
|
@staticmethod
|
|
28
36
|
def from_datetime(dt: datetime.datetime) -> "Timestamp":
|
|
29
|
-
|
|
37
|
+
# dt.timestamp() mail fail if the year is not in [1970, 2038]
|
|
38
|
+
if dt.tzinfo is None:
|
|
39
|
+
timestamp = (
|
|
40
|
+
dt - _EPOCH_DT.astimezone().replace(tzinfo=None)
|
|
41
|
+
).total_seconds()
|
|
42
|
+
else:
|
|
43
|
+
timestamp = (dt - _EPOCH_DT).total_seconds()
|
|
44
|
+
return Timestamp.from_unix_seconds(timestamp)
|
|
30
45
|
|
|
31
46
|
@staticmethod
|
|
32
47
|
def now() -> "Timestamp":
|
|
@@ -41,9 +56,26 @@ class Timestamp:
|
|
|
41
56
|
return self.unix_millis / 1000.0
|
|
42
57
|
|
|
43
58
|
def to_datetime_or_raise(self) -> datetime.datetime:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
"""
|
|
60
|
+
Returns a datetime object representing the timestamp in UTC timezone.
|
|
61
|
+
|
|
62
|
+
Raises an exception if the timestamp is out of bounds for datetime.
|
|
63
|
+
If you don't want the exception, use 'to_datetime_or_limit()' instead.
|
|
64
|
+
"""
|
|
65
|
+
return _EPOCH_DT + datetime.timedelta(seconds=self.unix_seconds)
|
|
66
|
+
|
|
67
|
+
def to_datetime_or_limit(self) -> datetime.datetime:
|
|
68
|
+
"""
|
|
69
|
+
Returns a datetime object representing the timestamp in UTC timezone.
|
|
70
|
+
|
|
71
|
+
Clamps the timestamp to the minimum or maximum datetime if it is out of bounds.
|
|
72
|
+
"""
|
|
73
|
+
if self.unix_seconds <= (_MIN_DT_UTC - _EPOCH_DT).total_seconds():
|
|
74
|
+
return datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
|
|
75
|
+
elif self.unix_seconds >= (_MAX_DT_UTC - _EPOCH_DT).total_seconds():
|
|
76
|
+
return datetime.datetime.max.replace(tzinfo=datetime.timezone.utc)
|
|
77
|
+
else:
|
|
78
|
+
return self.to_datetime_or_raise()
|
|
47
79
|
|
|
48
80
|
def __add__(self, td: datetime.timedelta) -> "Timestamp":
|
|
49
81
|
return Timestamp(
|
|
@@ -126,3 +158,8 @@ class Timestamp:
|
|
|
126
158
|
setattr(Timestamp, "EPOCH", Timestamp.from_unix_millis(0))
|
|
127
159
|
setattr(Timestamp, "MIN", Timestamp.from_unix_millis(-8640000000000000))
|
|
128
160
|
setattr(Timestamp, "MAX", Timestamp.from_unix_millis(8640000000000000))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
_EPOCH_DT: Final = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
|
|
164
|
+
_MIN_DT_UTC: Final = datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
|
|
165
|
+
_MAX_DT_UTC: Final = datetime.datetime.max.replace(tzinfo=datetime.timezone.utc)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
soia/__init__.py,sha256=
|
|
1
|
+
soia/__init__.py,sha256=WPhdlu7zKBSWd4DVFWXQKQoptD0gU7xGFm4tbQHrVc8,787
|
|
2
2
|
soia/_module_initializer.py,sha256=1TWnc0qUO7_iljQR2ywVERrrkY5jIkT3zucc-AYZyrQ,4221
|
|
3
3
|
soia/_spec.py,sha256=Y5EHHQa6qNeJc29aaqGrFPnPFXxlL7TED9_AXUGBjf0,3663
|
|
4
4
|
soia/reflection.py,sha256=U5knJGmawARCdcEhNxek4dvx48WLPETLqIqKBPWwT4Q,8771
|
|
@@ -6,21 +6,22 @@ soia/_impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
6
6
|
soia/_impl/arrays.py,sha256=C3j3RZdSvqmogbGAi3EprLkUYMFfO-IJhoCOte5blAw,5477
|
|
7
7
|
soia/_impl/enums.py,sha256=jlg2YYGgMg6BMmSy0v0MBgZmZYzLvVS0j68TxOCiSm8,17039
|
|
8
8
|
soia/_impl/function_maker.py,sha256=MvCDv1WwzKGJzZbNCnJ_8-MP3m1xTIabumXA-9Ydd9M,5639
|
|
9
|
+
soia/_impl/keep.py,sha256=GDeMKKUcvUFXWJVzBPB9CuLkcQb93sWKvo0PcyKUBzg,370
|
|
9
10
|
soia/_impl/keyed_items.py,sha256=EoPrdeLJ8i7zCH-oW-7qOcyadyrzykB6lkrf3xRVmyk,337
|
|
10
11
|
soia/_impl/method.py,sha256=C6ygh-Li4zADxFR_gfeBTuPUzLjZ1XHCp0ZI4-uiRYs,455
|
|
11
12
|
soia/_impl/never.py,sha256=T7PHtIipuXlici4wa0yuJKrn9JgcT-ODC3QVCwsPprY,46
|
|
12
13
|
soia/_impl/optionals.py,sha256=KTgmbfYLJLrju5v10jgnnnW98QG6NYw2We3gdgi9i2E,2414
|
|
13
|
-
soia/_impl/primitives.py,sha256=
|
|
14
|
+
soia/_impl/primitives.py,sha256=r-55rO12TwwWCQJgzEEgVpXMVGg0dRhGvFWX_rGeqsQ,9025
|
|
14
15
|
soia/_impl/repr.py,sha256=7WX0bEAVENTjlyZIcbT8TcJylS7IRIyafGCmqaIMxFM,1413
|
|
15
16
|
soia/_impl/serializer.py,sha256=28IwkjtUnLpbnPQfVNfJXkApCK4JhXHwLkC5MVhF8xo,3529
|
|
16
17
|
soia/_impl/serializers.py,sha256=IL9jHHMo11pgrL1-crarOEElvTyV5YM6FTcgumjW6IU,2564
|
|
17
18
|
soia/_impl/service.py,sha256=3dpcH4XEBTEShyjzxMA_zDGg9_amTFF5A49U6hivAdQ,14792
|
|
18
19
|
soia/_impl/service_client.py,sha256=0WgiN6Stg548FAdZQMuezZoooe4MYw2frEQ5KUE7xag,5311
|
|
19
|
-
soia/_impl/structs.py,sha256=
|
|
20
|
-
soia/_impl/timestamp.py,sha256=
|
|
20
|
+
soia/_impl/structs.py,sha256=z9PVPlwG36z6uCQFnl7QNfVjCldgPze4rDXJhhcco24,28221
|
|
21
|
+
soia/_impl/timestamp.py,sha256=upnEkvW-7AaMJ9_nNqntvhJ4pFWWCAXNjEOPtszdwgE,5645
|
|
21
22
|
soia/_impl/type_adapter.py,sha256=RyIyh4Fnt9rMy0HRzC-a2v2JAdZsV9FBzoGEUVygVRE,2101
|
|
22
|
-
soia_client-1.0.
|
|
23
|
-
soia_client-1.0.
|
|
24
|
-
soia_client-1.0.
|
|
25
|
-
soia_client-1.0.
|
|
26
|
-
soia_client-1.0.
|
|
23
|
+
soia_client-1.1.0.dist-info/licenses/LICENSE,sha256=SaAftKkX6hfSOiPdENQPS70tifH3PDHgazq8eK2Pwfw,1064
|
|
24
|
+
soia_client-1.1.0.dist-info/METADATA,sha256=6ojgF0DUendLMRkYBgyMSdTlDVR7rp1E4_XM2QtPBTQ,2121
|
|
25
|
+
soia_client-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
soia_client-1.1.0.dist-info/top_level.txt,sha256=lsYG9JrvauFe1oIV5zvnwsS9hsx3ztwfK_937op9mxc,5
|
|
27
|
+
soia_client-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|