cocoindex 0.1.50__cp312-cp312-macosx_11_0_arm64.whl → 0.1.52__cp312-cp312-macosx_11_0_arm64.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.
- cocoindex/__init__.py +55 -1
- cocoindex/_engine.cpython-312-darwin.so +0 -0
- cocoindex/cli.py +23 -6
- cocoindex/convert.py +7 -3
- cocoindex/flow.py +2 -2
- cocoindex/op.py +3 -3
- cocoindex/setting.py +10 -6
- cocoindex/{storages.py → targets.py} +8 -8
- cocoindex/tests/test_convert.py +151 -104
- cocoindex/tests/test_optional_database.py +249 -0
- cocoindex/tests/test_typing.py +62 -56
- cocoindex/typing.py +19 -19
- cocoindex/utils.py +7 -4
- {cocoindex-0.1.50.dist-info → cocoindex-0.1.52.dist-info}/METADATA +2 -2
- cocoindex-0.1.52.dist-info/RECORD +28 -0
- cocoindex-0.1.50.dist-info/RECORD +0 -27
- {cocoindex-0.1.50.dist-info → cocoindex-0.1.52.dist-info}/WHEEL +0 -0
- {cocoindex-0.1.50.dist-info → cocoindex-0.1.52.dist-info}/entry_points.txt +0 -0
- {cocoindex-0.1.50.dist-info → cocoindex-0.1.52.dist-info}/licenses/LICENSE +0 -0
cocoindex/tests/test_convert.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
import uuid
|
2
2
|
import datetime
|
3
3
|
from dataclasses import dataclass, make_dataclass
|
4
|
-
from typing import NamedTuple, Literal, Any, Callable
|
4
|
+
from typing import NamedTuple, Literal, Any, Callable, Union
|
5
5
|
import pytest
|
6
6
|
import cocoindex
|
7
7
|
from cocoindex.typing import (
|
8
8
|
encode_enriched_type,
|
9
9
|
Vector,
|
10
|
+
Float32,
|
11
|
+
Float64,
|
10
12
|
)
|
11
13
|
from cocoindex.convert import (
|
12
14
|
encode_engine_value,
|
@@ -32,7 +34,7 @@ class Tag:
|
|
32
34
|
|
33
35
|
@dataclass
|
34
36
|
class Basket:
|
35
|
-
items: list
|
37
|
+
items: list[str]
|
36
38
|
|
37
39
|
|
38
40
|
@dataclass
|
@@ -74,39 +76,50 @@ def build_engine_value_decoder(
|
|
74
76
|
|
75
77
|
|
76
78
|
def validate_full_roundtrip(
|
77
|
-
value: Any,
|
79
|
+
value: Any,
|
80
|
+
value_type: Any = None,
|
81
|
+
*other_decoded_values: tuple[Any, Any],
|
78
82
|
) -> None:
|
79
83
|
"""
|
80
84
|
Validate the given value doesn't change after encoding, sending to engine (using output_type), receiving back and decoding (using input_type).
|
81
85
|
|
82
|
-
|
86
|
+
`other_decoded_values` is a tuple of (value, type) pairs.
|
87
|
+
If provided, also validate the value can be decoded to the other types.
|
83
88
|
"""
|
84
|
-
from cocoindex import _engine
|
89
|
+
from cocoindex import _engine # type: ignore
|
85
90
|
|
86
91
|
encoded_value = encode_engine_value(value)
|
87
|
-
|
92
|
+
value_type = value_type or type(value)
|
93
|
+
encoded_output_type = encode_enriched_type(value_type)["type"]
|
88
94
|
value_from_engine = _engine.testutil.seder_roundtrip(
|
89
95
|
encoded_value, encoded_output_type
|
90
96
|
)
|
91
|
-
decoded_value = build_engine_value_decoder(
|
97
|
+
decoded_value = build_engine_value_decoder(value_type, value_type)(
|
92
98
|
value_from_engine
|
93
99
|
)
|
94
|
-
|
100
|
+
np.testing.assert_array_equal(decoded_value, value)
|
95
101
|
|
102
|
+
if other_decoded_values is not None:
|
103
|
+
for other_value, other_type in other_decoded_values:
|
104
|
+
other_decoded_value = build_engine_value_decoder(other_type, other_type)(
|
105
|
+
value_from_engine
|
106
|
+
)
|
107
|
+
np.testing.assert_array_equal(other_decoded_value, other_value)
|
96
108
|
|
97
|
-
|
109
|
+
|
110
|
+
def test_encode_engine_value_basic_types() -> None:
|
98
111
|
assert encode_engine_value(123) == 123
|
99
112
|
assert encode_engine_value(3.14) == 3.14
|
100
113
|
assert encode_engine_value("hello") == "hello"
|
101
114
|
assert encode_engine_value(True) is True
|
102
115
|
|
103
116
|
|
104
|
-
def test_encode_engine_value_uuid():
|
117
|
+
def test_encode_engine_value_uuid() -> None:
|
105
118
|
u = uuid.uuid4()
|
106
119
|
assert encode_engine_value(u) == u.bytes
|
107
120
|
|
108
121
|
|
109
|
-
def test_encode_engine_value_date_time_types():
|
122
|
+
def test_encode_engine_value_date_time_types() -> None:
|
110
123
|
d = datetime.date(2024, 1, 1)
|
111
124
|
assert encode_engine_value(d) == d
|
112
125
|
t = datetime.time(12, 30)
|
@@ -115,7 +128,7 @@ def test_encode_engine_value_date_time_types():
|
|
115
128
|
assert encode_engine_value(dt) == dt
|
116
129
|
|
117
130
|
|
118
|
-
def test_encode_engine_value_struct():
|
131
|
+
def test_encode_engine_value_struct() -> None:
|
119
132
|
order = Order(order_id="O123", name="mixed nuts", price=25.0)
|
120
133
|
assert encode_engine_value(order) == ["O123", "mixed nuts", 25.0, "default_extra"]
|
121
134
|
|
@@ -128,7 +141,7 @@ def test_encode_engine_value_struct():
|
|
128
141
|
]
|
129
142
|
|
130
143
|
|
131
|
-
def test_encode_engine_value_list_of_structs():
|
144
|
+
def test_encode_engine_value_list_of_structs() -> None:
|
132
145
|
orders = [Order("O1", "item1", 10.0), Order("O2", "item2", 20.0)]
|
133
146
|
assert encode_engine_value(orders) == [
|
134
147
|
["O1", "item1", 10.0, "default_extra"],
|
@@ -145,12 +158,12 @@ def test_encode_engine_value_list_of_structs():
|
|
145
158
|
]
|
146
159
|
|
147
160
|
|
148
|
-
def test_encode_engine_value_struct_with_list():
|
161
|
+
def test_encode_engine_value_struct_with_list() -> None:
|
149
162
|
basket = Basket(items=["apple", "banana"])
|
150
163
|
assert encode_engine_value(basket) == [["apple", "banana"]]
|
151
164
|
|
152
165
|
|
153
|
-
def test_encode_engine_value_nested_struct():
|
166
|
+
def test_encode_engine_value_nested_struct() -> None:
|
154
167
|
customer = Customer(name="Alice", order=Order("O1", "item1", 10.0))
|
155
168
|
assert encode_engine_value(customer) == [
|
156
169
|
"Alice",
|
@@ -168,12 +181,12 @@ def test_encode_engine_value_nested_struct():
|
|
168
181
|
]
|
169
182
|
|
170
183
|
|
171
|
-
def test_encode_engine_value_empty_list():
|
184
|
+
def test_encode_engine_value_empty_list() -> None:
|
172
185
|
assert encode_engine_value([]) == []
|
173
186
|
assert encode_engine_value([[]]) == [[]]
|
174
187
|
|
175
188
|
|
176
|
-
def test_encode_engine_value_tuple():
|
189
|
+
def test_encode_engine_value_tuple() -> None:
|
177
190
|
assert encode_engine_value(()) == []
|
178
191
|
assert encode_engine_value((1, 2, 3)) == [1, 2, 3]
|
179
192
|
assert encode_engine_value(((1, 2), (3, 4))) == [[1, 2], [3, 4]]
|
@@ -181,20 +194,23 @@ def test_encode_engine_value_tuple():
|
|
181
194
|
assert encode_engine_value(((),)) == [[]]
|
182
195
|
|
183
196
|
|
184
|
-
def test_encode_engine_value_none():
|
197
|
+
def test_encode_engine_value_none() -> None:
|
185
198
|
assert encode_engine_value(None) is None
|
186
199
|
|
187
200
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
201
|
+
def test_roundtrip_basic_types() -> None:
|
202
|
+
validate_full_roundtrip(42, int)
|
203
|
+
validate_full_roundtrip(3.25, float, (3.25, Float64))
|
204
|
+
validate_full_roundtrip(3.25, Float64, (3.25, float))
|
205
|
+
validate_full_roundtrip(3.25, Float32)
|
206
|
+
validate_full_roundtrip("hello", str)
|
207
|
+
validate_full_roundtrip(True, bool)
|
208
|
+
validate_full_roundtrip(False, bool)
|
209
|
+
validate_full_roundtrip(datetime.date(2025, 1, 1), datetime.date)
|
210
|
+
validate_full_roundtrip(datetime.datetime.now(), cocoindex.LocalDateTime)
|
211
|
+
validate_full_roundtrip(
|
212
|
+
datetime.datetime.now(datetime.UTC), cocoindex.OffsetDateTime
|
213
|
+
)
|
198
214
|
|
199
215
|
|
200
216
|
@pytest.mark.parametrize(
|
@@ -312,18 +328,18 @@ def test_make_engine_value_decoder_basic_types():
|
|
312
328
|
),
|
313
329
|
],
|
314
330
|
)
|
315
|
-
def test_struct_decoder_cases(data_type, engine_val, expected):
|
331
|
+
def test_struct_decoder_cases(data_type: Any, engine_val: Any, expected: Any) -> None:
|
316
332
|
decoder = build_engine_value_decoder(data_type)
|
317
333
|
assert decoder(engine_val) == expected
|
318
334
|
|
319
335
|
|
320
|
-
def
|
336
|
+
def test_make_engine_value_decoder_list_of_struct() -> None:
|
321
337
|
# List of structs (dataclass)
|
322
|
-
decoder = build_engine_value_decoder(list[Order])
|
323
338
|
engine_val = [
|
324
339
|
["O1", "item1", 10.0, "default_extra"],
|
325
340
|
["O2", "item2", 20.0, "default_extra"],
|
326
341
|
]
|
342
|
+
decoder = build_engine_value_decoder(list[Order])
|
327
343
|
assert decoder(engine_val) == [
|
328
344
|
Order("O1", "item1", 10.0, "default_extra"),
|
329
345
|
Order("O2", "item2", 20.0, "default_extra"),
|
@@ -336,13 +352,15 @@ def test_make_engine_value_decoder_collections():
|
|
336
352
|
OrderNamedTuple("O2", "item2", 20.0, "default_extra"),
|
337
353
|
]
|
338
354
|
|
355
|
+
|
356
|
+
def test_make_engine_value_decoder_struct_of_list() -> None:
|
339
357
|
# Struct with list field
|
340
|
-
decoder = build_engine_value_decoder(Customer)
|
341
358
|
engine_val = [
|
342
359
|
"Alice",
|
343
360
|
["O1", "item1", 10.0, "default_extra"],
|
344
361
|
[["vip"], ["premium"]],
|
345
362
|
]
|
363
|
+
decoder = build_engine_value_decoder(Customer)
|
346
364
|
assert decoder(engine_val) == Customer(
|
347
365
|
"Alice",
|
348
366
|
Order("O1", "item1", 10.0, "default_extra"),
|
@@ -357,8 +375,9 @@ def test_make_engine_value_decoder_collections():
|
|
357
375
|
[Tag("vip"), Tag("premium")],
|
358
376
|
)
|
359
377
|
|
378
|
+
|
379
|
+
def test_make_engine_value_decoder_struct_of_struct() -> None:
|
360
380
|
# Struct with struct field
|
361
|
-
decoder = build_engine_value_decoder(NestedStruct)
|
362
381
|
engine_val = [
|
363
382
|
["Alice", ["O1", "item1", 10.0, "default_extra"], [["vip"]]],
|
364
383
|
[
|
@@ -367,6 +386,7 @@ def test_make_engine_value_decoder_collections():
|
|
367
386
|
],
|
368
387
|
2,
|
369
388
|
]
|
389
|
+
decoder = build_engine_value_decoder(NestedStruct)
|
370
390
|
assert decoder(engine_val) == NestedStruct(
|
371
391
|
Customer("Alice", Order("O1", "item1", 10.0, "default_extra"), [Tag("vip")]),
|
372
392
|
[
|
@@ -377,11 +397,13 @@ def test_make_engine_value_decoder_collections():
|
|
377
397
|
)
|
378
398
|
|
379
399
|
|
380
|
-
def make_engine_order(fields):
|
400
|
+
def make_engine_order(fields: list[tuple[str, type]]) -> type:
|
381
401
|
return make_dataclass("EngineOrder", fields)
|
382
402
|
|
383
403
|
|
384
|
-
def make_python_order(
|
404
|
+
def make_python_order(
|
405
|
+
fields: list[tuple[str, type]], defaults: dict[str, Any] | None = None
|
406
|
+
) -> type:
|
385
407
|
if defaults is None:
|
386
408
|
defaults = {}
|
387
409
|
# Move all fields with defaults to the end (Python dataclass requirement)
|
@@ -455,8 +477,12 @@ def make_python_order(fields, defaults=None):
|
|
455
477
|
],
|
456
478
|
)
|
457
479
|
def test_field_position_cases(
|
458
|
-
engine_fields
|
459
|
-
|
480
|
+
engine_fields: list[tuple[str, type]],
|
481
|
+
python_fields: list[tuple[str, type]],
|
482
|
+
python_defaults: dict[str, Any],
|
483
|
+
engine_val: list[Any],
|
484
|
+
expected_python_val: tuple[Any, ...],
|
485
|
+
) -> None:
|
460
486
|
EngineOrder = make_engine_order(engine_fields)
|
461
487
|
PythonOrder = make_python_order(python_fields, python_defaults)
|
462
488
|
decoder = build_engine_value_decoder(EngineOrder, PythonOrder)
|
@@ -513,13 +539,13 @@ def test_roundtrip_ktable_struct_key() -> None:
|
|
513
539
|
validate_full_roundtrip(value_nt, t_nt)
|
514
540
|
|
515
541
|
|
516
|
-
IntVectorType = cocoindex.Vector[np.
|
542
|
+
IntVectorType = cocoindex.Vector[np.int64, Literal[5]]
|
517
543
|
|
518
544
|
|
519
545
|
def test_vector_as_vector() -> None:
|
520
|
-
value
|
546
|
+
value = np.array([1, 2, 3, 4, 5], dtype=np.int64)
|
521
547
|
encoded = encode_engine_value(value)
|
522
|
-
assert encoded
|
548
|
+
assert np.array_equal(encoded, value)
|
523
549
|
decoded = build_engine_value_decoder(IntVectorType)(encoded)
|
524
550
|
assert np.array_equal(decoded, value)
|
525
551
|
|
@@ -540,12 +566,17 @@ Float32VectorType = Vector[np.float32, Literal[3]]
|
|
540
566
|
Float64VectorType = Vector[np.float64, Literal[3]]
|
541
567
|
Int64VectorType = Vector[np.int64, Literal[3]]
|
542
568
|
Int32VectorType = Vector[np.int32, Literal[3]]
|
569
|
+
UInt8VectorType = Vector[np.uint8, Literal[3]]
|
570
|
+
UInt16VectorType = Vector[np.uint16, Literal[3]]
|
571
|
+
UInt32VectorType = Vector[np.uint32, Literal[3]]
|
572
|
+
UInt64VectorType = Vector[np.uint64, Literal[3]]
|
573
|
+
StrVectorType = Vector[str]
|
543
574
|
NDArrayFloat32Type = NDArray[np.float32]
|
544
575
|
NDArrayFloat64Type = NDArray[np.float64]
|
545
576
|
NDArrayInt64Type = NDArray[np.int64]
|
546
577
|
|
547
578
|
|
548
|
-
def test_encode_engine_value_ndarray():
|
579
|
+
def test_encode_engine_value_ndarray() -> None:
|
549
580
|
"""Test encoding NDArray vectors to lists for the Rust engine."""
|
550
581
|
vec_f32: Float32VectorType = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
551
582
|
assert np.array_equal(encode_engine_value(vec_f32), [1.0, 2.0, 3.0])
|
@@ -557,7 +588,7 @@ def test_encode_engine_value_ndarray():
|
|
557
588
|
assert np.array_equal(encode_engine_value(vec_nd_f32), [1.0, 2.0, 3.0])
|
558
589
|
|
559
590
|
|
560
|
-
def test_make_engine_value_decoder_ndarray():
|
591
|
+
def test_make_engine_value_decoder_ndarray() -> None:
|
561
592
|
"""Test decoding engine lists to NDArray vectors."""
|
562
593
|
decoder_f32 = build_engine_value_decoder(Float32VectorType)
|
563
594
|
result_f32 = decoder_f32([1.0, 2.0, 3.0])
|
@@ -581,16 +612,16 @@ def test_make_engine_value_decoder_ndarray():
|
|
581
612
|
assert np.array_equal(result_nd_f32, np.array([1.0, 2.0, 3.0], dtype=np.float32))
|
582
613
|
|
583
614
|
|
584
|
-
def test_roundtrip_ndarray_vector():
|
615
|
+
def test_roundtrip_ndarray_vector() -> None:
|
585
616
|
"""Test roundtrip encoding and decoding of NDArray vectors."""
|
586
|
-
value_f32
|
617
|
+
value_f32 = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
587
618
|
encoded_f32 = encode_engine_value(value_f32)
|
588
619
|
np.array_equal(encoded_f32, [1.0, 2.0, 3.0])
|
589
620
|
decoded_f32 = build_engine_value_decoder(Float32VectorType)(encoded_f32)
|
590
621
|
assert isinstance(decoded_f32, np.ndarray)
|
591
622
|
assert decoded_f32.dtype == np.float32
|
592
623
|
assert np.array_equal(decoded_f32, value_f32)
|
593
|
-
value_i64
|
624
|
+
value_i64 = np.array([1, 2, 3], dtype=np.int64)
|
594
625
|
encoded_i64 = encode_engine_value(value_i64)
|
595
626
|
assert np.array_equal(encoded_i64, [1, 2, 3])
|
596
627
|
decoded_i64 = build_engine_value_decoder(Int64VectorType)(encoded_i64)
|
@@ -606,63 +637,23 @@ def test_roundtrip_ndarray_vector():
|
|
606
637
|
assert np.array_equal(decoded_nd_f64, value_nd_f64)
|
607
638
|
|
608
639
|
|
609
|
-
def
|
610
|
-
"""Test encoding and decoding of unsigned integer vectors."""
|
611
|
-
value_uint8 = np.array([1, 2, 3, 4], dtype=np.uint8)
|
612
|
-
encoded = encode_engine_value(value_uint8)
|
613
|
-
assert np.array_equal(encoded, [1, 2, 3, 4])
|
614
|
-
decoder = make_engine_value_decoder(
|
615
|
-
[], {"kind": "Vector", "element_type": {"kind": "UInt8"}}, NDArray[np.uint8]
|
616
|
-
)
|
617
|
-
decoded = decoder(encoded)
|
618
|
-
assert np.array_equal(decoded, value_uint8)
|
619
|
-
assert decoded.dtype == np.uint8
|
620
|
-
value_uint16 = np.array([1, 2, 3, 4], dtype=np.uint16)
|
621
|
-
encoded = encode_engine_value(value_uint16)
|
622
|
-
assert np.array_equal(encoded, [1, 2, 3, 4])
|
623
|
-
decoder = make_engine_value_decoder(
|
624
|
-
[], {"kind": "Vector", "element_type": {"kind": "UInt16"}}, NDArray[np.uint16]
|
625
|
-
)
|
626
|
-
decoded = decoder(encoded)
|
627
|
-
assert np.array_equal(decoded, value_uint16)
|
628
|
-
assert decoded.dtype == np.uint16
|
629
|
-
value_uint32 = np.array([1, 2, 3], dtype=np.uint32)
|
630
|
-
encoded = encode_engine_value(value_uint32)
|
631
|
-
assert np.array_equal(encoded, [1, 2, 3])
|
632
|
-
decoder = make_engine_value_decoder(
|
633
|
-
[], {"kind": "Vector", "element_type": {"kind": "UInt32"}}, NDArray[np.uint32]
|
634
|
-
)
|
635
|
-
decoded = decoder(encoded)
|
636
|
-
assert np.array_equal(decoded, value_uint32)
|
637
|
-
assert decoded.dtype == np.uint32
|
638
|
-
value_uint64 = np.array([1, 2, 3], dtype=np.uint64)
|
639
|
-
encoded = encode_engine_value(value_uint64)
|
640
|
-
assert np.array_equal(encoded, [1, 2, 3])
|
641
|
-
decoder = make_engine_value_decoder(
|
642
|
-
[], {"kind": "Vector", "element_type": {"kind": "UInt8"}}, NDArray[np.uint64]
|
643
|
-
)
|
644
|
-
decoded = decoder(encoded)
|
645
|
-
assert np.array_equal(decoded, value_uint64)
|
646
|
-
assert decoded.dtype == np.uint64
|
647
|
-
|
648
|
-
|
649
|
-
def test_ndarray_dimension_mismatch():
|
640
|
+
def test_ndarray_dimension_mismatch() -> None:
|
650
641
|
"""Test dimension enforcement for Vector with specified dimension."""
|
651
|
-
value
|
642
|
+
value = np.array([1.0, 2.0], dtype=np.float32)
|
652
643
|
encoded = encode_engine_value(value)
|
653
644
|
assert np.array_equal(encoded, [1.0, 2.0])
|
654
645
|
with pytest.raises(ValueError, match="Vector dimension mismatch"):
|
655
646
|
build_engine_value_decoder(Float32VectorType)(encoded)
|
656
647
|
|
657
648
|
|
658
|
-
def test_list_vector_backward_compatibility():
|
649
|
+
def test_list_vector_backward_compatibility() -> None:
|
659
650
|
"""Test that list-based vectors still work for backward compatibility."""
|
660
|
-
value
|
651
|
+
value = [1, 2, 3, 4, 5]
|
661
652
|
encoded = encode_engine_value(value)
|
662
653
|
assert encoded == [1, 2, 3, 4, 5]
|
663
654
|
decoded = build_engine_value_decoder(IntVectorType)(encoded)
|
664
655
|
assert isinstance(decoded, np.ndarray)
|
665
|
-
assert decoded.dtype == np.
|
656
|
+
assert decoded.dtype == np.int64
|
666
657
|
assert np.array_equal(decoded, np.array([1, 2, 3, 4, 5], dtype=np.int64))
|
667
658
|
value_list: ListIntType = [1, 2, 3, 4, 5]
|
668
659
|
encoded = encode_engine_value(value_list)
|
@@ -671,7 +662,7 @@ def test_list_vector_backward_compatibility():
|
|
671
662
|
assert np.array_equal(decoded, [1, 2, 3, 4, 5])
|
672
663
|
|
673
664
|
|
674
|
-
def test_encode_complex_structure_with_ndarray():
|
665
|
+
def test_encode_complex_structure_with_ndarray() -> None:
|
675
666
|
"""Test encoding a complex structure that includes an NDArray."""
|
676
667
|
|
677
668
|
@dataclass
|
@@ -684,17 +675,13 @@ def test_encode_complex_structure_with_ndarray():
|
|
684
675
|
name="test_np", data=np.array([1.0, 0.5], dtype=np.float32), value=100
|
685
676
|
)
|
686
677
|
encoded = encode_engine_value(original)
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
]
|
692
|
-
assert encoded[0] == expected[0]
|
693
|
-
assert np.array_equal(encoded[1], expected[1])
|
694
|
-
assert encoded[2] == expected[2]
|
678
|
+
|
679
|
+
assert encoded[0] == original.name
|
680
|
+
assert np.array_equal(encoded[1], original.data)
|
681
|
+
assert encoded[2] == original.value
|
695
682
|
|
696
683
|
|
697
|
-
def test_decode_nullable_ndarray_none_or_value_input():
|
684
|
+
def test_decode_nullable_ndarray_none_or_value_input() -> None:
|
698
685
|
"""Test decoding a nullable NDArray with None or value inputs."""
|
699
686
|
src_type_dict = {
|
700
687
|
"kind": "Vector",
|
@@ -718,7 +705,7 @@ def test_decode_nullable_ndarray_none_or_value_input():
|
|
718
705
|
)
|
719
706
|
|
720
707
|
|
721
|
-
def test_decode_vector_string():
|
708
|
+
def test_decode_vector_string() -> None:
|
722
709
|
"""Test decoding a vector of strings works for Python native list type."""
|
723
710
|
src_type_dict = {
|
724
711
|
"kind": "Vector",
|
@@ -729,7 +716,7 @@ def test_decode_vector_string():
|
|
729
716
|
assert decoder(["hello", "world"]) == ["hello", "world"]
|
730
717
|
|
731
718
|
|
732
|
-
def test_decode_error_non_nullable_or_non_list_vector():
|
719
|
+
def test_decode_error_non_nullable_or_non_list_vector() -> None:
|
733
720
|
"""Test decoding errors for non-nullable vectors or non-list inputs."""
|
734
721
|
src_type_dict = {
|
735
722
|
"kind": "Vector",
|
@@ -743,7 +730,7 @@ def test_decode_error_non_nullable_or_non_list_vector():
|
|
743
730
|
decoder("not a list")
|
744
731
|
|
745
732
|
|
746
|
-
def test_dump_vector_type_annotation_with_dim():
|
733
|
+
def test_dump_vector_type_annotation_with_dim() -> None:
|
747
734
|
"""Test dumping a vector type annotation with a specified dimension."""
|
748
735
|
expected_dump = {
|
749
736
|
"type": {
|
@@ -755,7 +742,7 @@ def test_dump_vector_type_annotation_with_dim():
|
|
755
742
|
assert dump_engine_object(Float32VectorType) == expected_dump
|
756
743
|
|
757
744
|
|
758
|
-
def test_dump_vector_type_annotation_no_dim():
|
745
|
+
def test_dump_vector_type_annotation_no_dim() -> None:
|
759
746
|
"""Test dumping a vector type annotation with no dimension."""
|
760
747
|
expected_dump_no_dim = {
|
761
748
|
"type": {
|
@@ -765,3 +752,63 @@ def test_dump_vector_type_annotation_no_dim():
|
|
765
752
|
}
|
766
753
|
}
|
767
754
|
assert dump_engine_object(Float64VectorTypeNoDim) == expected_dump_no_dim
|
755
|
+
|
756
|
+
|
757
|
+
def test_full_roundtrip_vector_numeric_types() -> None:
|
758
|
+
"""Test full roundtrip for numeric vector types using NDArray."""
|
759
|
+
value_f32: Vector[np.float32, Literal[3]] = np.array(
|
760
|
+
[1.0, 2.0, 3.0], dtype=np.float32
|
761
|
+
)
|
762
|
+
validate_full_roundtrip(value_f32, Vector[np.float32, Literal[3]])
|
763
|
+
value_f64: Vector[np.float64, Literal[3]] = np.array(
|
764
|
+
[1.0, 2.0, 3.0], dtype=np.float64
|
765
|
+
)
|
766
|
+
validate_full_roundtrip(value_f64, Vector[np.float64, Literal[3]])
|
767
|
+
value_i64: Vector[np.int64, Literal[3]] = np.array([1, 2, 3], dtype=np.int64)
|
768
|
+
validate_full_roundtrip(value_i64, Vector[np.int64, Literal[3]])
|
769
|
+
value_i32: Vector[np.int32, Literal[3]] = np.array([1, 2, 3], dtype=np.int32)
|
770
|
+
with pytest.raises(ValueError, match="type unsupported yet"):
|
771
|
+
validate_full_roundtrip(value_i32, Vector[np.int32, Literal[3]])
|
772
|
+
value_u8: Vector[np.uint8, Literal[3]] = np.array([1, 2, 3], dtype=np.uint8)
|
773
|
+
with pytest.raises(ValueError, match="type unsupported yet"):
|
774
|
+
validate_full_roundtrip(value_u8, Vector[np.uint8, Literal[3]])
|
775
|
+
value_u16: Vector[np.uint16, Literal[3]] = np.array([1, 2, 3], dtype=np.uint16)
|
776
|
+
with pytest.raises(ValueError, match="type unsupported yet"):
|
777
|
+
validate_full_roundtrip(value_u16, Vector[np.uint16, Literal[3]])
|
778
|
+
value_u32: Vector[np.uint32, Literal[3]] = np.array([1, 2, 3], dtype=np.uint32)
|
779
|
+
with pytest.raises(ValueError, match="type unsupported yet"):
|
780
|
+
validate_full_roundtrip(value_u32, Vector[np.uint32, Literal[3]])
|
781
|
+
value_u64: Vector[np.uint64, Literal[3]] = np.array([1, 2, 3], dtype=np.uint64)
|
782
|
+
with pytest.raises(ValueError, match="type unsupported yet"):
|
783
|
+
validate_full_roundtrip(value_u64, Vector[np.uint64, Literal[3]])
|
784
|
+
|
785
|
+
|
786
|
+
def test_roundtrip_vector_no_dimension() -> None:
|
787
|
+
"""Test full roundtrip for vector types without dimension annotation."""
|
788
|
+
value_f64: Vector[np.float64] = np.array([1.0, 2.0, 3.0], dtype=np.float64)
|
789
|
+
validate_full_roundtrip(value_f64, Vector[np.float64])
|
790
|
+
|
791
|
+
|
792
|
+
def test_roundtrip_string_vector() -> None:
|
793
|
+
"""Test full roundtrip for string vector using list."""
|
794
|
+
value_str: Vector[str] = ["hello", "world"]
|
795
|
+
validate_full_roundtrip(value_str, Vector[str])
|
796
|
+
|
797
|
+
|
798
|
+
def test_roundtrip_empty_vector() -> None:
|
799
|
+
"""Test full roundtrip for empty numeric vector."""
|
800
|
+
value_empty: Vector[np.float32] = np.array([], dtype=np.float32)
|
801
|
+
validate_full_roundtrip(value_empty, Vector[np.float32])
|
802
|
+
|
803
|
+
|
804
|
+
def test_roundtrip_dimension_mismatch() -> None:
|
805
|
+
"""Test that dimension mismatch raises an error during roundtrip."""
|
806
|
+
value_f32: Vector[np.float32, Literal[3]] = np.array([1.0, 2.0], dtype=np.float32)
|
807
|
+
with pytest.raises(ValueError, match="Vector dimension mismatch"):
|
808
|
+
validate_full_roundtrip(value_f32, Vector[np.float32, Literal[3]])
|
809
|
+
|
810
|
+
|
811
|
+
def test_roundtrip_list_backward_compatibility() -> None:
|
812
|
+
"""Test full roundtrip for list-based vectors for backward compatibility."""
|
813
|
+
value_list: list[int] = [1, 2, 3]
|
814
|
+
validate_full_roundtrip(value_list, list[int])
|