soia-client 1.0.30__py3-none-any.whl → 1.1.1__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.
- soia/__init__.py +3 -0
- soia/_impl/keep.py +20 -0
- soia/_impl/primitives.py +5 -2
- soia/_impl/structs.py +90 -21
- soia/_impl/timestamp.py +41 -4
- {soia_client-1.0.30.dist-info → soia_client-1.1.1.dist-info}/METADATA +1 -1
- {soia_client-1.0.30.dist-info → soia_client-1.1.1.dist-info}/RECORD +10 -9
- {soia_client-1.0.30.dist-info → soia_client-1.1.1.dist-info}/WHEEL +0 -0
- {soia_client-1.0.30.dist-info → soia_client-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {soia_client-1.0.30.dist-info → soia_client-1.1.1.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
|
@@ -3,11 +3,12 @@ from collections.abc import Callable
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Final, final
|
|
5
5
|
|
|
6
|
-
from soia import _spec, reflection
|
|
7
6
|
from soia._impl.function_maker import Expr, ExprLike
|
|
8
7
|
from soia._impl.timestamp import Timestamp
|
|
9
8
|
from soia._impl.type_adapter import TypeAdapter
|
|
10
9
|
|
|
10
|
+
from soia import _spec, reflection
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
class AbstractPrimitiveAdapter(TypeAdapter):
|
|
13
14
|
@final
|
|
@@ -296,7 +297,9 @@ class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
|
296
297
|
) -> Expr:
|
|
297
298
|
return Expr.join(
|
|
298
299
|
Expr.local("b64encode", base64.b64encode),
|
|
299
|
-
"(",
|
|
300
|
+
"(",
|
|
301
|
+
in_expr,
|
|
302
|
+
").decode('utf-8')",
|
|
300
303
|
)
|
|
301
304
|
|
|
302
305
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
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 = ["_self"]
|
|
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, f" 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="replace",
|
|
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=1XhjZmqtD7IMURdSVR0DwMdrIhWPZft1eCfwVW84c0E,28230
|
|
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.
|
|
23
|
-
soia_client-1.
|
|
24
|
-
soia_client-1.
|
|
25
|
-
soia_client-1.
|
|
26
|
-
soia_client-1.
|
|
23
|
+
soia_client-1.1.1.dist-info/licenses/LICENSE,sha256=SaAftKkX6hfSOiPdENQPS70tifH3PDHgazq8eK2Pwfw,1064
|
|
24
|
+
soia_client-1.1.1.dist-info/METADATA,sha256=WqVO1QsXh3w0mjkhuPC71WcCWlwSOnKeAX40_Mt2zFU,2121
|
|
25
|
+
soia_client-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
soia_client-1.1.1.dist-info/top_level.txt,sha256=lsYG9JrvauFe1oIV5zvnwsS9hsx3ztwfK_937op9mxc,5
|
|
27
|
+
soia_client-1.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|