tracdap-runtime 0.7.0rc1__py3-none-any.whl → 0.8.0b2__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/_exec/actors.py +5 -4
- tracdap/rt/_exec/context.py +166 -74
- tracdap/rt/_exec/dev_mode.py +147 -71
- tracdap/rt/_exec/engine.py +224 -99
- tracdap/rt/_exec/functions.py +122 -80
- tracdap/rt/_exec/graph.py +23 -35
- tracdap/rt/_exec/graph_builder.py +250 -113
- tracdap/rt/_exec/runtime.py +24 -10
- tracdap/rt/_exec/server.py +4 -3
- tracdap/rt/_impl/config_parser.py +3 -2
- tracdap/rt/_impl/data.py +89 -16
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +3 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +8 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +64 -62
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +16 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +27 -25
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +14 -4
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +3 -3
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +2 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +4 -4
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +4 -2
- tracdap/rt/_impl/logging.py +195 -0
- tracdap/rt/_impl/models.py +11 -8
- tracdap/rt/_impl/repos.py +5 -3
- tracdap/rt/_impl/schemas.py +2 -2
- tracdap/rt/_impl/shim.py +3 -2
- tracdap/rt/_impl/static_api.py +53 -33
- tracdap/rt/_impl/storage.py +4 -3
- tracdap/rt/_impl/util.py +1 -111
- tracdap/rt/_impl/validation.py +57 -30
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/__init__.py +6 -3
- tracdap/rt/api/file_types.py +29 -0
- tracdap/rt/api/hook.py +15 -7
- tracdap/rt/api/model_api.py +16 -0
- tracdap/rt/api/static_api.py +211 -125
- tracdap/rt/config/__init__.py +6 -6
- tracdap/rt/config/common.py +11 -1
- tracdap/rt/config/platform.py +4 -6
- tracdap/rt/ext/plugins.py +2 -2
- tracdap/rt/launch/launch.py +9 -11
- tracdap/rt/metadata/__init__.py +11 -9
- tracdap/rt/metadata/file.py +8 -0
- tracdap/rt/metadata/job.py +16 -0
- tracdap/rt/metadata/model.py +12 -2
- tracdap/rt/metadata/object.py +2 -0
- tracdap/rt/metadata/object_id.py +2 -0
- {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0b2.dist-info}/METADATA +15 -15
- {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0b2.dist-info}/RECORD +52 -50
- {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0b2.dist-info}/WHEEL +1 -1
- {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0b2.dist-info}/LICENSE +0 -0
- {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0b2.dist-info}/top_level.txt +0 -0
tracdap/rt/_exec/functions.py
CHANGED
@@ -13,10 +13,10 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
import copy
|
18
17
|
import datetime
|
19
18
|
import abc
|
19
|
+
import pathlib
|
20
20
|
import random
|
21
21
|
import dataclasses as dc # noqa
|
22
22
|
|
@@ -28,6 +28,7 @@ import tracdap.rt._exec.graph_builder as _graph
|
|
28
28
|
import tracdap.rt._impl.config_parser as _cfg_p # noqa
|
29
29
|
import tracdap.rt._impl.type_system as _types # noqa
|
30
30
|
import tracdap.rt._impl.data as _data # noqa
|
31
|
+
import tracdap.rt._impl.logging as _logging # noqa
|
31
32
|
import tracdap.rt._impl.storage as _storage # noqa
|
32
33
|
import tracdap.rt._impl.models as _models # noqa
|
33
34
|
import tracdap.rt._impl.util as _util # noqa
|
@@ -227,11 +228,22 @@ class BuildJobResultFunc(NodeFunction[_config.JobResult]):
|
|
227
228
|
job_result.jobId = self.node.job_id
|
228
229
|
job_result.statusCode = meta.JobStatusCode.SUCCEEDED
|
229
230
|
|
231
|
+
if self.node.result_id is not None:
|
232
|
+
|
233
|
+
result_def = meta.ResultDefinition()
|
234
|
+
result_def.jobId = _util.selector_for(self.node.job_id)
|
235
|
+
result_def.statusCode = meta.JobStatusCode.SUCCEEDED
|
236
|
+
|
237
|
+
result_key = _util.object_key(self.node.result_id)
|
238
|
+
result_obj = meta.ObjectDefinition(objectType=meta.ObjectType.RESULT, result=result_def)
|
239
|
+
|
240
|
+
job_result.results[result_key] = result_obj
|
241
|
+
|
230
242
|
# TODO: Handle individual failed results
|
231
243
|
|
232
|
-
for
|
244
|
+
for obj_key, node_id in self.node.outputs.objects.items():
|
233
245
|
obj_def = _ctx_lookup(node_id, ctx)
|
234
|
-
job_result.results[
|
246
|
+
job_result.results[obj_key] = obj_def
|
235
247
|
|
236
248
|
for bundle_id in self.node.outputs.bundles:
|
237
249
|
bundle = _ctx_lookup(bundle_id, ctx)
|
@@ -241,9 +253,9 @@ class BuildJobResultFunc(NodeFunction[_config.JobResult]):
|
|
241
253
|
|
242
254
|
runtime_outputs = _ctx_lookup(self.node.runtime_outputs, ctx)
|
243
255
|
|
244
|
-
for
|
256
|
+
for obj_key, node_id in runtime_outputs.objects.items():
|
245
257
|
obj_def = _ctx_lookup(node_id, ctx)
|
246
|
-
job_result.results[
|
258
|
+
job_result.results[obj_key] = obj_def
|
247
259
|
|
248
260
|
for bundle_id in runtime_outputs.bundles:
|
249
261
|
bundle = _ctx_lookup(bundle_id, ctx)
|
@@ -252,37 +264,6 @@ class BuildJobResultFunc(NodeFunction[_config.JobResult]):
|
|
252
264
|
return job_result
|
253
265
|
|
254
266
|
|
255
|
-
class SaveJobResultFunc(NodeFunction[None]):
|
256
|
-
|
257
|
-
def __init__(self, node: SaveJobResultNode):
|
258
|
-
super().__init__()
|
259
|
-
self.node = node
|
260
|
-
|
261
|
-
def _execute(self, ctx: NodeContext) -> None:
|
262
|
-
|
263
|
-
job_result = _ctx_lookup(self.node.job_result_id, ctx)
|
264
|
-
|
265
|
-
if not self.node.result_spec.save_result:
|
266
|
-
return None
|
267
|
-
|
268
|
-
job_result_format = self.node.result_spec.result_format
|
269
|
-
job_result_str = _cfg_p.ConfigQuoter.quote(job_result, job_result_format)
|
270
|
-
job_result_bytes = bytes(job_result_str, "utf-8")
|
271
|
-
|
272
|
-
job_key = _util.object_key(job_result.jobId)
|
273
|
-
job_result_file = f"job_result_{job_key}.{self.node.result_spec.result_format}"
|
274
|
-
job_result_path = pathlib \
|
275
|
-
.Path(self.node.result_spec.result_dir) \
|
276
|
-
.joinpath(job_result_file)
|
277
|
-
|
278
|
-
_util.logger_for_object(self).info(f"Saving job result to [{job_result_path}]")
|
279
|
-
|
280
|
-
with open(job_result_path, "xb") as result_stream:
|
281
|
-
result_stream.write(job_result_bytes)
|
282
|
-
|
283
|
-
return None
|
284
|
-
|
285
|
-
|
286
267
|
class DataViewFunc(NodeFunction[_data.DataView]):
|
287
268
|
|
288
269
|
def __init__(self, node: DataViewNode):
|
@@ -296,8 +277,13 @@ class DataViewFunc(NodeFunction[_data.DataView]):
|
|
296
277
|
|
297
278
|
# Map empty item -> emtpy view (for optional inputs not supplied)
|
298
279
|
if root_item.is_empty():
|
299
|
-
return _data.DataView.create_empty()
|
280
|
+
return _data.DataView.create_empty(root_item.object_type)
|
300
281
|
|
282
|
+
# Handle file data views
|
283
|
+
if root_item.object_type == meta.ObjectType.FILE:
|
284
|
+
return _data.DataView.for_file_item(root_item)
|
285
|
+
|
286
|
+
# Everything else is a regular data view
|
301
287
|
if self.node.schema is not None and len(self.node.schema.table.fields) > 0:
|
302
288
|
trac_schema = self.node.schema
|
303
289
|
else:
|
@@ -322,7 +308,11 @@ class DataItemFunc(NodeFunction[_data.DataItem]):
|
|
322
308
|
|
323
309
|
# Map empty view -> emtpy item (for optional outputs not supplied)
|
324
310
|
if data_view.is_empty():
|
325
|
-
return _data.DataItem.create_empty()
|
311
|
+
return _data.DataItem.create_empty(data_view.object_type)
|
312
|
+
|
313
|
+
# Handle file data views
|
314
|
+
if data_view.object_type == meta.ObjectType.FILE:
|
315
|
+
return data_view.file_item
|
326
316
|
|
327
317
|
# TODO: Support selecting data item described by self.node
|
328
318
|
|
@@ -342,25 +332,24 @@ class DataResultFunc(NodeFunction[ObjectBundle]):
|
|
342
332
|
|
343
333
|
def _execute(self, ctx: NodeContext) -> ObjectBundle:
|
344
334
|
|
345
|
-
|
335
|
+
data_spec = _ctx_lookup(self.node.data_save_id, ctx)
|
346
336
|
|
347
|
-
|
348
|
-
if data_item.is_empty():
|
349
|
-
return {}
|
337
|
+
result_bundle = dict()
|
350
338
|
|
351
|
-
|
339
|
+
# Do not record output metadata for optional outputs that are empty
|
340
|
+
if data_spec.is_empty():
|
341
|
+
return result_bundle
|
352
342
|
|
353
|
-
|
354
|
-
|
343
|
+
if self.node.data_key is not None:
|
344
|
+
result_bundle[self.node.data_key] = meta.ObjectDefinition(objectType=meta.ObjectType.DATA, data=data_spec.data_def)
|
355
345
|
|
356
|
-
|
357
|
-
|
346
|
+
if self.node.file_key is not None:
|
347
|
+
result_bundle[self.node.file_key] = meta.ObjectDefinition(objectType=meta.ObjectType.FILE, file=data_spec.file_def)
|
358
348
|
|
359
|
-
|
360
|
-
self.node.
|
361
|
-
self.node.storage_key: storage_result}
|
349
|
+
if self.node.storage_key is not None:
|
350
|
+
result_bundle[self.node.storage_key] = meta.ObjectDefinition(objectType=meta.ObjectType.STORAGE, storage=data_spec.storage_def)
|
362
351
|
|
363
|
-
return
|
352
|
+
return result_bundle
|
364
353
|
|
365
354
|
|
366
355
|
class DynamicDataSpecFunc(NodeFunction[_data.DataSpec]):
|
@@ -443,11 +432,7 @@ class DynamicDataSpecFunc(NodeFunction[_data.DataSpec]):
|
|
443
432
|
|
444
433
|
# Dynamic data def will always use an embedded schema (this is no ID for an external schema)
|
445
434
|
|
446
|
-
return _data.DataSpec(
|
447
|
-
data_item,
|
448
|
-
data_def,
|
449
|
-
storage_def,
|
450
|
-
schema_def=None)
|
435
|
+
return _data.DataSpec.create_data_spec(data_item, data_def, storage_def, schema_def=None)
|
451
436
|
|
452
437
|
|
453
438
|
class _LoadSaveDataFunc(abc.ABC):
|
@@ -455,6 +440,16 @@ class _LoadSaveDataFunc(abc.ABC):
|
|
455
440
|
def __init__(self, storage: _storage.StorageManager):
|
456
441
|
self.storage = storage
|
457
442
|
|
443
|
+
@classmethod
|
444
|
+
def _choose_data_spec(cls, spec_id, spec, ctx: NodeContext):
|
445
|
+
|
446
|
+
if spec_id is not None:
|
447
|
+
return _ctx_lookup(spec_id, ctx)
|
448
|
+
elif spec is not None:
|
449
|
+
return spec
|
450
|
+
else:
|
451
|
+
raise _ex.EUnexpected()
|
452
|
+
|
458
453
|
def _choose_copy(self, data_item: str, storage_def: meta.StorageDefinition) -> meta.StorageCopy:
|
459
454
|
|
460
455
|
# Metadata should be checked for consistency before a job is accepted
|
@@ -491,9 +486,19 @@ class LoadDataFunc( _LoadSaveDataFunc, NodeFunction[_data.DataItem],):
|
|
491
486
|
|
492
487
|
def _execute(self, ctx: NodeContext) -> _data.DataItem:
|
493
488
|
|
494
|
-
data_spec =
|
489
|
+
data_spec = self._choose_data_spec(self.node.spec_id, self.node.spec, ctx)
|
495
490
|
data_copy = self._choose_copy(data_spec.data_item, data_spec.storage_def)
|
496
|
-
|
491
|
+
|
492
|
+
if data_spec.object_type == _api.ObjectType.DATA:
|
493
|
+
return self._load_data(data_spec, data_copy)
|
494
|
+
|
495
|
+
elif data_spec.object_type == _api.ObjectType.FILE:
|
496
|
+
return self._load_file(data_copy)
|
497
|
+
|
498
|
+
else:
|
499
|
+
raise _ex.EUnexpected()
|
500
|
+
|
501
|
+
def _load_data(self, data_spec, data_copy):
|
497
502
|
|
498
503
|
trac_schema = data_spec.schema_def if data_spec.schema_def else data_spec.data_def.schema
|
499
504
|
arrow_schema = _data.DataMapping.trac_to_arrow_schema(trac_schema) if trac_schema else None
|
@@ -503,36 +508,52 @@ class LoadDataFunc( _LoadSaveDataFunc, NodeFunction[_data.DataItem],):
|
|
503
508
|
for opt_key, opt_value in data_spec.storage_def.storageOptions.items():
|
504
509
|
options[opt_key] = _types.MetadataCodec.decode_value(opt_value)
|
505
510
|
|
506
|
-
|
511
|
+
storage = self.storage.get_data_storage(data_copy.storageKey)
|
512
|
+
table = storage.read_table(
|
507
513
|
data_copy.storagePath,
|
508
514
|
data_copy.storageFormat,
|
509
515
|
arrow_schema,
|
510
516
|
storage_options=options)
|
511
517
|
|
512
|
-
return _data.DataItem(table.schema, table)
|
518
|
+
return _data.DataItem(_api.ObjectType.DATA, table.schema, table)
|
513
519
|
|
520
|
+
def _load_file(self, data_copy):
|
514
521
|
|
515
|
-
|
522
|
+
storage = self.storage.get_file_storage(data_copy.storageKey)
|
523
|
+
raw_bytes = storage.read_bytes(data_copy.storagePath)
|
524
|
+
|
525
|
+
return _data.DataItem(_api.ObjectType.FILE, raw_bytes=raw_bytes)
|
526
|
+
|
527
|
+
|
528
|
+
class SaveDataFunc(_LoadSaveDataFunc, NodeFunction[_data.DataSpec]):
|
516
529
|
|
517
530
|
def __init__(self, node: SaveDataNode, storage: _storage.StorageManager):
|
518
531
|
super().__init__(storage)
|
519
532
|
self.node = node
|
520
533
|
|
521
|
-
def _execute(self, ctx: NodeContext):
|
534
|
+
def _execute(self, ctx: NodeContext) -> _data.DataSpec:
|
522
535
|
|
523
536
|
# Item to be saved should exist in the current context
|
524
537
|
data_item = _ctx_lookup(self.node.data_item_id, ctx)
|
525
538
|
|
539
|
+
# Metadata already exists as data_spec but may not contain schema, row count, file size etc.
|
540
|
+
data_spec = self._choose_data_spec(self.node.spec_id, self.node.spec, ctx)
|
541
|
+
data_copy = self._choose_copy(data_spec.data_item, data_spec.storage_def)
|
542
|
+
|
526
543
|
# Do not save empty outputs (optional outputs that were not produced)
|
527
544
|
if data_item.is_empty():
|
528
|
-
return
|
545
|
+
return _data.DataSpec.create_empty_spec(data_item.object_type)
|
529
546
|
|
530
|
-
|
531
|
-
|
547
|
+
if data_item.object_type == _api.ObjectType.DATA:
|
548
|
+
return self._save_data(data_item, data_spec, data_copy)
|
532
549
|
|
533
|
-
|
534
|
-
|
535
|
-
|
550
|
+
elif data_item.object_type == _api.ObjectType.FILE:
|
551
|
+
return self._save_file(data_item, data_spec, data_copy)
|
552
|
+
|
553
|
+
else:
|
554
|
+
raise _ex.EUnexpected()
|
555
|
+
|
556
|
+
def _save_data(self, data_item, data_spec, data_copy):
|
536
557
|
|
537
558
|
# Current implementation will always put an Arrow table in the data item
|
538
559
|
# Empty tables are allowed, so explicitly check if table is None
|
@@ -546,11 +567,32 @@ class SaveDataFunc(_LoadSaveDataFunc, NodeFunction[None]):
|
|
546
567
|
for opt_key, opt_value in data_spec.storage_def.storageOptions.items():
|
547
568
|
options[opt_key] = _types.MetadataCodec.decode_value(opt_value)
|
548
569
|
|
549
|
-
|
570
|
+
storage = self.storage.get_data_storage(data_copy.storageKey)
|
571
|
+
storage.write_table(
|
550
572
|
data_copy.storagePath, data_copy.storageFormat,
|
551
573
|
data_item.table,
|
552
574
|
storage_options=options, overwrite=False)
|
553
575
|
|
576
|
+
data_spec = copy.deepcopy(data_spec)
|
577
|
+
# TODO: Save row count in metadata
|
578
|
+
|
579
|
+
if data_spec.data_def.schema is None and data_spec.data_def.schemaId is None:
|
580
|
+
data_spec.data_def.schema = _data.DataMapping.arrow_to_trac_schema(data_item.table.schema)
|
581
|
+
|
582
|
+
return data_spec
|
583
|
+
|
584
|
+
def _save_file(self, data_item, data_spec, data_copy):
|
585
|
+
|
586
|
+
if data_item.raw_bytes is None:
|
587
|
+
raise _ex.EUnexpected()
|
588
|
+
|
589
|
+
storage = self.storage.get_file_storage(data_copy.storageKey)
|
590
|
+
storage.write_bytes(data_copy.storagePath, data_item.raw_bytes)
|
591
|
+
|
592
|
+
data_spec = copy.deepcopy(data_spec)
|
593
|
+
data_spec.file_def.size = len(data_item.raw_bytes)
|
594
|
+
|
595
|
+
return data_spec
|
554
596
|
|
555
597
|
def _model_def_for_import(import_details: meta.ImportModelJob):
|
556
598
|
|
@@ -571,8 +613,6 @@ class ImportModelFunc(NodeFunction[meta.ObjectDefinition]):
|
|
571
613
|
self.node = node
|
572
614
|
self._models = models
|
573
615
|
|
574
|
-
self._log = _util.logger_for_object(self)
|
575
|
-
|
576
616
|
def _execute(self, ctx: NodeContext) -> meta.ObjectDefinition:
|
577
617
|
|
578
618
|
model_stub = _model_def_for_import(self.node.import_details)
|
@@ -589,13 +629,15 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
|
|
589
629
|
self, node: RunModelNode,
|
590
630
|
model_class: _api.TracModel.__class__,
|
591
631
|
checkout_directory: pathlib.Path,
|
592
|
-
storage_manager: _storage.StorageManager
|
632
|
+
storage_manager: _storage.StorageManager,
|
633
|
+
log_provider: _logging.LogProvider):
|
593
634
|
|
594
635
|
super().__init__()
|
595
636
|
self.node = node
|
596
637
|
self.model_class = model_class
|
597
638
|
self.checkout_directory = checkout_directory
|
598
639
|
self.storage_manager = storage_manager
|
640
|
+
self.log_provider = log_provider
|
599
641
|
|
600
642
|
def _execute(self, ctx: NodeContext) -> Bundle[_data.DataView]:
|
601
643
|
|
@@ -622,7 +664,7 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
|
|
622
664
|
for storage_key in self.node.storage_access:
|
623
665
|
if self.storage_manager.has_file_storage(storage_key, external=True):
|
624
666
|
storage_impl = self.storage_manager.get_file_storage(storage_key, external=True)
|
625
|
-
storage = _ctx.TracFileStorageImpl(storage_key, storage_impl, write_access, self.checkout_directory)
|
667
|
+
storage = _ctx.TracFileStorageImpl(storage_key, storage_impl, write_access, self.checkout_directory, self.log_provider)
|
626
668
|
storage_map[storage_key] = storage
|
627
669
|
elif self.storage_manager.has_data_storage(storage_key, external=True):
|
628
670
|
storage_impl = self.storage_manager.get_data_storage(storage_key, external=True)
|
@@ -630,7 +672,7 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
|
|
630
672
|
if not isinstance(storage_impl, _storage.IDataStorageBase):
|
631
673
|
raise _ex.EStorageConfig(f"External storage for [{storage_key}] is using the legacy storage framework]")
|
632
674
|
converter = _data.DataConverter.noop()
|
633
|
-
storage = _ctx.TracDataStorageImpl(storage_key, storage_impl, converter, write_access, self.checkout_directory)
|
675
|
+
storage = _ctx.TracDataStorageImpl(storage_key, storage_impl, converter, write_access, self.checkout_directory, self.log_provider)
|
634
676
|
storage_map[storage_key] = storage
|
635
677
|
else:
|
636
678
|
raise _ex.EStorageConfig(f"External storage is not available: [{storage_key}]")
|
@@ -642,12 +684,12 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
|
|
642
684
|
trac_ctx = _ctx.TracDataContextImpl(
|
643
685
|
self.node.model_def, self.model_class,
|
644
686
|
local_ctx, dynamic_outputs, storage_map,
|
645
|
-
self.checkout_directory)
|
687
|
+
self.checkout_directory, self.log_provider)
|
646
688
|
else:
|
647
689
|
trac_ctx = _ctx.TracContextImpl(
|
648
690
|
self.node.model_def, self.model_class,
|
649
691
|
local_ctx, dynamic_outputs,
|
650
|
-
self.checkout_directory)
|
692
|
+
self.checkout_directory, self.log_provider)
|
651
693
|
|
652
694
|
try:
|
653
695
|
model = self.model_class()
|
@@ -750,9 +792,10 @@ class FunctionResolver:
|
|
750
792
|
|
751
793
|
__ResolveFunc = tp.Callable[['FunctionResolver', Node[_T]], NodeFunction[_T]]
|
752
794
|
|
753
|
-
def __init__(self, models: _models.ModelLoader, storage: _storage.StorageManager):
|
795
|
+
def __init__(self, models: _models.ModelLoader, storage: _storage.StorageManager, log_provider: _logging.LogProvider):
|
754
796
|
self._models = models
|
755
797
|
self._storage = storage
|
798
|
+
self._log_provider = log_provider
|
756
799
|
|
757
800
|
def resolve_node(self, node: Node[_T]) -> NodeFunction[_T]:
|
758
801
|
|
@@ -788,7 +831,7 @@ class FunctionResolver:
|
|
788
831
|
checkout_directory = self._models.model_load_checkout_directory(node.model_scope, node.model_def)
|
789
832
|
storage_manager = self._storage if node.storage_access else None
|
790
833
|
|
791
|
-
return RunModelFunc(node, model_class, checkout_directory, storage_manager)
|
834
|
+
return RunModelFunc(node, model_class, checkout_directory, storage_manager, self._log_provider)
|
792
835
|
|
793
836
|
__basic_node_mapping: tp.Dict[Node.__class__, NodeFunction.__class__] = {
|
794
837
|
|
@@ -799,7 +842,6 @@ class FunctionResolver:
|
|
799
842
|
DataViewNode: DataViewFunc,
|
800
843
|
DataItemNode: DataItemFunc,
|
801
844
|
BuildJobResultNode: BuildJobResultFunc,
|
802
|
-
SaveJobResultNode: SaveJobResultFunc,
|
803
845
|
DataResultNode: DataResultFunc,
|
804
846
|
StaticValueNode: StaticValueFunc,
|
805
847
|
RuntimeOutputsNode: RuntimeOutputsFunc,
|
tracdap/rt/_exec/graph.py
CHANGED
@@ -13,7 +13,6 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
import pathlib
|
17
16
|
import typing as tp
|
18
17
|
import dataclasses as dc
|
19
18
|
|
@@ -182,15 +181,6 @@ class JobOutputs:
|
|
182
181
|
bundles: tp.List[NodeId[ObjectBundle]] = dc.field(default_factory=list)
|
183
182
|
|
184
183
|
|
185
|
-
# TODO: Where does this go?
|
186
|
-
@dc.dataclass(frozen=True)
|
187
|
-
class JobResultSpec:
|
188
|
-
|
189
|
-
save_result: bool = False
|
190
|
-
result_dir: tp.Union[str, pathlib.Path] = None
|
191
|
-
result_format: str = None
|
192
|
-
|
193
|
-
|
194
184
|
# ----------------------------------------------------------------------------------------------------------------------
|
195
185
|
# NODE DEFINITIONS
|
196
186
|
# ----------------------------------------------------------------------------------------------------------------------
|
@@ -309,20 +299,18 @@ class DataItemNode(MappingNode[_data.DataItem]):
|
|
309
299
|
@_node_type
|
310
300
|
class DataResultNode(Node[ObjectBundle]):
|
311
301
|
|
302
|
+
# TODO: Remove this node type
|
303
|
+
# Either produce metadata in SaveDataNode, or handle DataSpec outputs in result processing nodes
|
304
|
+
|
312
305
|
output_name: str
|
313
|
-
|
314
|
-
data_spec_id: NodeId[_data.DataSpec]
|
315
|
-
data_save_id: NodeId[type(None)]
|
306
|
+
data_save_id: NodeId[_data.DataSpec]
|
316
307
|
|
317
|
-
data_key: str
|
318
|
-
|
308
|
+
data_key: str = None
|
309
|
+
file_key: str = None
|
310
|
+
storage_key: str = None
|
319
311
|
|
320
312
|
def _node_dependencies(self) -> tp.Dict[NodeId, DependencyType]:
|
321
|
-
|
322
|
-
return {
|
323
|
-
self.data_item_id: DependencyType.HARD,
|
324
|
-
self.data_spec_id: DependencyType.HARD,
|
325
|
-
self.data_save_id: DependencyType.HARD}
|
313
|
+
return {self.data_save_id: DependencyType.HARD}
|
326
314
|
|
327
315
|
|
328
316
|
@_node_type
|
@@ -333,24 +321,33 @@ class LoadDataNode(Node[_data.DataItem]):
|
|
333
321
|
The latest incarnation of the item will be loaded from any available copy
|
334
322
|
"""
|
335
323
|
|
336
|
-
spec_id: NodeId[_data.DataSpec]
|
324
|
+
spec_id: tp.Optional[NodeId[_data.DataSpec]] = None
|
325
|
+
spec: tp.Optional[_data.DataSpec] = None
|
337
326
|
|
338
327
|
def _node_dependencies(self) -> tp.Dict[NodeId, DependencyType]:
|
339
|
-
|
328
|
+
deps = dict()
|
329
|
+
if self.spec_id is not None:
|
330
|
+
deps[self.spec_id] = DependencyType.HARD
|
331
|
+
return deps
|
340
332
|
|
341
333
|
|
342
334
|
@_node_type
|
343
|
-
class SaveDataNode(Node[
|
335
|
+
class SaveDataNode(Node[_data.DataSpec]):
|
344
336
|
|
345
337
|
"""
|
346
338
|
Save an individual data item to storage
|
347
339
|
"""
|
348
340
|
|
349
|
-
spec_id: NodeId[_data.DataSpec]
|
350
341
|
data_item_id: NodeId[_data.DataItem]
|
351
342
|
|
343
|
+
spec_id: tp.Optional[NodeId[_data.DataSpec]] = None
|
344
|
+
spec: tp.Optional[_data.DataSpec] = None
|
345
|
+
|
352
346
|
def _node_dependencies(self) -> tp.Dict[NodeId, DependencyType]:
|
353
|
-
|
347
|
+
deps = {self.data_item_id: DependencyType.HARD}
|
348
|
+
if self.spec_id is not None:
|
349
|
+
deps[self.spec_id] = DependencyType.HARD
|
350
|
+
return deps
|
354
351
|
|
355
352
|
|
356
353
|
@_node_type
|
@@ -395,6 +392,7 @@ class RuntimeOutputsNode(Node[JobOutputs]):
|
|
395
392
|
@_node_type
|
396
393
|
class BuildJobResultNode(Node[cfg.JobResult]):
|
397
394
|
|
395
|
+
result_id: meta.TagHeader
|
398
396
|
job_id: meta.TagHeader
|
399
397
|
|
400
398
|
outputs: JobOutputs
|
@@ -407,16 +405,6 @@ class BuildJobResultNode(Node[cfg.JobResult]):
|
|
407
405
|
return {node_id: DependencyType.HARD for node_id in dep_ids}
|
408
406
|
|
409
407
|
|
410
|
-
@_node_type
|
411
|
-
class SaveJobResultNode(Node[None]):
|
412
|
-
|
413
|
-
job_result_id: NodeId[cfg.JobResult]
|
414
|
-
result_spec: JobResultSpec
|
415
|
-
|
416
|
-
def _node_dependencies(self) -> tp.Dict[NodeId, DependencyType]:
|
417
|
-
return {self.job_result_id: DependencyType.HARD}
|
418
|
-
|
419
|
-
|
420
408
|
@_node_type
|
421
409
|
class ChildJobNode(Node[cfg.JobResult]):
|
422
410
|
|