cocoindex 0.1.73__cp311-cp311-manylinux_2_28_aarch64.whl → 0.1.75__cp311-cp311-manylinux_2_28_aarch64.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 +5 -3
- cocoindex/_engine.cpython-311-aarch64-linux-gnu.so +0 -0
- cocoindex/convert.py +188 -167
- cocoindex/flow.py +29 -12
- cocoindex/op.py +264 -6
- cocoindex/tests/test_convert.py +184 -151
- cocoindex/tests/test_transform_flow.py +103 -0
- cocoindex/tests/test_typing.py +5 -12
- cocoindex/typing.py +8 -8
- {cocoindex-0.1.73.dist-info → cocoindex-0.1.75.dist-info}/METADATA +5 -3
- {cocoindex-0.1.73.dist-info → cocoindex-0.1.75.dist-info}/RECORD +14 -13
- {cocoindex-0.1.73.dist-info → cocoindex-0.1.75.dist-info}/WHEEL +1 -1
- {cocoindex-0.1.73.dist-info → cocoindex-0.1.75.dist-info}/entry_points.txt +0 -0
- {cocoindex-0.1.73.dist-info → cocoindex-0.1.75.dist-info}/licenses/LICENSE +0 -0
cocoindex/__init__.py
CHANGED
@@ -11,7 +11,8 @@ from .flow import FlowBuilder, DataScope, DataSlice, Flow, transform_flow
|
|
11
11
|
from .flow import flow_def
|
12
12
|
from .flow import EvaluateAndDumpOptions, GeneratedField
|
13
13
|
from .flow import FlowLiveUpdater, FlowLiveUpdaterOptions, FlowUpdaterStatusUpdates
|
14
|
-
from .flow import
|
14
|
+
from .flow import open_flow
|
15
|
+
from .flow import add_flow_def, remove_flow # DEPRECATED
|
15
16
|
from .flow import update_all_flows_async, setup_all_flows, drop_all_flows
|
16
17
|
from .lib import init, start_server, stop
|
17
18
|
from .llm import LlmSpec, LlmApiType
|
@@ -57,8 +58,9 @@ __all__ = [
|
|
57
58
|
"FlowLiveUpdater",
|
58
59
|
"FlowLiveUpdaterOptions",
|
59
60
|
"FlowUpdaterStatusUpdates",
|
60
|
-
"
|
61
|
-
"
|
61
|
+
"open_flow",
|
62
|
+
"add_flow_def", # DEPRECATED
|
63
|
+
"remove_flow", # DEPRECATED
|
62
64
|
"update_all_flows_async",
|
63
65
|
"setup_all_flows",
|
64
66
|
"drop_all_flows",
|
Binary file
|
cocoindex/convert.py
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
Utilities to convert between Python and engine values.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
6
|
+
|
5
7
|
import dataclasses
|
6
8
|
import datetime
|
7
9
|
import inspect
|
10
|
+
import warnings
|
8
11
|
from enum import Enum
|
9
12
|
from typing import Any, Callable, Mapping, get_origin
|
10
13
|
|
@@ -29,6 +32,24 @@ from .typing import (
|
|
29
32
|
)
|
30
33
|
|
31
34
|
|
35
|
+
class ChildFieldPath:
|
36
|
+
"""Context manager to append a field to field_path on enter and pop it on exit."""
|
37
|
+
|
38
|
+
_field_path: list[str]
|
39
|
+
_field_name: str
|
40
|
+
|
41
|
+
def __init__(self, field_path: list[str], field_name: str):
|
42
|
+
self._field_path: list[str] = field_path
|
43
|
+
self._field_name = field_name
|
44
|
+
|
45
|
+
def __enter__(self) -> ChildFieldPath:
|
46
|
+
self._field_path.append(self._field_name)
|
47
|
+
return self
|
48
|
+
|
49
|
+
def __exit__(self, _exc_type: Any, _exc_val: Any, _exc_tb: Any) -> None:
|
50
|
+
self._field_path.pop()
|
51
|
+
|
52
|
+
|
32
53
|
def encode_engine_value(value: Any) -> Any:
|
33
54
|
"""Encode a Python value to an engine value."""
|
34
55
|
if dataclasses.is_dataclass(value):
|
@@ -73,7 +94,8 @@ def _is_type_kind_convertible_to(src_type_kind: str, dst_type_kind: str) -> bool
|
|
73
94
|
def make_engine_value_decoder(
|
74
95
|
field_path: list[str],
|
75
96
|
src_type: dict[str, Any],
|
76
|
-
|
97
|
+
dst_type_info: AnalyzedTypeInfo,
|
98
|
+
for_key: bool = False,
|
77
99
|
) -> Callable[[Any], Any]:
|
78
100
|
"""
|
79
101
|
Make a decoder from an engine value to a Python value.
|
@@ -89,7 +111,6 @@ def make_engine_value_decoder(
|
|
89
111
|
|
90
112
|
src_type_kind = src_type["kind"]
|
91
113
|
|
92
|
-
dst_type_info = analyze_type_info(dst_annotation)
|
93
114
|
dst_type_variant = dst_type_info.variant
|
94
115
|
|
95
116
|
if isinstance(dst_type_variant, AnalyzedUnknownType):
|
@@ -99,96 +120,100 @@ def make_engine_value_decoder(
|
|
99
120
|
)
|
100
121
|
|
101
122
|
if src_type_kind == "Struct":
|
102
|
-
return
|
123
|
+
return make_engine_struct_decoder(
|
103
124
|
field_path,
|
104
125
|
src_type["fields"],
|
105
126
|
dst_type_info,
|
127
|
+
for_key=for_key,
|
106
128
|
)
|
107
129
|
|
108
130
|
if src_type_kind in TABLE_TYPES:
|
109
|
-
field_path
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
131
|
+
with ChildFieldPath(field_path, "[*]"):
|
132
|
+
engine_fields_schema = src_type["row"]["fields"]
|
133
|
+
|
134
|
+
if src_type_kind == "LTable":
|
135
|
+
if isinstance(dst_type_variant, AnalyzedAnyType):
|
136
|
+
dst_elem_type = Any
|
137
|
+
elif isinstance(dst_type_variant, AnalyzedListType):
|
138
|
+
dst_elem_type = dst_type_variant.elem_type
|
139
|
+
else:
|
140
|
+
raise ValueError(
|
141
|
+
f"Type mismatch for `{''.join(field_path)}`: "
|
142
|
+
f"declared `{dst_type_info.core_type}`, a list type expected"
|
143
|
+
)
|
144
|
+
row_decoder = make_engine_struct_decoder(
|
145
|
+
field_path,
|
146
|
+
engine_fields_schema,
|
147
|
+
analyze_type_info(dst_elem_type),
|
121
148
|
)
|
122
|
-
row_decoder = _make_engine_struct_value_decoder(
|
123
|
-
field_path,
|
124
|
-
engine_fields_schema,
|
125
|
-
analyze_type_info(dst_type_variant.elem_type),
|
126
|
-
)
|
127
149
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
150
|
+
def decode(value: Any) -> Any | None:
|
151
|
+
if value is None:
|
152
|
+
return None
|
153
|
+
return [row_decoder(v) for v in value]
|
154
|
+
|
155
|
+
elif src_type_kind == "KTable":
|
156
|
+
if isinstance(dst_type_variant, AnalyzedAnyType):
|
157
|
+
key_type, value_type = Any, Any
|
158
|
+
elif isinstance(dst_type_variant, AnalyzedDictType):
|
159
|
+
key_type = dst_type_variant.key_type
|
160
|
+
value_type = dst_type_variant.value_type
|
161
|
+
else:
|
162
|
+
raise ValueError(
|
163
|
+
f"Type mismatch for `{''.join(field_path)}`: "
|
164
|
+
f"declared `{dst_type_info.core_type}`, a dict type expected"
|
165
|
+
)
|
132
166
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
field_path,
|
167
|
+
key_field_schema = engine_fields_schema[0]
|
168
|
+
field_path.append(f".{key_field_schema.get('name', KEY_FIELD_NAME)}")
|
169
|
+
key_decoder = make_engine_value_decoder(
|
170
|
+
field_path,
|
171
|
+
key_field_schema["type"],
|
172
|
+
analyze_type_info(key_type),
|
173
|
+
for_key=True,
|
137
174
|
)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
175
|
+
field_path.pop()
|
176
|
+
value_decoder = make_engine_struct_decoder(
|
177
|
+
field_path,
|
178
|
+
engine_fields_schema[1:],
|
179
|
+
analyze_type_info(value_type),
|
142
180
|
)
|
143
181
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
)
|
149
|
-
field_path.pop()
|
150
|
-
value_decoder = _make_engine_struct_value_decoder(
|
151
|
-
field_path,
|
152
|
-
engine_fields_schema[1:],
|
153
|
-
analyze_type_info(dst_type_variant.value_type),
|
154
|
-
)
|
155
|
-
|
156
|
-
def decode(value: Any) -> Any | None:
|
157
|
-
if value is None:
|
158
|
-
return None
|
159
|
-
return {key_decoder(v[0]): value_decoder(v[1:]) for v in value}
|
182
|
+
def decode(value: Any) -> Any | None:
|
183
|
+
if value is None:
|
184
|
+
return None
|
185
|
+
return {key_decoder(v[0]): value_decoder(v[1:]) for v in value}
|
160
186
|
|
161
|
-
field_path.pop()
|
162
187
|
return decode
|
163
188
|
|
164
189
|
if src_type_kind == "Union":
|
165
190
|
if isinstance(dst_type_variant, AnalyzedAnyType):
|
166
191
|
return lambda value: value[1]
|
167
192
|
|
168
|
-
|
169
|
-
dst_type_variant.variant_types
|
193
|
+
dst_type_info_variants = (
|
194
|
+
[analyze_type_info(t) for t in dst_type_variant.variant_types]
|
170
195
|
if isinstance(dst_type_variant, AnalyzedUnionType)
|
171
|
-
else [
|
196
|
+
else [dst_type_info]
|
172
197
|
)
|
173
198
|
src_type_variants = src_type["types"]
|
174
199
|
decoders = []
|
175
200
|
for i, src_type_variant in enumerate(src_type_variants):
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
201
|
+
with ChildFieldPath(field_path, f"[{i}]"):
|
202
|
+
decoder = None
|
203
|
+
for dst_type_info_variant in dst_type_info_variants:
|
204
|
+
try:
|
205
|
+
decoder = make_engine_value_decoder(
|
206
|
+
field_path, src_type_variant, dst_type_info_variant
|
207
|
+
)
|
208
|
+
break
|
209
|
+
except ValueError:
|
210
|
+
pass
|
211
|
+
if decoder is None:
|
212
|
+
raise ValueError(
|
213
|
+
f"Type mismatch for `{''.join(field_path)}`: "
|
214
|
+
f"cannot find matched target type for source type variant {src_type_variant}"
|
182
215
|
)
|
183
|
-
|
184
|
-
except ValueError:
|
185
|
-
pass
|
186
|
-
if decoder is None:
|
187
|
-
raise ValueError(
|
188
|
-
f"Type mismatch for `{''.join(field_path)}`: "
|
189
|
-
f"cannot find matched target type for source type variant {src_type_variant}"
|
190
|
-
)
|
191
|
-
decoders.append(decoder)
|
216
|
+
decoders.append(decoder)
|
192
217
|
return lambda value: decoders[value[0]](value[1])
|
193
218
|
|
194
219
|
if isinstance(dst_type_variant, AnalyzedAnyType):
|
@@ -216,7 +241,9 @@ def make_engine_value_decoder(
|
|
216
241
|
vec_elem_decoder = make_engine_value_decoder(
|
217
242
|
field_path + ["[*]"],
|
218
243
|
src_type["element_type"],
|
219
|
-
|
244
|
+
analyze_type_info(
|
245
|
+
dst_type_variant.elem_type if dst_type_variant else Any
|
246
|
+
),
|
220
247
|
)
|
221
248
|
|
222
249
|
def decode_vector(value: Any) -> Any | None:
|
@@ -247,7 +274,7 @@ def make_engine_value_decoder(
|
|
247
274
|
if not _is_type_kind_convertible_to(src_type_kind, dst_type_variant.kind):
|
248
275
|
raise ValueError(
|
249
276
|
f"Type mismatch for `{''.join(field_path)}`: "
|
250
|
-
f"passed in {src_type_kind}, declared {
|
277
|
+
f"passed in {src_type_kind}, declared {dst_type_info.core_type} ({dst_type_variant.kind})"
|
251
278
|
)
|
252
279
|
|
253
280
|
if dst_type_variant.kind in ("Float32", "Float64", "Int64"):
|
@@ -267,30 +294,54 @@ def make_engine_value_decoder(
|
|
267
294
|
return lambda value: value
|
268
295
|
|
269
296
|
|
270
|
-
def
|
297
|
+
def _get_auto_default_for_type(
|
298
|
+
type_info: AnalyzedTypeInfo,
|
299
|
+
) -> tuple[Any, bool]:
|
300
|
+
"""
|
301
|
+
Get an auto-default value for a type annotation if it's safe to do so.
|
302
|
+
|
303
|
+
Returns:
|
304
|
+
A tuple of (default_value, is_supported) where:
|
305
|
+
- default_value: The default value if auto-defaulting is supported
|
306
|
+
- is_supported: True if auto-defaulting is supported for this type
|
307
|
+
"""
|
308
|
+
# Case 1: Nullable types (Optional[T] or T | None)
|
309
|
+
if type_info.nullable:
|
310
|
+
return None, True
|
311
|
+
|
312
|
+
# Case 2: Table types (KTable or LTable) - check if it's a list or dict type
|
313
|
+
if isinstance(type_info.variant, AnalyzedListType):
|
314
|
+
return [], True
|
315
|
+
elif isinstance(type_info.variant, AnalyzedDictType):
|
316
|
+
return {}, True
|
317
|
+
|
318
|
+
return None, False
|
319
|
+
|
320
|
+
|
321
|
+
def make_engine_struct_decoder(
|
271
322
|
field_path: list[str],
|
272
323
|
src_fields: list[dict[str, Any]],
|
273
324
|
dst_type_info: AnalyzedTypeInfo,
|
325
|
+
for_key: bool = False,
|
274
326
|
) -> Callable[[list[Any]], Any]:
|
275
327
|
"""Make a decoder from an engine field values to a Python value."""
|
276
328
|
|
277
329
|
dst_type_variant = dst_type_info.variant
|
278
330
|
|
279
|
-
use_dict = False
|
280
331
|
if isinstance(dst_type_variant, AnalyzedAnyType):
|
281
|
-
|
332
|
+
if for_key:
|
333
|
+
return _make_engine_struct_to_tuple_decoder(field_path, src_fields)
|
334
|
+
else:
|
335
|
+
return _make_engine_struct_to_dict_decoder(field_path, src_fields, Any)
|
282
336
|
elif isinstance(dst_type_variant, AnalyzedDictType):
|
283
337
|
analyzed_key_type = analyze_type_info(dst_type_variant.key_type)
|
284
|
-
|
285
|
-
use_dict = (
|
338
|
+
if (
|
286
339
|
isinstance(analyzed_key_type.variant, AnalyzedAnyType)
|
287
|
-
or
|
288
|
-
|
289
|
-
|
340
|
+
or analyzed_key_type.core_type is str
|
341
|
+
):
|
342
|
+
return _make_engine_struct_to_dict_decoder(
|
343
|
+
field_path, src_fields, dst_type_variant.value_type
|
290
344
|
)
|
291
|
-
) and isinstance(analyzed_value_type.variant, AnalyzedAnyType)
|
292
|
-
if use_dict:
|
293
|
-
return _make_engine_struct_to_dict_decoder(field_path, src_fields)
|
294
345
|
|
295
346
|
if not isinstance(dst_type_variant, AnalyzedStructType):
|
296
347
|
raise ValueError(
|
@@ -321,32 +372,39 @@ def _make_engine_struct_value_decoder(
|
|
321
372
|
else:
|
322
373
|
raise ValueError(f"Unsupported struct type: {dst_struct_type}")
|
323
374
|
|
324
|
-
def
|
375
|
+
def make_closure_for_field(
|
325
376
|
name: str, param: inspect.Parameter
|
326
377
|
) -> Callable[[list[Any]], Any]:
|
327
378
|
src_idx = src_name_to_idx.get(name)
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
lambda values: field_decoder(values[src_idx])
|
336
|
-
|
337
|
-
|
338
|
-
|
379
|
+
type_info = analyze_type_info(param.annotation)
|
380
|
+
|
381
|
+
with ChildFieldPath(field_path, f".{name}"):
|
382
|
+
if src_idx is not None:
|
383
|
+
field_decoder = make_engine_value_decoder(
|
384
|
+
field_path, src_fields[src_idx]["type"], type_info, for_key=for_key
|
385
|
+
)
|
386
|
+
return lambda values: field_decoder(values[src_idx])
|
387
|
+
|
388
|
+
default_value = param.default
|
389
|
+
if default_value is not inspect.Parameter.empty:
|
390
|
+
return lambda _: default_value
|
391
|
+
|
392
|
+
auto_default, is_supported = _get_auto_default_for_type(type_info)
|
393
|
+
if is_supported:
|
394
|
+
warnings.warn(
|
395
|
+
f"Field '{name}' (type {param.annotation}) without default value is missing in input: "
|
396
|
+
f"{''.join(field_path)}. Auto-assigning default value: {auto_default}",
|
397
|
+
UserWarning,
|
398
|
+
stacklevel=4,
|
399
|
+
)
|
400
|
+
return lambda _: auto_default
|
339
401
|
|
340
|
-
default_value = param.default
|
341
|
-
if default_value is inspect.Parameter.empty:
|
342
402
|
raise ValueError(
|
343
|
-
f"Field without default value is missing in input: {''.join(field_path)}"
|
403
|
+
f"Field '{name}' (type {param.annotation}) without default value is missing in input: {''.join(field_path)}"
|
344
404
|
)
|
345
405
|
|
346
|
-
return lambda _: default_value
|
347
|
-
|
348
406
|
field_value_decoder = [
|
349
|
-
|
407
|
+
make_closure_for_field(name, param) for (name, param) in parameters.items()
|
350
408
|
]
|
351
409
|
|
352
410
|
return lambda values: dst_struct_type(
|
@@ -357,19 +415,20 @@ def _make_engine_struct_value_decoder(
|
|
357
415
|
def _make_engine_struct_to_dict_decoder(
|
358
416
|
field_path: list[str],
|
359
417
|
src_fields: list[dict[str, Any]],
|
418
|
+
value_type_annotation: Any,
|
360
419
|
) -> Callable[[list[Any] | None], dict[str, Any] | None]:
|
361
420
|
"""Make a decoder from engine field values to a Python dict."""
|
362
421
|
|
363
422
|
field_decoders = []
|
364
|
-
|
423
|
+
value_type_info = analyze_type_info(value_type_annotation)
|
424
|
+
for field_schema in src_fields:
|
365
425
|
field_name = field_schema["name"]
|
366
|
-
field_path
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
field_path.pop()
|
426
|
+
with ChildFieldPath(field_path, f".{field_name}"):
|
427
|
+
field_decoder = make_engine_value_decoder(
|
428
|
+
field_path,
|
429
|
+
field_schema["type"],
|
430
|
+
value_type_info,
|
431
|
+
)
|
373
432
|
field_decoders.append((field_name, field_decoder))
|
374
433
|
|
375
434
|
def decode_to_dict(values: list[Any] | None) -> dict[str, Any] | None:
|
@@ -387,75 +446,37 @@ def _make_engine_struct_to_dict_decoder(
|
|
387
446
|
return decode_to_dict
|
388
447
|
|
389
448
|
|
390
|
-
def
|
449
|
+
def _make_engine_struct_to_tuple_decoder(
|
391
450
|
field_path: list[str],
|
392
451
|
src_fields: list[dict[str, Any]],
|
393
|
-
) -> Callable[[list[Any] | None],
|
394
|
-
"""Make a decoder from engine
|
395
|
-
|
396
|
-
# Create a decoder for each row (struct) to dict
|
397
|
-
row_decoder = _make_engine_struct_to_dict_decoder(field_path, src_fields)
|
452
|
+
) -> Callable[[list[Any] | None], tuple[Any, ...] | None]:
|
453
|
+
"""Make a decoder from engine field values to a Python tuple."""
|
398
454
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
455
|
+
field_decoders = []
|
456
|
+
value_type_info = analyze_type_info(Any)
|
457
|
+
for field_schema in src_fields:
|
458
|
+
field_name = field_schema["name"]
|
459
|
+
with ChildFieldPath(field_path, f".{field_name}"):
|
460
|
+
field_decoders.append(
|
461
|
+
make_engine_value_decoder(
|
462
|
+
field_path,
|
463
|
+
field_schema["type"],
|
464
|
+
value_type_info,
|
408
465
|
)
|
409
|
-
|
410
|
-
return result
|
411
|
-
|
412
|
-
return decode_to_list_dict
|
413
|
-
|
414
|
-
|
415
|
-
def _make_engine_ktable_to_dict_dict_decoder(
|
416
|
-
field_path: list[str],
|
417
|
-
src_fields: list[dict[str, Any]],
|
418
|
-
) -> Callable[[list[Any] | None], dict[Any, dict[str, Any]] | None]:
|
419
|
-
"""Make a decoder from engine KTable values to a dict of dicts."""
|
420
|
-
|
421
|
-
if not src_fields:
|
422
|
-
raise ValueError("KTable must have at least one field for the key")
|
423
|
-
|
424
|
-
# First field is the key, remaining fields are the value
|
425
|
-
key_field_schema = src_fields[0]
|
426
|
-
value_fields_schema = src_fields[1:]
|
427
|
-
|
428
|
-
# Create decoders
|
429
|
-
field_path.append(f".{key_field_schema.get('name', KEY_FIELD_NAME)}")
|
430
|
-
key_decoder = make_engine_value_decoder(field_path, key_field_schema["type"], Any)
|
431
|
-
field_path.pop()
|
432
|
-
|
433
|
-
value_decoder = _make_engine_struct_to_dict_decoder(field_path, value_fields_schema)
|
466
|
+
)
|
434
467
|
|
435
|
-
def
|
436
|
-
values: list[Any] | None,
|
437
|
-
) -> dict[Any, dict[str, Any]] | None:
|
468
|
+
def decode_to_tuple(values: list[Any] | None) -> tuple[Any, ...] | None:
|
438
469
|
if values is None:
|
439
470
|
return None
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
else:
|
448
|
-
tmp = value_decoder(row_values[1:])
|
449
|
-
if tmp is None:
|
450
|
-
value = {}
|
451
|
-
else:
|
452
|
-
value = tmp
|
453
|
-
if isinstance(key, dict):
|
454
|
-
key = tuple(key.values())
|
455
|
-
result[key] = value
|
456
|
-
return result
|
471
|
+
if len(field_decoders) != len(values):
|
472
|
+
raise ValueError(
|
473
|
+
f"Field count mismatch: expected {len(field_decoders)}, got {len(values)}"
|
474
|
+
)
|
475
|
+
return tuple(
|
476
|
+
field_decoder(value) for value, field_decoder in zip(values, field_decoders)
|
477
|
+
)
|
457
478
|
|
458
|
-
return
|
479
|
+
return decode_to_tuple
|
459
480
|
|
460
481
|
|
461
482
|
def dump_engine_object(v: Any) -> Any:
|
cocoindex/flow.py
CHANGED
@@ -16,6 +16,7 @@ from .validation import (
|
|
16
16
|
validate_full_flow_name,
|
17
17
|
validate_target_name,
|
18
18
|
)
|
19
|
+
from .typing import analyze_type_info
|
19
20
|
|
20
21
|
from dataclasses import dataclass
|
21
22
|
from enum import Enum
|
@@ -797,7 +798,7 @@ class Flow:
|
|
797
798
|
The current instance is still valid after it's called.
|
798
799
|
For example, you can still call `setup()` after it, to setup the persistent backends again.
|
799
800
|
|
800
|
-
Call `
|
801
|
+
Call `close()` if you want to remove the flow from the current process.
|
801
802
|
"""
|
802
803
|
execution_context.run(self.drop_async(report_to_stdout=report_to_stdout))
|
803
804
|
|
@@ -809,6 +810,18 @@ class Flow:
|
|
809
810
|
report_to_stdout=report_to_stdout
|
810
811
|
)
|
811
812
|
|
813
|
+
def close(self) -> None:
|
814
|
+
"""
|
815
|
+
Close the flow. It will remove the flow from the current process to free up resources.
|
816
|
+
After it's called, methods of the flow should no longer be called.
|
817
|
+
|
818
|
+
This will NOT touch the persistent backends of the flow.
|
819
|
+
"""
|
820
|
+
_engine.remove_flow_context(self.full_name)
|
821
|
+
self._lazy_engine_flow = None
|
822
|
+
with _flows_lock:
|
823
|
+
del _flows[self.name]
|
824
|
+
|
812
825
|
|
813
826
|
def _create_lazy_flow(
|
814
827
|
name: str | None, fl_def: Callable[[FlowBuilder, DataScope], None]
|
@@ -844,7 +857,10 @@ def get_flow_full_name(name: str) -> str:
|
|
844
857
|
return f"{setting.get_app_namespace(trailing_delimiter='.')}{name}"
|
845
858
|
|
846
859
|
|
847
|
-
def
|
860
|
+
def open_flow(name: str, fl_def: Callable[[FlowBuilder, DataScope], None]) -> Flow:
|
861
|
+
"""
|
862
|
+
Open a flow, with the given name and definition.
|
863
|
+
"""
|
848
864
|
with _flows_lock:
|
849
865
|
if name in _flows:
|
850
866
|
raise KeyError(f"Flow with name {name} already exists")
|
@@ -852,17 +868,18 @@ def add_flow_def(name: str, fl_def: Callable[[FlowBuilder, DataScope], None]) ->
|
|
852
868
|
return fl
|
853
869
|
|
854
870
|
|
855
|
-
def
|
871
|
+
def add_flow_def(name: str, fl_def: Callable[[FlowBuilder, DataScope], None]) -> Flow:
|
856
872
|
"""
|
857
|
-
|
858
|
-
|
873
|
+
DEPRECATED: Use `open_flow()` instead.
|
874
|
+
"""
|
875
|
+
return open_flow(name, fl_def)
|
876
|
+
|
859
877
|
|
860
|
-
|
878
|
+
def remove_flow(fl: Flow) -> None:
|
861
879
|
"""
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
del _flows[fl.name]
|
880
|
+
DEPRECATED: Use `Flow.close()` instead.
|
881
|
+
"""
|
882
|
+
fl.close()
|
866
883
|
|
867
884
|
|
868
885
|
def flow_def(
|
@@ -871,7 +888,7 @@ def flow_def(
|
|
871
888
|
"""
|
872
889
|
A decorator to wrap the flow definition.
|
873
890
|
"""
|
874
|
-
return lambda fl_def:
|
891
|
+
return lambda fl_def: open_flow(name or fl_def.__name__, fl_def)
|
875
892
|
|
876
893
|
|
877
894
|
def flow_names() -> list[str]:
|
@@ -1053,7 +1070,7 @@ class TransformFlow(Generic[T]):
|
|
1053
1070
|
sig.return_annotation
|
1054
1071
|
)
|
1055
1072
|
result_decoder = make_engine_value_decoder(
|
1056
|
-
[], engine_return_type["type"], python_return_type
|
1073
|
+
[], engine_return_type["type"], analyze_type_info(python_return_type)
|
1057
1074
|
)
|
1058
1075
|
|
1059
1076
|
return TransformFlowInfo(engine_flow, result_decoder)
|