tracdap-runtime 0.8.0rc2__py3-none-any.whl → 0.9.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/_impl/core/config_parser.py +29 -3
- tracdap/rt/_impl/core/data.py +627 -40
- tracdap/rt/_impl/core/repos.py +17 -8
- tracdap/rt/_impl/core/storage.py +25 -13
- tracdap/rt/_impl/core/struct.py +254 -60
- tracdap/rt/_impl/core/util.py +125 -11
- tracdap/rt/_impl/exec/context.py +35 -8
- tracdap/rt/_impl/exec/dev_mode.py +169 -127
- tracdap/rt/_impl/exec/engine.py +203 -140
- tracdap/rt/_impl/exec/functions.py +228 -263
- tracdap/rt/_impl/exec/graph.py +141 -126
- tracdap/rt/_impl/exec/graph_builder.py +428 -449
- tracdap/rt/_impl/grpc/codec.py +8 -13
- tracdap/rt/_impl/grpc/server.py +7 -7
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +25 -18
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +27 -9
- tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +37 -35
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +37 -43
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +67 -63
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +11 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.py +11 -9
- tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.pyi +11 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +23 -19
- tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +15 -2
- tracdap/rt/_impl/runtime.py +3 -9
- tracdap/rt/_impl/static_api.py +5 -6
- tracdap/rt/_plugins/format_csv.py +2 -2
- tracdap/rt/_plugins/repo_git.py +56 -11
- tracdap/rt/_plugins/storage_aws.py +165 -150
- tracdap/rt/_plugins/storage_azure.py +17 -11
- tracdap/rt/_plugins/storage_gcp.py +35 -18
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/model_api.py +45 -0
- tracdap/rt/config/__init__.py +7 -9
- tracdap/rt/config/common.py +3 -14
- tracdap/rt/config/job.py +17 -3
- tracdap/rt/config/platform.py +9 -32
- tracdap/rt/config/result.py +8 -4
- tracdap/rt/config/runtime.py +5 -10
- tracdap/rt/config/tenant.py +28 -0
- tracdap/rt/launch/cli.py +0 -8
- tracdap/rt/launch/launch.py +1 -3
- tracdap/rt/metadata/__init__.py +35 -35
- tracdap/rt/metadata/data.py +19 -31
- tracdap/rt/metadata/job.py +3 -1
- tracdap/rt/metadata/storage.py +9 -0
- tracdap/rt/metadata/type.py +9 -5
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/METADATA +5 -3
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/RECORD +64 -63
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/WHEEL +1 -1
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/licenses/LICENSE +0 -0
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/top_level.txt +0 -0
tracdap/rt/_impl/core/util.py
CHANGED
@@ -16,10 +16,12 @@
|
|
16
16
|
import datetime as dt
|
17
17
|
import pathlib
|
18
18
|
import platform
|
19
|
+
import re
|
19
20
|
|
20
21
|
import typing as tp
|
21
22
|
import uuid
|
22
23
|
|
24
|
+
import tracdap.rt.api as api
|
23
25
|
import tracdap.rt.exceptions as ex
|
24
26
|
import tracdap.rt.metadata as meta
|
25
27
|
import tracdap.rt.config as cfg
|
@@ -30,6 +32,7 @@ import traceback as tb
|
|
30
32
|
__IS_WINDOWS = platform.system() == "Windows"
|
31
33
|
__FIRST_MODEL_FRAME_NAME = "run_model"
|
32
34
|
__FIRST_MODEL_FRAME_TEST_NAME = "_callTestMethod"
|
35
|
+
__OBJ_KEY_PATTERN = re.compile(r"([A-Z]+)-(.*)-v(\d+)")
|
33
36
|
|
34
37
|
|
35
38
|
def is_windows():
|
@@ -60,7 +63,7 @@ def format_file_size(size: int) -> str:
|
|
60
63
|
|
61
64
|
def new_object_id(object_type: meta.ObjectType) -> meta.TagHeader:
|
62
65
|
|
63
|
-
timestamp = dt.datetime.
|
66
|
+
timestamp = dt.datetime.now(dt.timezone.utc)
|
64
67
|
|
65
68
|
return meta.TagHeader(
|
66
69
|
objectType=object_type,
|
@@ -71,6 +74,19 @@ def new_object_id(object_type: meta.ObjectType) -> meta.TagHeader:
|
|
71
74
|
tagTimestamp=meta.DatetimeValue(timestamp.isoformat()))
|
72
75
|
|
73
76
|
|
77
|
+
def new_object_version(prior_id: meta.TagHeader) -> meta.TagHeader:
|
78
|
+
|
79
|
+
timestamp = dt.datetime.now(dt.timezone.utc)
|
80
|
+
|
81
|
+
return meta.TagHeader(
|
82
|
+
objectType=prior_id.objectType,
|
83
|
+
objectId=prior_id.objectId,
|
84
|
+
objectVersion=prior_id.objectVersion + 1,
|
85
|
+
objectTimestamp=meta.DatetimeValue(timestamp.isoformat()),
|
86
|
+
tagVersion=1,
|
87
|
+
tagTimestamp=meta.DatetimeValue(timestamp.isoformat()))
|
88
|
+
|
89
|
+
|
74
90
|
def object_key(object_id: tp.Union[meta.TagHeader, meta.TagSelector]) -> str:
|
75
91
|
|
76
92
|
if isinstance(object_id, meta.TagHeader):
|
@@ -106,29 +122,105 @@ def selector_for_latest(object_id: meta.TagHeader) -> meta.TagSelector:
|
|
106
122
|
latestTag=True)
|
107
123
|
|
108
124
|
|
109
|
-
def
|
125
|
+
def get_job_mapping(
|
126
|
+
selector: tp.Union[meta.TagHeader, meta.TagSelector],
|
127
|
+
job_config: cfg.JobConfig) \
|
128
|
+
-> meta.TagHeader:
|
129
|
+
|
130
|
+
obj_key = object_key(selector)
|
131
|
+
obj_id = job_config.objectMapping.get(obj_key)
|
132
|
+
|
133
|
+
if obj_id is not None:
|
134
|
+
return obj_id
|
135
|
+
|
136
|
+
obj_key_match = __OBJ_KEY_PATTERN.match(obj_key)
|
137
|
+
|
138
|
+
if not obj_key_match:
|
139
|
+
err = f"Missing required {selector.objectType.name} ID for [{object_key(selector)}]"
|
140
|
+
raise ex.ERuntimeValidation(err)
|
141
|
+
|
142
|
+
obj_type = obj_key_match.group(1)
|
143
|
+
obj_id = obj_key_match.group(2)
|
144
|
+
obj_ver = obj_key_match.group(3)
|
145
|
+
obj_ts = job_config.jobId.objectTimestamp
|
146
|
+
|
147
|
+
return meta.TagHeader(
|
148
|
+
meta.ObjectType.__members__[obj_type], obj_id,
|
149
|
+
int(obj_ver), obj_ts, 1, obj_ts)
|
150
|
+
|
151
|
+
|
152
|
+
def get_job_metadata(
|
110
153
|
selector: tp.Union[meta.TagHeader, meta.TagSelector],
|
111
154
|
job_config: cfg.JobConfig,
|
112
|
-
optional: bool = False)
|
155
|
+
optional: bool = False) \
|
156
|
+
-> tp.Optional[meta.ObjectDefinition]:
|
157
|
+
|
158
|
+
return __get_job_metadata_item(selector, job_config, job_config.objects, "object", optional)
|
159
|
+
|
160
|
+
|
161
|
+
def get_job_metadata_tag(
|
162
|
+
selector: tp.Union[meta.TagHeader, meta.TagSelector],
|
163
|
+
job_config: cfg.JobConfig,
|
164
|
+
optional: bool = False) \
|
165
|
+
-> tp.Optional[meta.Tag]:
|
166
|
+
|
167
|
+
return __get_job_metadata_item(selector, job_config, job_config.tags, "tag", optional)
|
113
168
|
|
114
|
-
resource_key = object_key(selector)
|
115
|
-
resource_id = job_config.resourceMapping.get(resource_key)
|
116
169
|
|
117
|
-
|
118
|
-
resource_key = object_key(resource_id)
|
170
|
+
__METADATA_TYPE = tp.TypeVar("__METADATA_TYPE")
|
119
171
|
|
120
|
-
resource = job_config.resources.get(resource_key)
|
121
172
|
|
122
|
-
|
123
|
-
|
173
|
+
def __get_job_metadata_item(
|
174
|
+
selector: tp.Union[meta.TagHeader, meta.TagSelector], job_config: cfg.JobConfig,
|
175
|
+
metadata: tp.Dict[str, __METADATA_TYPE], metadata_type: str,
|
176
|
+
optional: bool = False) \
|
177
|
+
-> tp.Optional[__METADATA_TYPE]:
|
178
|
+
|
179
|
+
obj_key = object_key(selector)
|
180
|
+
obj_id = job_config.objectMapping.get(obj_key)
|
181
|
+
|
182
|
+
if obj_id is not None:
|
183
|
+
obj_key = object_key(obj_id)
|
184
|
+
|
185
|
+
item = metadata.get(obj_key)
|
186
|
+
|
187
|
+
if item is not None:
|
188
|
+
return item
|
124
189
|
|
125
190
|
if optional:
|
126
191
|
return None
|
127
192
|
|
128
|
-
err = f"Missing required {selector.objectType.name}
|
193
|
+
err = f"Missing required {selector.objectType.name} {metadata_type} for [{object_key(selector)}]"
|
129
194
|
raise ex.ERuntimeValidation(err)
|
130
195
|
|
131
196
|
|
197
|
+
def attach_runtime_metadata(obj: tp.Any, metadata: api.RuntimeMetadata):
|
198
|
+
|
199
|
+
if hasattr(obj, "with_metadata"):
|
200
|
+
attach_func = getattr(obj, "with_metadata")
|
201
|
+
if isinstance(attach_func, tp.Callable):
|
202
|
+
return attach_func(metadata)
|
203
|
+
|
204
|
+
setattr(obj, "_metadata", metadata)
|
205
|
+
|
206
|
+
return obj
|
207
|
+
|
208
|
+
|
209
|
+
def retrieve_runtime_metadata(obj: tp.Any) -> tp.Optional[api.RuntimeMetadata]:
|
210
|
+
|
211
|
+
if hasattr(obj, "metadata"):
|
212
|
+
metadata = getattr(obj, "metadata")
|
213
|
+
if isinstance(metadata, api.RuntimeMetadata):
|
214
|
+
return metadata
|
215
|
+
|
216
|
+
if hasattr(obj, "_metadata"):
|
217
|
+
metadata = getattr(obj, "_metadata")
|
218
|
+
if isinstance(metadata, api.RuntimeMetadata):
|
219
|
+
return metadata
|
220
|
+
|
221
|
+
return None
|
222
|
+
|
223
|
+
|
132
224
|
def get_origin(metaclass: type):
|
133
225
|
|
134
226
|
# Minimum supported Python is 3.7, which does not provide get_origin and get_args
|
@@ -253,3 +345,25 @@ def filter_model_stack_trace(full_stack: tb.StackSummary, checkout_directory: pa
|
|
253
345
|
last_model_frame = first_model_frame + frame_index
|
254
346
|
|
255
347
|
return full_stack[first_model_frame:last_model_frame+1]
|
348
|
+
|
349
|
+
|
350
|
+
__T = tp.TypeVar("__T")
|
351
|
+
|
352
|
+
def read_property(properties: tp.Dict[str, str], key: str, default: tp.Optional[__T] = None, convert: tp.Optional[tp.Type[__T]] = str) -> __T:
|
353
|
+
|
354
|
+
value = properties.get(key)
|
355
|
+
|
356
|
+
if value is None:
|
357
|
+
if default is not None:
|
358
|
+
value = default
|
359
|
+
else:
|
360
|
+
raise ex.EConfigParse(f"Missing required property: [{key}]")
|
361
|
+
|
362
|
+
try:
|
363
|
+
if convert is bool and isinstance(value, str):
|
364
|
+
return True if value.lower() == "true" else False
|
365
|
+
else:
|
366
|
+
return convert(value)
|
367
|
+
|
368
|
+
except (ValueError, TypeError):
|
369
|
+
raise ex.EConfigParse(f"Wrong property type: [{key}] = [{value}], expected type is [{convert}]")
|
tracdap/rt/_impl/exec/context.py
CHANGED
@@ -118,6 +118,19 @@ class TracContextImpl(_api.TracContext):
|
|
118
118
|
|
119
119
|
return not data_view.is_empty()
|
120
120
|
|
121
|
+
def get_metadata(self, item_name: str) -> tp.Optional[_api.RuntimeMetadata]:
|
122
|
+
|
123
|
+
_val.validate_signature(self.get_metadata, item_name)
|
124
|
+
|
125
|
+
self.__val.check_item_valid_identifier(item_name, TracContextValidator.ITEM)
|
126
|
+
self.__val.check_item_defined_in_model(item_name, TracContextValidator.ITEM)
|
127
|
+
self.__val.check_item_available_in_context(item_name, TracContextValidator.ITEM)
|
128
|
+
|
129
|
+
obj = self.__local_ctx.get(item_name)
|
130
|
+
|
131
|
+
# Can be none if no metadata is attached
|
132
|
+
return _util.retrieve_runtime_metadata(obj)
|
133
|
+
|
121
134
|
def get_schema(self, dataset_name: str) -> _meta.SchemaDefinition:
|
122
135
|
|
123
136
|
_val.validate_signature(self.get_schema, dataset_name)
|
@@ -201,8 +214,12 @@ class TracContextImpl(_api.TracContext):
|
|
201
214
|
self.__val.check_context_data_view_type(struct_name, data_view, _meta.ObjectType.DATA)
|
202
215
|
self.__val.check_dataset_schema_defined(struct_name, data_view)
|
203
216
|
|
204
|
-
struct_data
|
205
|
-
|
217
|
+
struct_data = data_view.parts[part_key][0].content
|
218
|
+
|
219
|
+
if isinstance(struct_data, python_class):
|
220
|
+
return struct_data
|
221
|
+
else:
|
222
|
+
return _struct.StructProcessor.parse_struct(struct_data, None, python_class)
|
206
223
|
|
207
224
|
def get_file(self, file_name: str) -> bytes:
|
208
225
|
|
@@ -371,16 +388,25 @@ class TracContextImpl(_api.TracContext):
|
|
371
388
|
self.__val.check_item_valid_identifier(file_name, TracContextValidator.FILE)
|
372
389
|
self.__val.check_item_is_model_output(file_name, TracContextValidator.FILE)
|
373
390
|
|
391
|
+
class DelayedClose(io.BytesIO):
|
392
|
+
|
393
|
+
def __init__(self):
|
394
|
+
super().__init__()
|
395
|
+
|
396
|
+
def close(self):
|
397
|
+
super().flush()
|
398
|
+
|
374
399
|
@contextlib.contextmanager
|
375
400
|
def memory_stream(stream: io.BytesIO):
|
376
401
|
try:
|
377
402
|
yield stream
|
378
|
-
buffer = stream.getbuffer().tobytes()
|
379
|
-
self.put_file(file_name, buffer)
|
380
403
|
finally:
|
381
|
-
stream.
|
404
|
+
with stream.getbuffer() as buffer:
|
405
|
+
self.put_file(file_name, bytes(buffer))
|
406
|
+
if not stream.closed:
|
407
|
+
io.BytesIO.close(stream)
|
382
408
|
|
383
|
-
return memory_stream(
|
409
|
+
return memory_stream(DelayedClose())
|
384
410
|
|
385
411
|
def log(self) -> logging.Logger:
|
386
412
|
|
@@ -816,6 +842,7 @@ class TracContextErrorReporter:
|
|
816
842
|
|
817
843
|
class TracContextValidator(TracContextErrorReporter):
|
818
844
|
|
845
|
+
ITEM = "Item"
|
819
846
|
PARAMETER = "Parameter"
|
820
847
|
DATASET = "Dataset"
|
821
848
|
FILE = "File"
|
@@ -891,10 +918,10 @@ class TracContextValidator(TracContextErrorReporter):
|
|
891
918
|
if schema is None:
|
892
919
|
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
893
920
|
|
894
|
-
if schema.schemaType == _meta.SchemaType.
|
921
|
+
if schema.schemaType == _meta.SchemaType.TABLE_SCHEMA and (schema.table is None or not schema.table.fields):
|
895
922
|
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
896
923
|
|
897
|
-
if schema.schemaType == _meta.SchemaType.
|
924
|
+
if schema.schemaType == _meta.SchemaType.STRUCT_SCHEMA and not schema.fields:
|
898
925
|
self._report_error(f"Schema not defined for dataset {dataset_name} in the current context")
|
899
926
|
|
900
927
|
def check_dataset_schema_not_defined(self, dataset_name: str, data_view: _data.DataView):
|