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/dev_mode.py
CHANGED
@@ -23,6 +23,7 @@ import tracdap.rt.config as _cfg
|
|
23
23
|
import tracdap.rt.metadata as _meta
|
24
24
|
import tracdap.rt.exceptions as _ex
|
25
25
|
import tracdap.rt._impl.config_parser as _cfg_p # noqa
|
26
|
+
import tracdap.rt._impl.logging as _logging # noqa
|
26
27
|
import tracdap.rt._impl.models as _models # noqa
|
27
28
|
import tracdap.rt._impl.storage as _storage # noqa
|
28
29
|
import tracdap.rt._impl.type_system as _types # noqa
|
@@ -50,7 +51,7 @@ DEV_MODE_SYS_CONFIG = []
|
|
50
51
|
|
51
52
|
class DevModeTranslator:
|
52
53
|
|
53
|
-
_log: tp.Optional[
|
54
|
+
_log: tp.Optional[_logging.Logger] = None
|
54
55
|
|
55
56
|
@classmethod
|
56
57
|
def translate_sys_config(cls, sys_config: _cfg.RuntimeConfig, config_mgr: _cfg_p.ConfigManager):
|
@@ -137,11 +138,14 @@ class DevModeTranslator:
|
|
137
138
|
raise _ex.EConfigParse(msg)
|
138
139
|
|
139
140
|
|
140
|
-
def __init__(
|
141
|
+
def __init__(
|
142
|
+
self, sys_config: _cfg.RuntimeConfig, config_mgr: _cfg_p.ConfigManager, scratch_dir: pathlib.Path = None,
|
143
|
+
model_loader: _models.ModelLoader = None, storage_manager: _storage.StorageManager = None):
|
144
|
+
|
141
145
|
self._sys_config = sys_config
|
142
146
|
self._config_mgr = config_mgr
|
143
|
-
self.
|
144
|
-
self.
|
147
|
+
self._model_loader = model_loader or _models.ModelLoader(self._sys_config, scratch_dir)
|
148
|
+
self._storage_manager = storage_manager or _storage.StorageManager(self._sys_config)
|
145
149
|
|
146
150
|
def translate_job_config(
|
147
151
|
self, job_config: _cfg.JobConfig,
|
@@ -150,8 +154,6 @@ class DevModeTranslator:
|
|
150
154
|
|
151
155
|
try:
|
152
156
|
self._log.info(f"Applying dev mode config translation to job config")
|
153
|
-
|
154
|
-
self._model_loader = _models.ModelLoader(self._sys_config, self._scratch_dir)
|
155
157
|
self._model_loader.create_scope("DEV_MODE_TRANSLATION")
|
156
158
|
|
157
159
|
job_config = copy.deepcopy(job_config)
|
@@ -168,7 +170,6 @@ class DevModeTranslator:
|
|
168
170
|
|
169
171
|
finally:
|
170
172
|
self._model_loader.destroy_scope("DEV_MODE_TRANSLATION")
|
171
|
-
self._model_loader = None
|
172
173
|
|
173
174
|
def translate_job_def(
|
174
175
|
self, job_config: _cfg.JobConfig, job_def: _meta.JobDefinition,
|
@@ -694,7 +695,7 @@ class DevModeTranslator:
|
|
694
695
|
|
695
696
|
model_selector = job_def.runFlow.models.get(source.node)
|
696
697
|
model_obj = _util.get_job_resource(model_selector, job_config)
|
697
|
-
model_input = model_obj.model.
|
698
|
+
model_input = model_obj.model.outputs.get(source.socket)
|
698
699
|
model_outputs.append(model_input)
|
699
700
|
|
700
701
|
if len(model_outputs) == 0:
|
@@ -764,7 +765,7 @@ class DevModeTranslator:
|
|
764
765
|
else:
|
765
766
|
p_spec = param_specs[p_name]
|
766
767
|
|
767
|
-
cls._log.info(f"Encoding parameter [{p_name}] as {p_spec.paramType.basicType}")
|
768
|
+
cls._log.info(f"Encoding parameter [{p_name}] as {p_spec.paramType.basicType.name}")
|
768
769
|
|
769
770
|
encoded_value = _types.MetadataCodec.convert_value(p_value, p_spec.paramType)
|
770
771
|
encoded_values[p_name] = encoded_value
|
@@ -798,38 +799,46 @@ class DevModeTranslator:
|
|
798
799
|
if not (isinstance(input_value, str) and input_value in job_resources):
|
799
800
|
|
800
801
|
model_input = required_inputs[input_key]
|
801
|
-
input_schema = model_input.schema if model_input and not model_input.dynamic else None
|
802
802
|
|
803
|
-
|
804
|
-
|
805
|
-
|
803
|
+
if model_input.objectType == _meta.ObjectType.DATA:
|
804
|
+
schema = model_input.schema if model_input and not model_input.dynamic else None
|
805
|
+
input_id = self._process_data_socket(input_key, input_value, schema, job_resources, new_unique_file=False)
|
806
|
+
elif model_input.objectType == _meta.ObjectType.FILE:
|
807
|
+
file_type = model_input.fileType
|
808
|
+
input_id = self._process_file_socket(input_key, input_value, file_type, job_resources, new_unique_file=False)
|
809
|
+
else:
|
810
|
+
raise _ex.EUnexpected()
|
806
811
|
|
807
812
|
job_inputs[input_key] = _util.selector_for(input_id)
|
808
813
|
|
809
814
|
for output_key, output_value in job_outputs.items():
|
810
815
|
if not (isinstance(output_value, str) and output_value in job_resources):
|
811
816
|
|
812
|
-
model_output= required_outputs[output_key]
|
813
|
-
output_schema = model_output.schema if model_output and not model_output.dynamic else None
|
817
|
+
model_output = required_outputs[output_key]
|
814
818
|
|
815
|
-
|
816
|
-
|
817
|
-
|
819
|
+
if model_output.objectType == _meta.ObjectType.DATA:
|
820
|
+
schema = model_output.schema if model_output and not model_output.dynamic else None
|
821
|
+
output_id = self._process_data_socket(output_key, output_value, schema, job_resources, new_unique_file=True)
|
822
|
+
elif model_output.objectType == _meta.ObjectType.FILE:
|
823
|
+
file_type = model_output.fileType
|
824
|
+
output_id = self._process_file_socket(output_key, output_value, file_type, job_resources, new_unique_file=True)
|
825
|
+
else:
|
826
|
+
raise _ex.EUnexpected()
|
818
827
|
|
819
828
|
job_outputs[output_key] = _util.selector_for(output_id)
|
820
829
|
|
821
830
|
return job_config, job_def
|
822
831
|
|
823
|
-
def
|
824
|
-
self, data_key, data_value,
|
825
|
-
resources: tp.Dict[str, _meta.ObjectDefinition],
|
826
|
-
new_unique_file=False,
|
827
|
-
schema: tp.Optional[_meta.SchemaDefinition] = None) \
|
832
|
+
def _process_data_socket(
|
833
|
+
self, data_key, data_value, schema: tp.Optional[_meta.SchemaDefinition],
|
834
|
+
resources: tp.Dict[str, _meta.ObjectDefinition], new_unique_file=False) \
|
828
835
|
-> _meta.TagHeader:
|
829
836
|
|
830
837
|
data_id = _util.new_object_id(_meta.ObjectType.DATA)
|
831
838
|
storage_id = _util.new_object_id(_meta.ObjectType.STORAGE)
|
832
839
|
|
840
|
+
self._log.info(f"Generating data definition for [{data_key}] with ID = [{_util.object_key(data_id)}]")
|
841
|
+
|
833
842
|
if isinstance(data_value, str):
|
834
843
|
storage_path = data_value
|
835
844
|
storage_key = self._sys_config.storage.defaultBucket
|
@@ -850,43 +859,85 @@ class DevModeTranslator:
|
|
850
859
|
else:
|
851
860
|
raise _ex.EConfigParse(f"Invalid configuration for input '{data_key}'")
|
852
861
|
|
853
|
-
self._log.info(f"Generating data definition for [{data_key}] with ID = [{_util.object_key(data_id)}]")
|
854
|
-
|
855
862
|
# For unique outputs, increment the snap number to find a new unique snap
|
856
863
|
# These are not incarnations, bc likely in dev mode model code and inputs are changing
|
857
864
|
# Incarnations are for recreation of a dataset using the exact same code path and inputs
|
858
865
|
|
859
866
|
if new_unique_file:
|
867
|
+
storage_path, snap_version = self._new_unique_file(data_key, storage_key, storage_path, snap_version)
|
860
868
|
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
x_name = x_orig_path.name
|
865
|
-
|
866
|
-
if x_storage.exists(str(x_orig_path.parent)):
|
867
|
-
listing = x_storage.ls(str(x_orig_path.parent))
|
868
|
-
existing_files = list(map(lambda stat: stat.file_name, listing))
|
869
|
-
else:
|
870
|
-
existing_files = []
|
871
|
-
|
872
|
-
while x_name in existing_files:
|
869
|
+
part_key = _meta.PartKey(opaqueKey="part-root", partType=_meta.PartType.PART_ROOT)
|
870
|
+
delta_index = 1
|
871
|
+
incarnation_index = 1
|
873
872
|
|
874
|
-
|
875
|
-
|
876
|
-
storage_path = str(x_orig_path.parent.joinpath(x_name))
|
873
|
+
# This is also defined in functions.DynamicDataSpecFunc, maybe centralize?
|
874
|
+
data_item = f"data/table/{data_id.objectId}/{part_key.opaqueKey}/snap-{snap_version}/delta-{delta_index}"
|
877
875
|
|
878
|
-
|
876
|
+
data_obj = self._generate_data_definition(
|
877
|
+
part_key, snap_version, delta_index, data_item,
|
878
|
+
schema, storage_id)
|
879
879
|
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
schema=schema)
|
880
|
+
storage_obj = self._generate_storage_definition(
|
881
|
+
storage_id, storage_key, storage_path, storage_format,
|
882
|
+
data_item, incarnation_index)
|
884
883
|
|
885
884
|
resources[_util.object_key(data_id)] = data_obj
|
886
885
|
resources[_util.object_key(storage_id)] = storage_obj
|
887
886
|
|
888
887
|
return data_id
|
889
888
|
|
889
|
+
def _process_file_socket(
|
890
|
+
self, file_key, file_value, file_type: _meta.FileType,
|
891
|
+
resources: tp.Dict[str, _meta.ObjectDefinition], new_unique_file=False) \
|
892
|
+
-> _meta.TagHeader:
|
893
|
+
|
894
|
+
file_id = _util.new_object_id(_meta.ObjectType.FILE)
|
895
|
+
storage_id = _util.new_object_id(_meta.ObjectType.STORAGE)
|
896
|
+
|
897
|
+
self._log.info(f"Generating file definition for [{file_key}] with ID = [{_util.object_key(file_id)}]")
|
898
|
+
|
899
|
+
if isinstance(file_value, str):
|
900
|
+
|
901
|
+
storage_key = self._sys_config.storage.defaultBucket
|
902
|
+
storage_path = file_value
|
903
|
+
|
904
|
+
elif isinstance(file_value, dict):
|
905
|
+
|
906
|
+
storage_key = file_value.get("storageKey") or self._sys_config.storage.defaultBucket
|
907
|
+
storage_path = file_value.get("path")
|
908
|
+
|
909
|
+
if not storage_path:
|
910
|
+
raise _ex.EConfigParse(f"Invalid configuration for input [{file_key}] (missing required value 'path'")
|
911
|
+
|
912
|
+
else:
|
913
|
+
raise _ex.EConfigParse(f"Invalid configuration for input '{file_key}'")
|
914
|
+
|
915
|
+
storage_format = "application/x-binary"
|
916
|
+
file_version = 1
|
917
|
+
|
918
|
+
if new_unique_file:
|
919
|
+
storage_path, file_version = self._new_unique_file(file_key, storage_key, storage_path, file_version)
|
920
|
+
file_size = 0
|
921
|
+
else:
|
922
|
+
storage = self._storage_manager.get_file_storage(storage_key)
|
923
|
+
file_size = storage.size(storage_path)
|
924
|
+
|
925
|
+
data_item = f"file/{file_id.objectId}/version-{file_version}"
|
926
|
+
file_name = f"{file_key}.{file_type.extension}"
|
927
|
+
|
928
|
+
file_obj = self._generate_file_definition(
|
929
|
+
file_name, file_type, file_size,
|
930
|
+
storage_id, data_item)
|
931
|
+
|
932
|
+
storage_obj = self._generate_storage_definition(
|
933
|
+
storage_id, storage_key, storage_path, storage_format,
|
934
|
+
data_item, incarnation_index=1)
|
935
|
+
|
936
|
+
resources[_util.object_key(file_id)] = file_obj
|
937
|
+
resources[_util.object_key(storage_id)] = storage_obj
|
938
|
+
|
939
|
+
return file_id
|
940
|
+
|
890
941
|
@staticmethod
|
891
942
|
def infer_format(storage_path: str, storage_config: _cfg.StorageConfig):
|
892
943
|
|
@@ -898,20 +949,33 @@ class DevModeTranslator:
|
|
898
949
|
else:
|
899
950
|
return storage_config.defaultFormat
|
900
951
|
|
901
|
-
|
902
|
-
def _generate_input_definition(
|
903
|
-
cls, data_id: _meta.TagHeader, storage_id: _meta.TagHeader,
|
904
|
-
storage_key: str, storage_path: str, storage_format: str,
|
905
|
-
snap_index: int, delta_index: int, incarnation_index: int,
|
906
|
-
schema: tp.Optional[_meta.SchemaDefinition] = None) \
|
907
|
-
-> (_meta.ObjectDefinition, _meta.ObjectDefinition):
|
952
|
+
def _new_unique_file(self, socket_name, storage_key, storage_path, version):
|
908
953
|
|
909
|
-
|
910
|
-
|
911
|
-
|
954
|
+
x_storage = self._storage_manager.get_file_storage(storage_key)
|
955
|
+
x_orig_path = pathlib.PurePath(storage_path)
|
956
|
+
x_name = x_orig_path.name
|
912
957
|
|
913
|
-
|
914
|
-
|
958
|
+
if x_storage.exists(str(x_orig_path.parent)):
|
959
|
+
listing = x_storage.ls(str(x_orig_path.parent))
|
960
|
+
existing_files = list(map(lambda stat: stat.file_name, listing))
|
961
|
+
else:
|
962
|
+
existing_files = []
|
963
|
+
|
964
|
+
while x_name in existing_files:
|
965
|
+
|
966
|
+
version += 1
|
967
|
+
x_name = f"{x_orig_path.stem}-{version}{x_orig_path.suffix}"
|
968
|
+
storage_path = str(x_orig_path.parent.joinpath(x_name))
|
969
|
+
|
970
|
+
self._log.info(f"Output for [{socket_name}] will be version {version}")
|
971
|
+
|
972
|
+
return storage_path, version
|
973
|
+
|
974
|
+
@classmethod
|
975
|
+
def _generate_data_definition(
|
976
|
+
cls, part_key: _meta.PartKey, snap_index: int, delta_index: int, data_item: str,
|
977
|
+
schema: tp.Optional[_meta.SchemaDefinition], storage_id: _meta.TagHeader) \
|
978
|
+
-> (_meta.ObjectDefinition, _meta.ObjectDefinition):
|
915
979
|
|
916
980
|
delta = _meta.DataDefinition.Delta(
|
917
981
|
deltaIndex=delta_index,
|
@@ -925,17 +989,31 @@ class DevModeTranslator:
|
|
925
989
|
partKey=part_key,
|
926
990
|
snap=snap)
|
927
991
|
|
928
|
-
data_def = _meta.DataDefinition(
|
992
|
+
data_def = _meta.DataDefinition()
|
929
993
|
data_def.parts[part_key.opaqueKey] = part
|
994
|
+
data_def.schema = schema
|
995
|
+
data_def.storageId = _util.selector_for(storage_id)
|
930
996
|
|
931
|
-
|
932
|
-
data_def.schema = schema
|
933
|
-
else:
|
934
|
-
data_def.schema = None
|
997
|
+
return _meta.ObjectDefinition(objectType=_meta.ObjectType.DATA, data=data_def)
|
935
998
|
|
936
|
-
|
937
|
-
|
938
|
-
|
999
|
+
@classmethod
|
1000
|
+
def _generate_file_definition(
|
1001
|
+
cls, file_name: str, file_type: _meta.FileType, file_size: int,
|
1002
|
+
storage_id: _meta.TagHeader, data_item: str) \
|
1003
|
+
-> _meta.ObjectDefinition:
|
1004
|
+
|
1005
|
+
file_def = _meta.FileDefinition(
|
1006
|
+
name=file_name, extension=file_type.extension, mimeType=file_type.mimeType,
|
1007
|
+
storageId=_util.selector_for(storage_id), dataItem=data_item, size=file_size)
|
1008
|
+
|
1009
|
+
return _meta.ObjectDefinition(objectType=_meta.ObjectType.FILE, file=file_def)
|
1010
|
+
|
1011
|
+
@classmethod
|
1012
|
+
def _generate_storage_definition(
|
1013
|
+
cls, storage_id: _meta.TagHeader,
|
1014
|
+
storage_key: str, storage_path: str, storage_format: str,
|
1015
|
+
data_item: str, incarnation_index: int) \
|
1016
|
+
-> _meta.ObjectDefinition:
|
939
1017
|
|
940
1018
|
storage_copy = _meta.StorageCopy(
|
941
1019
|
storageKey=storage_key,
|
@@ -952,16 +1030,14 @@ class DevModeTranslator:
|
|
952
1030
|
storage_item = _meta.StorageItem(
|
953
1031
|
incarnations=[storage_incarnation])
|
954
1032
|
|
955
|
-
storage_def = _meta.StorageDefinition(
|
956
|
-
storage_def.dataItems[
|
1033
|
+
storage_def = _meta.StorageDefinition()
|
1034
|
+
storage_def.dataItems[data_item] = storage_item
|
957
1035
|
|
958
1036
|
if storage_format.lower() == "csv":
|
959
1037
|
storage_def.storageOptions["lenient_csv_parser"] = _types.MetadataCodec.encode_value(True)
|
960
1038
|
|
961
|
-
|
962
|
-
storage_obj = _meta.ObjectDefinition(objectType=_meta.ObjectType.STORAGE, storage=storage_def)
|
1039
|
+
return _meta.ObjectDefinition(objectType=_meta.ObjectType.STORAGE, storage=storage_def)
|
963
1040
|
|
964
|
-
return data_obj, storage_obj
|
965
1041
|
|
966
1042
|
|
967
|
-
DevModeTranslator._log =
|
1043
|
+
DevModeTranslator._log = _logging.logger_for_class(DevModeTranslator)
|