tracdap-runtime 0.8.0b3__py3-none-any.whl → 0.8.0rc1__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.
- tracdap/rt/_impl/core/config_parser.py +55 -37
- tracdap/rt/_impl/core/data.py +63 -32
- tracdap/rt/_impl/core/storage.py +4 -1
- tracdap/rt/_impl/core/struct.py +547 -0
- tracdap/rt/_impl/core/type_system.py +73 -33
- tracdap/rt/_impl/core/validation.py +56 -15
- tracdap/rt/_impl/exec/context.py +64 -10
- tracdap/rt/_impl/exec/dev_mode.py +25 -14
- tracdap/rt/_impl/exec/functions.py +79 -29
- tracdap/rt/_impl/grpc/codec.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +2 -2
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +31 -19
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +48 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +2 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +3 -3
- tracdap/rt/_impl/grpc/tracdap/metadata/{stoarge_pb2.py → storage_pb2.py} +3 -3
- tracdap/rt/_impl/static_api.py +9 -1
- tracdap/rt/_plugins/storage_sql.py +12 -5
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/__init__.py +1 -23
- tracdap/rt/api/constants.py +57 -0
- tracdap/rt/api/experimental.py +32 -0
- tracdap/rt/api/hook.py +11 -0
- tracdap/rt/api/static_api.py +54 -2
- tracdap/rt/config/__init__.py +1 -4
- tracdap/rt/config/common.py +0 -34
- tracdap/rt/config/platform.py +6 -26
- tracdap/rt/metadata/__init__.py +31 -29
- tracdap/rt/metadata/data.py +40 -0
- tracdap/rt/metadata/file.py +2 -0
- tracdap/rt/metadata/object.py +1 -1
- {tracdap_runtime-0.8.0b3.dist-info → tracdap_runtime-0.8.0rc1.dist-info}/METADATA +17 -14
- {tracdap_runtime-0.8.0b3.dist-info → tracdap_runtime-0.8.0rc1.dist-info}/RECORD +39 -38
- {tracdap_runtime-0.8.0b3.dist-info → tracdap_runtime-0.8.0rc1.dist-info}/WHEEL +1 -1
- tracdap/rt/api/file_types.py +0 -29
- /tracdap/rt/_impl/grpc/tracdap/metadata/{stoarge_pb2.pyi → storage_pb2.pyi} +0 -0
- /tracdap/rt/metadata/{stoarge.py → storage.py} +0 -0
- {tracdap_runtime-0.8.0b3.dist-info → tracdap_runtime-0.8.0rc1.dist-info}/LICENSE +0 -0
- {tracdap_runtime-0.8.0b3.dist-info → tracdap_runtime-0.8.0rc1.dist-info}/top_level.txt +0 -0
@@ -133,9 +133,13 @@ class MetadataCodec:
|
|
133
133
|
|
134
134
|
if basic_type == _meta.BasicType.ARRAY:
|
135
135
|
items = value.arrayValue.items
|
136
|
-
return list(
|
136
|
+
return list(MetadataCodec._decode_value_for_type(x, type_desc.arrayType) for x in items)
|
137
137
|
|
138
|
-
|
138
|
+
if basic_type == _meta.BasicType.MAP:
|
139
|
+
items = value.mapValue.entries.items()
|
140
|
+
return dict((k, MetadataCodec._decode_value_for_type(v, type_desc.mapType)) for k, v in items)
|
141
|
+
|
142
|
+
raise _ex.ETracInternal(f"Cannot decode value of type [{basic_type}]")
|
139
143
|
|
140
144
|
@classmethod
|
141
145
|
def encode_value(cls, value: tp.Any) -> _meta.Value:
|
@@ -183,19 +187,36 @@ class MetadataCodec:
|
|
183
187
|
if any(map(lambda x: type(x) != array_raw_type, value)):
|
184
188
|
raise _ex.ETracInternal("Cannot encode a list with values of different types")
|
185
189
|
|
186
|
-
encoded_items = list(map(lambda x: cls.convert_value(x, array_trac_type), value))
|
190
|
+
encoded_items = list(map(lambda x: cls.convert_value(x, array_trac_type, True), value))
|
187
191
|
|
188
192
|
return _meta.Value(
|
189
193
|
_meta.TypeDescriptor(_meta.BasicType.ARRAY, arrayType=array_trac_type),
|
190
194
|
arrayValue=_meta.ArrayValue(encoded_items))
|
191
195
|
|
192
|
-
|
196
|
+
if isinstance(value, dict):
|
197
|
+
|
198
|
+
if len(value) == 0:
|
199
|
+
raise _ex.ETracInternal("Cannot encode an empty dict")
|
200
|
+
|
201
|
+
map_raw_type = type(next(iter(value.values())))
|
202
|
+
map_trac_type = TypeMapping.python_to_trac(map_raw_type)
|
203
|
+
|
204
|
+
if any(map(lambda x: type(x) != array_raw_type, value.values())):
|
205
|
+
raise _ex.ETracInternal("Cannot encode a dict with values of different types")
|
206
|
+
|
207
|
+
encoded_entries = dict(map(lambda kv: (kv[0], cls.convert_value(kv[1], map_trac_type, True)), value.items()))
|
208
|
+
|
209
|
+
return _meta.Value(
|
210
|
+
_meta.TypeDescriptor(_meta.BasicType.ARRAY, mapType=map_trac_type),
|
211
|
+
mapValue=_meta.MapValue(encoded_entries))
|
212
|
+
|
213
|
+
raise _ex.ETracInternal(f"Cannot encode value of type [{type(value).__name__}]")
|
193
214
|
|
194
215
|
@classmethod
|
195
|
-
def convert_value(cls, raw_value: tp.Any, type_desc: _meta.TypeDescriptor):
|
216
|
+
def convert_value(cls, raw_value: tp.Any, type_desc: _meta.TypeDescriptor, nested: bool = False):
|
196
217
|
|
197
218
|
if type_desc.basicType == _meta.BasicType.BOOLEAN:
|
198
|
-
return cls.convert_boolean_value(raw_value)
|
219
|
+
return cls.convert_boolean_value(raw_value, nested)
|
199
220
|
|
200
221
|
if type_desc.basicType == _meta.BasicType.INTEGER:
|
201
222
|
return cls.convert_integer_value(raw_value)
|
@@ -218,78 +239,97 @@ class MetadataCodec:
|
|
218
239
|
if type_desc.basicType == _meta.BasicType.ARRAY:
|
219
240
|
return cls.convert_array_value(raw_value, type_desc.arrayType)
|
220
241
|
|
242
|
+
if type_desc.basicType == _meta.BasicType.MAP:
|
243
|
+
return cls.convert_map_value(raw_value, type_desc.mapType)
|
244
|
+
|
221
245
|
raise _ex.ETracInternal(f"Conversion to value type [{type_desc.basicType.name}] is not supported yet")
|
222
246
|
|
223
247
|
@staticmethod
|
224
248
|
def convert_array_value(raw_value: tp.List[tp.Any], array_type: _meta.TypeDescriptor) -> _meta.Value:
|
225
249
|
|
226
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.ARRAY, array_type)
|
250
|
+
type_desc = _meta.TypeDescriptor(basicType=_meta.BasicType.ARRAY, arrayType=array_type)
|
227
251
|
|
228
252
|
if not isinstance(raw_value, list):
|
229
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.ARRAY.name}"
|
253
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.ARRAY.name}"
|
230
254
|
raise _ex.ETracInternal(msg)
|
231
255
|
|
232
|
-
items = list(map(lambda x: MetadataCodec.convert_value(x, array_type), raw_value))
|
256
|
+
items = list(map(lambda x: MetadataCodec.convert_value(x, array_type, True), raw_value))
|
233
257
|
|
234
258
|
return _meta.Value(type_desc, arrayValue=_meta.ArrayValue(items))
|
235
259
|
|
236
260
|
@staticmethod
|
237
|
-
def
|
261
|
+
def convert_map_value(raw_value: tp.Dict[str, tp.Any], map_type: _meta.TypeDescriptor) -> _meta.Value:
|
262
|
+
|
263
|
+
type_desc = _meta.TypeDescriptor(basicType=_meta.BasicType.MAP, mapType=map_type)
|
264
|
+
|
265
|
+
if not isinstance(raw_value, dict):
|
266
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.MAP.name}"
|
267
|
+
raise _ex.ETracInternal(msg)
|
268
|
+
|
269
|
+
entries = dict(map(lambda kv: (kv[0], MetadataCodec.convert_value(kv[1], map_type, True)), raw_value.items()))
|
270
|
+
|
271
|
+
return _meta.Value(type_desc, mapValue=_meta.MapValue(entries))
|
272
|
+
|
273
|
+
@staticmethod
|
274
|
+
def convert_boolean_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
238
275
|
|
239
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.BOOLEAN)
|
276
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.BOOLEAN) if not nested else None
|
240
277
|
|
241
278
|
if isinstance(raw_value, bool):
|
242
279
|
return _meta.Value(type_desc, booleanValue=raw_value)
|
243
280
|
|
244
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.BOOLEAN.name}"
|
281
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.BOOLEAN.name}"
|
245
282
|
raise _ex.ETracInternal(msg)
|
246
283
|
|
247
284
|
@staticmethod
|
248
|
-
def convert_integer_value(raw_value: tp.Any) -> _meta.Value:
|
285
|
+
def convert_integer_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
249
286
|
|
250
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.INTEGER)
|
287
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.INTEGER) if not nested else None
|
251
288
|
|
252
|
-
|
289
|
+
# isinstance(bool_value, int) returns True! An explicit check is needed
|
290
|
+
if isinstance(raw_value, int) and not isinstance(raw_value, bool):
|
253
291
|
return _meta.Value(type_desc, integerValue=raw_value)
|
254
292
|
|
255
293
|
if isinstance(raw_value, float) and raw_value.is_integer():
|
256
294
|
return _meta.Value(type_desc, integerValue=int(raw_value))
|
257
295
|
|
258
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.INTEGER.name}"
|
296
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.INTEGER.name}"
|
259
297
|
raise _ex.ETracInternal(msg)
|
260
298
|
|
261
299
|
@staticmethod
|
262
|
-
def convert_float_value(raw_value: tp.Any) -> _meta.Value:
|
300
|
+
def convert_float_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
263
301
|
|
264
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.FLOAT)
|
302
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.FLOAT) if not nested else None
|
265
303
|
|
266
304
|
if isinstance(raw_value, float):
|
267
305
|
return _meta.Value(type_desc, floatValue=raw_value)
|
268
306
|
|
269
|
-
|
307
|
+
# isinstance(bool_value, int) returns True! An explicit check is needed
|
308
|
+
if isinstance(raw_value, int) and not isinstance(raw_value, bool):
|
270
309
|
return _meta.Value(type_desc, floatValue=float(raw_value))
|
271
310
|
|
272
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.FLOAT.name}"
|
311
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.FLOAT.name}"
|
273
312
|
raise _ex.ETracInternal(msg)
|
274
313
|
|
275
314
|
@staticmethod
|
276
|
-
def convert_decimal_value(raw_value: tp.Any) -> _meta.Value:
|
315
|
+
def convert_decimal_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
277
316
|
|
278
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.DECIMAL)
|
317
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.DECIMAL) if not nested else None
|
279
318
|
|
280
319
|
if isinstance(raw_value, decimal.Decimal):
|
281
320
|
return _meta.Value(type_desc, decimalValue=_meta.DecimalValue(str(raw_value)))
|
282
321
|
|
283
|
-
|
322
|
+
# isinstance(bool_value, int) returns True! An explicit check is needed
|
323
|
+
if isinstance(raw_value, int) or isinstance(raw_value, float) and not isinstance(raw_value, bool):
|
284
324
|
return _meta.Value(type_desc, decimalValue=_meta.DecimalValue(str(raw_value)))
|
285
325
|
|
286
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.DECIMAL.name}"
|
326
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.DECIMAL.name}"
|
287
327
|
raise _ex.ETracInternal(msg)
|
288
328
|
|
289
329
|
@staticmethod
|
290
|
-
def convert_string_value(raw_value: tp.Any) -> _meta.Value:
|
330
|
+
def convert_string_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
291
331
|
|
292
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.STRING)
|
332
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.STRING) if not nested else None
|
293
333
|
|
294
334
|
if isinstance(raw_value, str):
|
295
335
|
return _meta.Value(type_desc, stringValue=raw_value)
|
@@ -301,13 +341,13 @@ class MetadataCodec:
|
|
301
341
|
|
302
342
|
return _meta.Value(type_desc, stringValue=str(raw_value))
|
303
343
|
|
304
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.STRING.name}"
|
344
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.STRING.name}"
|
305
345
|
raise _ex.ETracInternal(msg)
|
306
346
|
|
307
347
|
@staticmethod
|
308
|
-
def convert_date_value(raw_value: tp.Any) -> _meta.Value:
|
348
|
+
def convert_date_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
309
349
|
|
310
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.DATE)
|
350
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.DATE) if not nested else None
|
311
351
|
|
312
352
|
if isinstance(raw_value, dt.date):
|
313
353
|
return _meta.Value(type_desc, dateValue=_meta.DateValue(isoDate=raw_value.isoformat()))
|
@@ -316,13 +356,13 @@ class MetadataCodec:
|
|
316
356
|
date_value = dt.date.fromisoformat(raw_value)
|
317
357
|
return _meta.Value(type_desc, dateValue=_meta.DateValue(isoDate=date_value.isoformat()))
|
318
358
|
|
319
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.DATE.name}"
|
359
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.DATE.name}"
|
320
360
|
raise _ex.ETracInternal(msg)
|
321
361
|
|
322
362
|
@staticmethod
|
323
|
-
def convert_datetime_value(raw_value: tp.Any) -> _meta.Value:
|
363
|
+
def convert_datetime_value(raw_value: tp.Any, nested: bool = False) -> _meta.Value:
|
324
364
|
|
325
|
-
type_desc = _meta.TypeDescriptor(_meta.BasicType.DATETIME)
|
365
|
+
type_desc = _meta.TypeDescriptor(_meta.BasicType.DATETIME) if not nested else None
|
326
366
|
|
327
367
|
if isinstance(raw_value, dt.datetime):
|
328
368
|
return _meta.Value(type_desc, datetimeValue=_meta.DatetimeValue(isoDatetime=raw_value.isoformat()))
|
@@ -331,5 +371,5 @@ class MetadataCodec:
|
|
331
371
|
datetime_value = dt.datetime.fromisoformat(raw_value)
|
332
372
|
return _meta.Value(type_desc, datetimeValue=_meta.DatetimeValue(isoDatetime=datetime_value.isoformat()))
|
333
373
|
|
334
|
-
msg = f"Value of type [{type(raw_value)}] cannot be converted to {_meta.BasicType.DATETIME.name}"
|
374
|
+
msg = f"Value of type [{type(raw_value).__name__}] cannot be converted to {_meta.BasicType.DATETIME.name}"
|
335
375
|
raise _ex.ETracInternal(msg)
|
@@ -67,9 +67,17 @@ class SkipValidation(tp.Generic[T_SKIP_VAL]):
|
|
67
67
|
|
68
68
|
class _TypeValidator:
|
69
69
|
|
70
|
-
#
|
71
|
-
#
|
72
|
-
__generic_metaclass =
|
70
|
+
# Support both new and old styles for generic, union and optional types
|
71
|
+
# Old-style annotations are still valid, even when the new style is fully supported
|
72
|
+
__generic_metaclass = [
|
73
|
+
types.GenericAlias,
|
74
|
+
type(tp.List[object]),
|
75
|
+
type(tp.Optional[object])
|
76
|
+
]
|
77
|
+
|
78
|
+
# UnionType was added to the types module in Python 3.10, we support 3.9 (Jan 2025)
|
79
|
+
if hasattr(types, "UnionType"):
|
80
|
+
__generic_metaclass.append(types.UnionType)
|
73
81
|
|
74
82
|
# Cache method signatures to avoid inspection on every call
|
75
83
|
# Inspecting a function signature can take ~ half a second in Python 3.7
|
@@ -204,7 +212,7 @@ class _TypeValidator:
|
|
204
212
|
if value.skip_type == expected_type:
|
205
213
|
return True
|
206
214
|
|
207
|
-
if isinstance(expected_type, cls.__generic_metaclass):
|
215
|
+
if any(map(lambda _t: isinstance(expected_type, _t), cls.__generic_metaclass)):
|
208
216
|
|
209
217
|
origin = util.get_origin(expected_type)
|
210
218
|
args = util.get_args(expected_type)
|
@@ -240,6 +248,33 @@ class _TypeValidator:
|
|
240
248
|
all(map(lambda k: cls._validate_type(key_type, k), value.keys())) and \
|
241
249
|
all(map(lambda v: cls._validate_type(value_type, v), value.values()))
|
242
250
|
|
251
|
+
if origin is type:
|
252
|
+
|
253
|
+
if not isinstance(value, type):
|
254
|
+
return False
|
255
|
+
|
256
|
+
type_arg = args[0]
|
257
|
+
|
258
|
+
if type_arg == tp.Any:
|
259
|
+
return True
|
260
|
+
|
261
|
+
if isinstance(type_arg, tp.TypeVar):
|
262
|
+
|
263
|
+
constraints = util.get_constraints(type_arg)
|
264
|
+
bound = util.get_bound(type_arg)
|
265
|
+
|
266
|
+
if constraints:
|
267
|
+
if not any(map(lambda c: expected_type == c, constraints)):
|
268
|
+
return False
|
269
|
+
|
270
|
+
if bound:
|
271
|
+
if not issubclass(expected_type, bound):
|
272
|
+
return False
|
273
|
+
|
274
|
+
# So long as constraints / bound are ok, any type matches a generic type var
|
275
|
+
return True
|
276
|
+
|
277
|
+
|
243
278
|
if origin.__module__.startswith("tracdap.rt.api."):
|
244
279
|
return isinstance(value, origin)
|
245
280
|
|
@@ -274,7 +309,7 @@ class _TypeValidator:
|
|
274
309
|
@classmethod
|
275
310
|
def _type_name(cls, type_var: tp.Type, qualified: bool = False) -> str:
|
276
311
|
|
277
|
-
if isinstance(type_var, cls.__generic_metaclass):
|
312
|
+
if any(map(lambda _t: isinstance(type_var, _t), cls.__generic_metaclass)):
|
278
313
|
|
279
314
|
origin = util.get_origin(type_var)
|
280
315
|
args = util.get_args(type_var)
|
@@ -291,7 +326,11 @@ class _TypeValidator:
|
|
291
326
|
|
292
327
|
if origin is list:
|
293
328
|
list_type = cls._type_name(args[0])
|
294
|
-
return f"
|
329
|
+
return f"list[{list_type}]"
|
330
|
+
|
331
|
+
if origin is type:
|
332
|
+
type_arg = cls._type_name(args[0])
|
333
|
+
return f"type[{type_arg}]"
|
295
334
|
|
296
335
|
raise ex.ETracInternal(f"Validation of [{origin.__name__}] generic parameters is not supported yet")
|
297
336
|
|
@@ -457,18 +496,20 @@ class StaticValidator:
|
|
457
496
|
else:
|
458
497
|
return
|
459
498
|
|
460
|
-
|
461
|
-
field_names = list(map(lambda f: f.fieldName, fields))
|
462
|
-
property_type = f"field in [{socket_name}]"
|
499
|
+
if socket.schema.schemaType == meta.SchemaType.TABLE:
|
463
500
|
|
464
|
-
|
465
|
-
|
501
|
+
fields = socket.schema.table.fields
|
502
|
+
field_names = list(map(lambda f: f.fieldName, fields))
|
503
|
+
property_type = f"field in [{socket_name}]"
|
466
504
|
|
467
|
-
|
468
|
-
|
505
|
+
if len(fields) == 0:
|
506
|
+
cls._fail(f"Invalid schema for [{socket_name}]: No fields defined")
|
469
507
|
|
470
|
-
|
471
|
-
cls.
|
508
|
+
cls._valid_identifiers(field_names, property_type)
|
509
|
+
cls._case_insensitive_duplicates(field_names, property_type)
|
510
|
+
|
511
|
+
for field in fields:
|
512
|
+
cls._check_single_field(field, property_type)
|
472
513
|
|
473
514
|
@classmethod
|
474
515
|
def _check_socket_file_type(cls, socket_name, socket):
|
tracdap/rt/_impl/exec/context.py
CHANGED
@@ -29,6 +29,7 @@ import tracdap.rt.exceptions as _ex
|
|
29
29
|
import tracdap.rt._impl.core.data as _data
|
30
30
|
import tracdap.rt._impl.core.logging as _logging
|
31
31
|
import tracdap.rt._impl.core.storage as _storage
|
32
|
+
import tracdap.rt._impl.core.struct as _struct
|
32
33
|
import tracdap.rt._impl.core.type_system as _types
|
33
34
|
import tracdap.rt._impl.core.util as _util
|
34
35
|
import tracdap.rt._impl.core.validation as _val
|
@@ -184,7 +185,25 @@ class TracContextImpl(_api.TracContext):
|
|
184
185
|
def get_polars_table(self, dataset_name: str) -> "_data.polars.DataFrame":
|
185
186
|
|
186
187
|
return self.get_table(dataset_name, _eapi.POLARS)
|
187
|
-
|
188
|
+
|
189
|
+
def get_struct(self, struct_name: str, python_class: type[_eapi.STRUCT_TYPE] = None) -> _eapi.STRUCT_TYPE:
|
190
|
+
|
191
|
+
_val.validate_signature(self.get_struct, struct_name, python_class)
|
192
|
+
|
193
|
+
self.__val.check_item_valid_identifier(struct_name, TracContextValidator.DATASET)
|
194
|
+
self.__val.check_item_defined_in_model(struct_name, TracContextValidator.DATASET)
|
195
|
+
self.__val.check_item_available_in_context(struct_name, TracContextValidator.DATASET)
|
196
|
+
|
197
|
+
data_view: _data.DataView = self.__local_ctx.get(struct_name)
|
198
|
+
part_key = _data.DataPartKey.for_root()
|
199
|
+
|
200
|
+
self.__val.check_context_object_type(struct_name, data_view, _data.DataView)
|
201
|
+
self.__val.check_context_data_view_type(struct_name, data_view, _meta.ObjectType.DATA)
|
202
|
+
self.__val.check_dataset_schema_defined(struct_name, data_view)
|
203
|
+
|
204
|
+
struct_data: dict = data_view.parts[part_key][0].content
|
205
|
+
return _struct.StructProcessor.parse_struct(struct_data, None, python_class)
|
206
|
+
|
188
207
|
def get_file(self, file_name: str) -> bytes:
|
189
208
|
|
190
209
|
_val.validate_signature(self.get_file, file_name)
|
@@ -199,7 +218,7 @@ class TracContextImpl(_api.TracContext):
|
|
199
218
|
self.__val.check_context_data_view_type(file_name, file_view, _meta.ObjectType.FILE)
|
200
219
|
self.__val.check_file_content_present(file_name, file_view)
|
201
220
|
|
202
|
-
return file_view.file_item.
|
221
|
+
return file_view.file_item.content
|
203
222
|
|
204
223
|
def get_file_stream(self, file_name: str) -> tp.ContextManager[tp.BinaryIO]:
|
205
224
|
|
@@ -274,13 +293,15 @@ class TracContextImpl(_api.TracContext):
|
|
274
293
|
# Prefer static schemas for data conformance
|
275
294
|
|
276
295
|
if static_schema is not None:
|
277
|
-
|
296
|
+
trac_schema = static_schema
|
297
|
+
native_schema = _data.DataMapping.trac_to_arrow_schema(static_schema)
|
278
298
|
else:
|
279
|
-
|
299
|
+
trac_schema = _data.DataMapping.arrow_to_trac_schema(data_view.arrow_schema)
|
300
|
+
native_schema = data_view.arrow_schema
|
280
301
|
|
281
302
|
# Data conformance is applied automatically inside the converter, if schema != None
|
282
|
-
table = converter.to_internal(dataset,
|
283
|
-
item = _data.DataItem(
|
303
|
+
table = converter.to_internal(dataset, native_schema)
|
304
|
+
item = _data.DataItem.for_table(table, native_schema, trac_schema)
|
284
305
|
|
285
306
|
updated_view = _data.DataMapping.add_item_to_view(data_view, part_key, item)
|
286
307
|
|
@@ -293,7 +314,34 @@ class TracContextImpl(_api.TracContext):
|
|
293
314
|
def put_polars_table(self, dataset_name: str, dataset: "_data.polars.DataFrame"):
|
294
315
|
|
295
316
|
self.put_table(dataset_name, dataset, _eapi.POLARS)
|
296
|
-
|
317
|
+
|
318
|
+
def put_struct(self, struct_name: str, struct: _eapi.STRUCT_TYPE):
|
319
|
+
|
320
|
+
_val.validate_signature(self.put_struct, struct_name, struct)
|
321
|
+
|
322
|
+
self.__val.check_item_valid_identifier(struct_name, TracContextValidator.DATASET)
|
323
|
+
self.__val.check_item_is_model_output(struct_name, TracContextValidator.DATASET)
|
324
|
+
|
325
|
+
static_schema = self.__get_static_schema(self.__model_def, struct_name)
|
326
|
+
data_view = self.__local_ctx.get(struct_name)
|
327
|
+
part_key = _data.DataPartKey.for_root()
|
328
|
+
|
329
|
+
if data_view is None:
|
330
|
+
if static_schema is not None:
|
331
|
+
data_view = _data.DataView.for_trac_schema(static_schema)
|
332
|
+
else:
|
333
|
+
data_view = _data.DataView.create_empty()
|
334
|
+
|
335
|
+
self.__val.check_context_object_type(struct_name, data_view, _data.DataView)
|
336
|
+
self.__val.check_context_data_view_type(struct_name, data_view, _meta.ObjectType.DATA)
|
337
|
+
self.__val.check_dataset_schema_defined(struct_name, data_view)
|
338
|
+
self.__val.check_dataset_part_not_present(struct_name, data_view, part_key)
|
339
|
+
|
340
|
+
data_item = _data.DataItem.for_struct(struct)
|
341
|
+
updated_view = _data.DataMapping.add_item_to_view(data_view, part_key, data_item)
|
342
|
+
|
343
|
+
self.__local_ctx[struct_name] = updated_view
|
344
|
+
|
297
345
|
def put_file(self, file_name: str, file_content: tp.Union[bytes, bytearray]):
|
298
346
|
|
299
347
|
_val.validate_signature(self.put_file, file_name, file_content)
|
@@ -840,7 +888,13 @@ class TracContextValidator(TracContextErrorReporter):
|
|
840
888
|
|
841
889
|
schema = data_view.trac_schema if data_view is not None else None
|
842
890
|
|
843
|
-
if schema is None
|
891
|
+
if schema is None:
|
892
|
+
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
893
|
+
|
894
|
+
if schema.schemaType == _meta.SchemaType.TABLE and (schema.table is None or not schema.table.fields):
|
895
|
+
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
896
|
+
|
897
|
+
if schema.schemaType == _meta.SchemaType.STRUCT and (schema.struct is None or not schema.struct.fields):
|
844
898
|
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
845
899
|
|
846
900
|
def check_dataset_schema_not_defined(self, dataset_name: str, data_view: _data.DataView):
|
@@ -945,12 +999,12 @@ class TracContextValidator(TracContextErrorReporter):
|
|
945
999
|
|
946
1000
|
def check_file_content_present(self, file_name: str, file_view: _data.DataView):
|
947
1001
|
|
948
|
-
if file_view.file_item is None or
|
1002
|
+
if file_view.file_item is None or file_view.file_item.content is None:
|
949
1003
|
self._report_error(f"File content is missing or empty for [{file_name}] in the current context")
|
950
1004
|
|
951
1005
|
def check_file_content_not_present(self, file_name: str, file_view: _data.DataView):
|
952
1006
|
|
953
|
-
if file_view.file_item is not None and file_view.file_item.
|
1007
|
+
if file_view.file_item is not None and file_view.file_item.content is not None:
|
954
1008
|
self._report_error(f"File content is already present for [{file_name}] in the current context")
|
955
1009
|
|
956
1010
|
def check_storage_valid_identifier(self, storage_key):
|
@@ -38,12 +38,12 @@ DEV_MODE_JOB_CONFIG = [
|
|
38
38
|
re.compile(r"job\.\w+\.model"),
|
39
39
|
re.compile(r"job\.\w+\.flow"),
|
40
40
|
|
41
|
-
re.compile(r".*\.jobs\.\
|
42
|
-
re.compile(r".*\.jobs\.\
|
43
|
-
re.compile(r".*\.jobs\.\
|
44
|
-
re.compile(r".*\.jobs\.\
|
45
|
-
re.compile(r".*\.jobs\.\
|
46
|
-
re.compile(r".*\.jobs\.\
|
41
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.parameters\.\w+"),
|
42
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.inputs\.\w+"),
|
43
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.outputs\.\w+"),
|
44
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.models\.\w+"),
|
45
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.model"),
|
46
|
+
re.compile(r".*\.jobs\[\d+]\.\w+\.flow")
|
47
47
|
]
|
48
48
|
|
49
49
|
DEV_MODE_SYS_CONFIG = []
|
@@ -765,10 +765,15 @@ class DevModeTranslator:
|
|
765
765
|
else:
|
766
766
|
p_spec = param_specs[p_name]
|
767
767
|
|
768
|
-
|
768
|
+
try:
|
769
|
+
cls._log.info(f"Encoding parameter [{p_name}] as {p_spec.paramType.basicType.name}")
|
770
|
+
encoded_value = _types.MetadataCodec.convert_value(p_value, p_spec.paramType)
|
771
|
+
encoded_values[p_name] = encoded_value
|
769
772
|
|
770
|
-
|
771
|
-
|
773
|
+
except Exception as e:
|
774
|
+
msg = f"Failed to encode parameter [{p_name}]: {str(e)}"
|
775
|
+
cls._log.error(msg)
|
776
|
+
raise _ex.EConfigParse(msg) from e
|
772
777
|
|
773
778
|
return encoded_values
|
774
779
|
|
@@ -842,7 +847,7 @@ class DevModeTranslator:
|
|
842
847
|
if isinstance(data_value, str):
|
843
848
|
storage_path = data_value
|
844
849
|
storage_key = self._sys_config.storage.defaultBucket
|
845
|
-
storage_format = self.infer_format(storage_path, self._sys_config.storage)
|
850
|
+
storage_format = self.infer_format(storage_path, self._sys_config.storage, schema)
|
846
851
|
snap_version = 1
|
847
852
|
|
848
853
|
elif isinstance(data_value, dict):
|
@@ -853,7 +858,7 @@ class DevModeTranslator:
|
|
853
858
|
raise _ex.EConfigParse(f"Invalid configuration for input [{data_key}] (missing required value 'path'")
|
854
859
|
|
855
860
|
storage_key = data_value.get("storageKey") or self._sys_config.storage.defaultBucket
|
856
|
-
storage_format = data_value.get("format") or self.infer_format(storage_path, self._sys_config.storage)
|
861
|
+
storage_format = data_value.get("format") or self.infer_format(storage_path, self._sys_config.storage, schema)
|
857
862
|
snap_version = 1
|
858
863
|
|
859
864
|
else:
|
@@ -939,12 +944,18 @@ class DevModeTranslator:
|
|
939
944
|
return file_id
|
940
945
|
|
941
946
|
@staticmethod
|
942
|
-
def infer_format(storage_path: str, storage_config: _cfg.StorageConfig):
|
947
|
+
def infer_format(storage_path: str, storage_config: _cfg.StorageConfig, schema: tp.Optional[_meta.SchemaDefinition]):
|
948
|
+
|
949
|
+
schema_type = schema.schemaType if schema and schema.schemaType else _meta.SchemaType.TABLE
|
943
950
|
|
944
951
|
if re.match(r'.*\.\w+$', storage_path):
|
945
952
|
extension = pathlib.Path(storage_path).suffix
|
946
|
-
|
947
|
-
|
953
|
+
# Only try to map TABLE codecs through IDataFormat for now
|
954
|
+
if schema_type == _meta.SchemaType.TABLE:
|
955
|
+
codec = _storage.FormatManager.get_data_format(extension, format_options={})
|
956
|
+
return codec.format_code()
|
957
|
+
else:
|
958
|
+
return extension[1:] if extension.startswith(".") else extension
|
948
959
|
|
949
960
|
else:
|
950
961
|
return storage_config.defaultFormat
|