tracdap-runtime 0.6.5__py3-none-any.whl → 0.6.6__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/context.py +272 -105
- tracdap/rt/_exec/dev_mode.py +231 -138
- tracdap/rt/_exec/engine.py +217 -59
- tracdap/rt/_exec/functions.py +25 -1
- tracdap/rt/_exec/graph.py +9 -0
- tracdap/rt/_exec/graph_builder.py +295 -198
- tracdap/rt/_exec/runtime.py +7 -5
- tracdap/rt/_impl/config_parser.py +11 -4
- tracdap/rt/_impl/data.py +278 -167
- tracdap/rt/_impl/ext/__init__.py +13 -0
- tracdap/rt/_impl/ext/sql.py +116 -0
- tracdap/rt/_impl/ext/storage.py +57 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +62 -54
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +37 -2
- tracdap/rt/_impl/static_api.py +24 -11
- tracdap/rt/_impl/storage.py +2 -2
- tracdap/rt/_impl/util.py +10 -0
- tracdap/rt/_impl/validation.py +66 -13
- tracdap/rt/_plugins/storage_sql.py +417 -0
- tracdap/rt/_plugins/storage_sql_dialects.py +117 -0
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/experimental.py +79 -32
- tracdap/rt/api/hook.py +10 -0
- tracdap/rt/metadata/__init__.py +4 -0
- tracdap/rt/metadata/job.py +45 -0
- {tracdap_runtime-0.6.5.dist-info → tracdap_runtime-0.6.6.dist-info}/METADATA +3 -1
- {tracdap_runtime-0.6.5.dist-info → tracdap_runtime-0.6.6.dist-info}/RECORD +30 -25
- {tracdap_runtime-0.6.5.dist-info → tracdap_runtime-0.6.6.dist-info}/WHEEL +1 -1
- {tracdap_runtime-0.6.5.dist-info → tracdap_runtime-0.6.6.dist-info}/LICENSE +0 -0
- {tracdap_runtime-0.6.5.dist-info → tracdap_runtime-0.6.6.dist-info}/top_level.txt +0 -0
tracdap/rt/_exec/context.py
CHANGED
@@ -55,8 +55,6 @@ class TracContextImpl(_api.TracContext):
|
|
55
55
|
Output views will contain schemas but no data.
|
56
56
|
"""
|
57
57
|
|
58
|
-
__DEFAULT_TEMPORAL_OBJECTS = False
|
59
|
-
|
60
58
|
def __init__(self,
|
61
59
|
model_def: _meta.ModelDefinition,
|
62
60
|
model_class: _api.TracModel.__class__,
|
@@ -134,53 +132,22 @@ class TracContextImpl(_api.TracContext):
|
|
134
132
|
else:
|
135
133
|
return copy.deepcopy(data_view.trac_schema)
|
136
134
|
|
137
|
-
def get_table(self, dataset_name: str, framework, **
|
138
|
-
|
139
|
-
# Support the experimental API data framework syntax
|
140
|
-
|
141
|
-
if framework == _eapi.PANDAS:
|
142
|
-
return self.get_pandas_table(dataset_name, **kwargs)
|
143
|
-
elif framework == _eapi.POLARS:
|
144
|
-
return self.get_polars_table(dataset_name)
|
145
|
-
else:
|
146
|
-
raise _ex.ERuntimeValidation(f"Unsupported data framework [{framework}]")
|
147
|
-
|
148
|
-
def get_pandas_table(self, dataset_name: str, use_temporal_objects: tp.Optional[bool] = None) \
|
149
|
-
-> "_data.pandas.DataFrame":
|
150
|
-
|
151
|
-
_val.require_package("pandas", _data.pandas)
|
152
|
-
_val.validate_signature(self.get_pandas_table, dataset_name, use_temporal_objects)
|
135
|
+
def get_table(self, dataset_name: str, framework: _eapi.DataFramework[_eapi.DATA_API], **framework_args) -> _eapi.DATA_API:
|
153
136
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
if use_temporal_objects is None:
|
158
|
-
use_temporal_objects = self.__DEFAULT_TEMPORAL_OBJECTS
|
159
|
-
|
160
|
-
return _data.DataMapping.view_to_pandas(data_view, part_key, schema, use_temporal_objects)
|
161
|
-
|
162
|
-
def get_polars_table(self, dataset_name: str) -> "_data.polars.DataFrame":
|
163
|
-
|
164
|
-
_val.require_package("polars", _data.polars)
|
165
|
-
_val.validate_signature(self.get_polars_table, dataset_name)
|
166
|
-
|
167
|
-
data_view, schema = self.__get_data_view(dataset_name)
|
168
|
-
part_key = _data.DataPartKey.for_root()
|
169
|
-
|
170
|
-
return _data.DataMapping.view_to_polars(data_view, part_key, schema)
|
171
|
-
|
172
|
-
def __get_data_view(self, dataset_name: str):
|
173
|
-
|
174
|
-
_val.validate_signature(self.__get_data_view, dataset_name)
|
137
|
+
_val.validate_signature(self.get_table, dataset_name, framework)
|
138
|
+
_val.require_package(framework.protocol_name, framework.api_type)
|
175
139
|
|
176
140
|
self.__val.check_dataset_valid_identifier(dataset_name)
|
177
141
|
self.__val.check_dataset_defined_in_model(dataset_name)
|
178
142
|
self.__val.check_dataset_available_in_context(dataset_name)
|
143
|
+
self.__val.check_data_framework_args(framework, framework_args)
|
179
144
|
|
180
145
|
static_schema = self.__get_static_schema(self.__model_def, dataset_name)
|
181
146
|
data_view = self.__local_ctx.get(dataset_name)
|
182
147
|
part_key = _data.DataPartKey.for_root()
|
183
148
|
|
149
|
+
converter = _data.DataConverter.for_framework(framework, **framework_args)
|
150
|
+
|
184
151
|
self.__val.check_context_object_type(dataset_name, data_view, _data.DataView)
|
185
152
|
self.__val.check_dataset_schema_defined(dataset_name, data_view)
|
186
153
|
self.__val.check_dataset_part_present(dataset_name, data_view, part_key)
|
@@ -193,7 +160,18 @@ class TracContextImpl(_api.TracContext):
|
|
193
160
|
else:
|
194
161
|
schema = data_view.arrow_schema
|
195
162
|
|
196
|
-
|
163
|
+
table = _data.DataMapping.view_to_arrow(data_view, part_key)
|
164
|
+
|
165
|
+
# Data conformance is applied automatically inside the converter, if schema != None
|
166
|
+
return converter.from_internal(table, schema)
|
167
|
+
|
168
|
+
def get_pandas_table(self, dataset_name: str, use_temporal_objects: tp.Optional[bool] = None) -> "_data.pandas.DataFrame":
|
169
|
+
|
170
|
+
return self.get_table(dataset_name, _eapi.PANDAS, use_temporal_objects=use_temporal_objects)
|
171
|
+
|
172
|
+
def get_polars_table(self, dataset_name: str) -> "_data.polars.DataFrame":
|
173
|
+
|
174
|
+
return self.get_table(dataset_name, _eapi.POLARS)
|
197
175
|
|
198
176
|
def put_schema(self, dataset_name: str, schema: _meta.SchemaDefinition):
|
199
177
|
|
@@ -225,57 +203,28 @@ class TracContextImpl(_api.TracContext):
|
|
225
203
|
|
226
204
|
self.__local_ctx[dataset_name] = updated_view
|
227
205
|
|
228
|
-
def put_table(
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
if _data.pandas and isinstance(dataset, _data.pandas.DataFrame):
|
233
|
-
self.put_pandas_table(dataset_name, dataset)
|
234
|
-
elif _data.polars and isinstance(dataset, _data.polars.DataFrame):
|
235
|
-
self.put_polars_table(dataset_name, dataset)
|
236
|
-
else:
|
237
|
-
raise _ex.ERuntimeValidation(f"Unsupported data framework[{type(dataset)}]")
|
238
|
-
|
239
|
-
def put_pandas_table(self, dataset_name: str, dataset: "_data.pandas.DataFrame"):
|
240
|
-
|
241
|
-
_val.require_package("pandas", _data.pandas)
|
242
|
-
_val.validate_signature(self.put_pandas_table, dataset_name, dataset)
|
243
|
-
|
244
|
-
part_key = _data.DataPartKey.for_root()
|
245
|
-
data_view, schema = self.__put_data_view(dataset_name, part_key, dataset, _data.pandas.DataFrame)
|
206
|
+
def put_table(
|
207
|
+
self, dataset_name: str, dataset: _eapi.DATA_API,
|
208
|
+
framework: tp.Optional[_eapi.DataFramework[_eapi.DATA_API]] = None,
|
209
|
+
**framework_args):
|
246
210
|
|
247
|
-
|
211
|
+
_val.validate_signature(self.put_table, dataset_name, dataset, framework)
|
248
212
|
|
249
|
-
|
250
|
-
|
213
|
+
if framework is None:
|
214
|
+
framework = _data.DataConverter.get_framework(dataset)
|
251
215
|
|
252
|
-
|
253
|
-
|
254
|
-
def put_polars_table(self, dataset_name: str, dataset: "_data.polars.DataFrame"):
|
255
|
-
|
256
|
-
_val.require_package("polars", _data.polars)
|
257
|
-
_val.validate_signature(self.put_polars_table, dataset_name, dataset)
|
258
|
-
|
259
|
-
part_key = _data.DataPartKey.for_root()
|
260
|
-
data_view, schema = self.__put_data_view(dataset_name, part_key, dataset, _data.polars.DataFrame)
|
261
|
-
|
262
|
-
# Data conformance is applied inside these conversion functions
|
263
|
-
|
264
|
-
updated_item = _data.DataMapping.polars_to_item(dataset, schema)
|
265
|
-
updated_view = _data.DataMapping.add_item_to_view(data_view, part_key, updated_item)
|
266
|
-
|
267
|
-
self.__local_ctx[dataset_name] = updated_view
|
268
|
-
|
269
|
-
def __put_data_view(self, dataset_name: str, part_key: _data.DataPartKey, dataset: tp.Any, framework: type):
|
270
|
-
|
271
|
-
_val.validate_signature(self.__put_data_view, dataset_name, part_key, dataset, framework)
|
216
|
+
_val.require_package(framework.protocol_name, framework.api_type)
|
272
217
|
|
273
218
|
self.__val.check_dataset_valid_identifier(dataset_name)
|
274
219
|
self.__val.check_dataset_is_model_output(dataset_name)
|
275
|
-
self.__val.check_provided_dataset_type(dataset, framework)
|
220
|
+
self.__val.check_provided_dataset_type(dataset, framework.api_type)
|
221
|
+
self.__val.check_data_framework_args(framework, framework_args)
|
276
222
|
|
277
223
|
static_schema = self.__get_static_schema(self.__model_def, dataset_name)
|
278
224
|
data_view = self.__local_ctx.get(dataset_name)
|
225
|
+
part_key = _data.DataPartKey.for_root()
|
226
|
+
|
227
|
+
converter = _data.DataConverter.for_framework(framework)
|
279
228
|
|
280
229
|
if data_view is None:
|
281
230
|
if static_schema is not None:
|
@@ -294,7 +243,21 @@ class TracContextImpl(_api.TracContext):
|
|
294
243
|
else:
|
295
244
|
schema = data_view.arrow_schema
|
296
245
|
|
297
|
-
|
246
|
+
# Data conformance is applied automatically inside the converter, if schema != None
|
247
|
+
table = converter.to_internal(dataset, schema)
|
248
|
+
item = _data.DataItem(schema, table)
|
249
|
+
|
250
|
+
updated_view = _data.DataMapping.add_item_to_view(data_view, part_key, item)
|
251
|
+
|
252
|
+
self.__local_ctx[dataset_name] = updated_view
|
253
|
+
|
254
|
+
def put_pandas_table(self, dataset_name: str, dataset: "_data.pandas.DataFrame"):
|
255
|
+
|
256
|
+
self.put_table(dataset_name, dataset, _eapi.PANDAS)
|
257
|
+
|
258
|
+
def put_polars_table(self, dataset_name: str, dataset: "_data.polars.DataFrame"):
|
259
|
+
|
260
|
+
self.put_table(dataset_name, dataset, _eapi.POLARS)
|
298
261
|
|
299
262
|
def log(self) -> logging.Logger:
|
300
263
|
|
@@ -335,7 +298,7 @@ class TracDataContextImpl(TracContextImpl, _eapi.TracDataContext):
|
|
335
298
|
def __init__(
|
336
299
|
self, model_def: _meta.ModelDefinition, model_class: _api.TracModel.__class__,
|
337
300
|
local_ctx: tp.Dict[str, tp.Any], dynamic_outputs: tp.List[str],
|
338
|
-
storage_map: tp.Dict[str, tp.Union[_eapi.TracFileStorage]],
|
301
|
+
storage_map: tp.Dict[str, tp.Union[_eapi.TracFileStorage, _eapi.TracDataStorage]],
|
339
302
|
checkout_directory: pathlib.Path = None):
|
340
303
|
|
341
304
|
super().__init__(model_def, model_class, local_ctx, dynamic_outputs, checkout_directory)
|
@@ -358,8 +321,27 @@ class TracDataContextImpl(TracContextImpl, _eapi.TracDataContext):
|
|
358
321
|
|
359
322
|
return self.__storage_map[storage_key]
|
360
323
|
|
361
|
-
def get_data_storage(
|
362
|
-
|
324
|
+
def get_data_storage(
|
325
|
+
self, storage_key: str,
|
326
|
+
framework: _eapi.DataFramework[_eapi.DATA_API],
|
327
|
+
**framework_args) -> _eapi.TracDataStorage[_eapi.DATA_API]:
|
328
|
+
|
329
|
+
_val.validate_signature(self.get_file_storage, storage_key)
|
330
|
+
|
331
|
+
self.__val.check_storage_valid_identifier(storage_key)
|
332
|
+
self.__val.check_storage_available(self.__storage_map, storage_key)
|
333
|
+
self.__val.check_storage_type(self.__storage_map, storage_key, _eapi.TracDataStorage)
|
334
|
+
self.__val.check_data_framework_args(framework, framework_args)
|
335
|
+
|
336
|
+
storage = self.__storage_map[storage_key]
|
337
|
+
converter = _data.DataConverter.for_framework(framework, **framework_args)
|
338
|
+
|
339
|
+
# Create a shallow copy of the storage impl with a converter for the requested data framework
|
340
|
+
# At some point we will need a storage factory class, bc the internal data API can also be different
|
341
|
+
storage = copy.copy(storage)
|
342
|
+
storage._TracDataStorageImpl__converter = converter
|
343
|
+
|
344
|
+
return storage
|
363
345
|
|
364
346
|
def add_data_import(self, dataset_name: str):
|
365
347
|
|
@@ -372,15 +354,30 @@ class TracDataContextImpl(TracContextImpl, _eapi.TracDataContext):
|
|
372
354
|
self.__local_ctx[dataset_name] = _data.DataView.create_empty()
|
373
355
|
self.__dynamic_outputs.append(dataset_name)
|
374
356
|
|
375
|
-
def set_source_metadata(self, dataset_name: str, storage_key: str, source_info: _eapi.FileStat):
|
357
|
+
def set_source_metadata(self, dataset_name: str, storage_key: str, source_info: tp.Union[_eapi.FileStat, str]):
|
376
358
|
|
377
|
-
_val.validate_signature(self.
|
359
|
+
_val.validate_signature(self.set_source_metadata, dataset_name, storage_key, source_info)
|
360
|
+
|
361
|
+
self.__val.check_dataset_valid_identifier(dataset_name)
|
362
|
+
self.__val.check_dataset_available_in_context(dataset_name)
|
363
|
+
self.__val.check_storage_valid_identifier(storage_key)
|
364
|
+
self.__val.check_storage_available(self.__storage_map, storage_key)
|
365
|
+
|
366
|
+
storage = self.__storage_map[storage_key]
|
367
|
+
|
368
|
+
if isinstance(storage, _eapi.TracFileStorage):
|
369
|
+
if not isinstance(source_info, _eapi.FileStat):
|
370
|
+
self.__val.report_public_error(f"Expected storage_info to be a FileStat, [{storage_key}] refers to file storage")
|
371
|
+
|
372
|
+
if isinstance(storage, _eapi.TracDataStorage):
|
373
|
+
if not isinstance(source_info, str):
|
374
|
+
self.__val.report_public_error(f"Expected storage_info to be a table name, [{storage_key}] refers to dadta storage")
|
378
375
|
|
379
376
|
pass # Not implemented yet, only required when imports are sent back to the platform
|
380
377
|
|
381
378
|
def set_attribute(self, dataset_name: str, attribute_name: str, value: tp.Any):
|
382
379
|
|
383
|
-
_val.validate_signature(self.
|
380
|
+
_val.validate_signature(self.set_attribute, dataset_name, attribute_name, value)
|
384
381
|
|
385
382
|
pass # Not implemented yet, only required when imports are sent back to the platform
|
386
383
|
|
@@ -531,13 +528,132 @@ class TracFileStorageImpl(_eapi.TracFileStorage):
|
|
531
528
|
super().write_bytes(storage_path, data)
|
532
529
|
|
533
530
|
|
531
|
+
class TracDataStorageImpl(_eapi.TracDataStorage[_eapi.DATA_API]):
|
532
|
+
|
533
|
+
def __init__(
|
534
|
+
self, storage_key: str, storage_impl: _storage.IDataStorageBase[_data.T_INTERNAL_DATA, _data.T_INTERNAL_SCHEMA],
|
535
|
+
data_converter: _data.DataConverter[_eapi.DATA_API, _data.T_INTERNAL_DATA, _data.T_INTERNAL_SCHEMA],
|
536
|
+
write_access: bool, checkout_directory):
|
537
|
+
|
538
|
+
self.__storage_key = storage_key
|
539
|
+
self.__converter = data_converter
|
540
|
+
|
541
|
+
self.__has_table = lambda tn: storage_impl.has_table(tn)
|
542
|
+
self.__list_tables = lambda: storage_impl.list_tables()
|
543
|
+
self.__read_table = lambda tn: storage_impl.read_table(tn)
|
544
|
+
self.__native_read_query = lambda q, ps: storage_impl.native_read_query(q, **ps)
|
545
|
+
|
546
|
+
if write_access:
|
547
|
+
self.__create_table = lambda tn, s: storage_impl.create_table(tn, s)
|
548
|
+
self.__write_table = lambda tn, ds: storage_impl.write_table(tn, ds)
|
549
|
+
else:
|
550
|
+
self.__create_table = None
|
551
|
+
self.__write_table = None
|
552
|
+
|
553
|
+
self.__log = _util.logger_for_object(self)
|
554
|
+
self.__val = TracStorageValidator(self.__log, checkout_directory, self.__storage_key)
|
555
|
+
|
556
|
+
def has_table(self, table_name: str) -> bool:
|
557
|
+
|
558
|
+
_val.validate_signature(self.has_table, table_name)
|
559
|
+
|
560
|
+
self.__val.check_operation_available(self.has_table, self.__has_table)
|
561
|
+
self.__val.check_table_name_is_valid(table_name)
|
562
|
+
self.__val.check_storage_path_is_valid(table_name)
|
563
|
+
|
564
|
+
try:
|
565
|
+
return self.__has_table(table_name)
|
566
|
+
except _ex.EStorageRequest as e:
|
567
|
+
self.__val.report_public_error(e)
|
568
|
+
|
569
|
+
def list_tables(self) -> tp.List[str]:
|
570
|
+
|
571
|
+
_val.validate_signature(self.list_tables)
|
572
|
+
|
573
|
+
self.__val.check_operation_available(self.list_tables, self.__list_tables)
|
574
|
+
|
575
|
+
try:
|
576
|
+
return self.__list_tables()
|
577
|
+
except _ex.EStorageRequest as e:
|
578
|
+
self.__val.report_public_error(e)
|
579
|
+
|
580
|
+
def create_table(self, table_name: str, schema: _api.SchemaDefinition):
|
581
|
+
|
582
|
+
_val.validate_signature(self.create_table, table_name, schema)
|
583
|
+
|
584
|
+
self.__val.check_operation_available(self.create_table, self.__create_table)
|
585
|
+
self.__val.check_table_name_is_valid(table_name)
|
586
|
+
self.__val.check_storage_path_is_valid(table_name)
|
587
|
+
|
588
|
+
arrow_schema = _data.DataMapping.trac_to_arrow_schema(schema)
|
589
|
+
|
590
|
+
try:
|
591
|
+
self.__create_table(table_name, arrow_schema)
|
592
|
+
except _ex.EStorageRequest as e:
|
593
|
+
self.__val.report_public_error(e)
|
594
|
+
|
595
|
+
def read_table(self, table_name: str) -> _eapi.DATA_API:
|
596
|
+
|
597
|
+
_val.validate_signature(self.read_table, table_name)
|
598
|
+
|
599
|
+
self.__val.check_operation_available(self.read_table, self.__read_table)
|
600
|
+
self.__val.check_table_name_is_valid(table_name)
|
601
|
+
self.__val.check_table_name_not_reserved(table_name)
|
602
|
+
|
603
|
+
try:
|
604
|
+
raw_data = self.__read_table(table_name)
|
605
|
+
return self.__converter.from_internal(raw_data)
|
606
|
+
|
607
|
+
except _ex.EStorageRequest as e:
|
608
|
+
self.__val.report_public_error(e)
|
609
|
+
|
610
|
+
def native_read_query(self, query: str, **parameters) -> _eapi.DATA_API:
|
611
|
+
|
612
|
+
_val.validate_signature(self.native_read_query, query, **parameters)
|
613
|
+
|
614
|
+
self.__val.check_operation_available(self.native_read_query, self.__native_read_query)
|
615
|
+
|
616
|
+
# TODO: validate query and parameters
|
617
|
+
# Some validation is performed by the impl
|
618
|
+
|
619
|
+
try:
|
620
|
+
raw_data = self.__native_read_query(query, **parameters)
|
621
|
+
return self.__converter.from_internal(raw_data)
|
622
|
+
|
623
|
+
except _ex.EStorageRequest as e:
|
624
|
+
self.__val.report_public_error(e)
|
625
|
+
|
626
|
+
def write_table(self, table_name: str, dataset: _eapi.DATA_API):
|
627
|
+
|
628
|
+
_val.validate_signature(self.write_table, table_name, dataset)
|
629
|
+
|
630
|
+
self.__val.check_operation_available(self.read_table, self.__read_table)
|
631
|
+
self.__val.check_table_name_is_valid(table_name)
|
632
|
+
self.__val.check_table_name_not_reserved(table_name)
|
633
|
+
self.__val.check_provided_dataset_type(dataset, self.__converter.framework.api_type)
|
634
|
+
|
635
|
+
try:
|
636
|
+
raw_data = self.__converter.to_internal(dataset)
|
637
|
+
self.__write_table(table_name, raw_data)
|
638
|
+
|
639
|
+
except _ex.EStorageRequest as e:
|
640
|
+
self.__val.report_public_error(e)
|
641
|
+
|
642
|
+
|
534
643
|
class TracContextErrorReporter:
|
535
644
|
|
645
|
+
_VALID_IDENTIFIER = re.compile("^[a-zA-Z_]\\w*$",)
|
646
|
+
_RESERVED_IDENTIFIER = re.compile("^(trac_|_)\\w*")
|
647
|
+
|
536
648
|
def __init__(self, log: logging.Logger, checkout_directory: pathlib.Path):
|
537
649
|
|
538
650
|
self.__log = log
|
539
651
|
self.__checkout_directory = checkout_directory
|
540
652
|
|
653
|
+
def report_public_error(self, exception: Exception):
|
654
|
+
|
655
|
+
self._report_error(str(exception), exception)
|
656
|
+
|
541
657
|
def _report_error(self, message, cause: Exception = None):
|
542
658
|
|
543
659
|
full_stack = traceback.extract_stack()
|
@@ -554,11 +670,18 @@ class TracContextErrorReporter:
|
|
554
670
|
else:
|
555
671
|
raise _ex.ERuntimeValidation(message)
|
556
672
|
|
673
|
+
@staticmethod
|
674
|
+
def _type_name(type_: type):
|
557
675
|
|
558
|
-
|
676
|
+
module = type_.__module__
|
677
|
+
|
678
|
+
if module is None or module == str.__class__.__module__ or module == tp.__name__:
|
679
|
+
return _val.type_name(type_, False)
|
680
|
+
else:
|
681
|
+
return _val.type_name(type_, True)
|
559
682
|
|
560
|
-
|
561
|
-
|
683
|
+
|
684
|
+
class TracContextValidator(TracContextErrorReporter):
|
562
685
|
|
563
686
|
def __init__(
|
564
687
|
self, log: logging.Logger,
|
@@ -578,7 +701,7 @@ class TracContextValidator(TracContextErrorReporter):
|
|
578
701
|
if param_name is None:
|
579
702
|
self._report_error(f"Parameter name is null")
|
580
703
|
|
581
|
-
if not self.
|
704
|
+
if not self._VALID_IDENTIFIER.match(param_name):
|
582
705
|
self._report_error(f"Parameter name {param_name} is not a valid identifier")
|
583
706
|
|
584
707
|
def check_param_defined_in_model(self, param_name: str):
|
@@ -596,7 +719,7 @@ class TracContextValidator(TracContextErrorReporter):
|
|
596
719
|
if dataset_name is None:
|
597
720
|
self._report_error(f"Dataset name is null")
|
598
721
|
|
599
|
-
if not self.
|
722
|
+
if not self._VALID_IDENTIFIER.match(dataset_name):
|
600
723
|
self._report_error(f"Dataset name {dataset_name} is not a valid identifier")
|
601
724
|
|
602
725
|
def check_dataset_not_defined_in_model(self, dataset_name: str):
|
@@ -710,12 +833,39 @@ class TracContextValidator(TracContextErrorReporter):
|
|
710
833
|
f"The object referenced by [{item_name}] in the current context has the wrong type" +
|
711
834
|
f" (expected {expected_type_name}, got {actual_type_name})")
|
712
835
|
|
836
|
+
def check_data_framework_args(self, framework: _eapi.DataFramework, framework_args: tp.Dict[str, tp.Any]):
|
837
|
+
|
838
|
+
expected_args = _data.DataConverter.get_framework_args(framework)
|
839
|
+
unexpected_args = list(filter(lambda arg: arg not in expected_args, framework_args.keys()))
|
840
|
+
|
841
|
+
if any(unexpected_args):
|
842
|
+
unknown_args = ", ".join(unexpected_args)
|
843
|
+
self._report_error(f"Using [{framework}], some arguments were not recognized: [{unknown_args}]")
|
844
|
+
|
845
|
+
for arg_name, arg_type in expected_args.items():
|
846
|
+
|
847
|
+
arg_value = framework_args.get(arg_name)
|
848
|
+
|
849
|
+
if _val.check_type(arg_type, arg_value):
|
850
|
+
continue
|
851
|
+
|
852
|
+
if arg_value is None:
|
853
|
+
self._report_error(f"Using [{framework}], required argument [{arg_name}] is missing")
|
854
|
+
|
855
|
+
else:
|
856
|
+
expected_type_name = self._type_name(arg_type)
|
857
|
+
actual_type_name = self._type_name(type(arg_value))
|
858
|
+
|
859
|
+
self._report_error(
|
860
|
+
f"Using [{framework}], argument [{arg_name}] has the wrong type" +
|
861
|
+
f" (expected {expected_type_name}, got {actual_type_name})")
|
862
|
+
|
713
863
|
def check_storage_valid_identifier(self, storage_key):
|
714
864
|
|
715
865
|
if storage_key is None:
|
716
866
|
self._report_error(f"Storage key is null")
|
717
867
|
|
718
|
-
if not self.
|
868
|
+
if not self._VALID_IDENTIFIER.match(storage_key):
|
719
869
|
self._report_error(f"Storage key {storage_key} is not a valid identifier")
|
720
870
|
|
721
871
|
def check_storage_available(self, storage_map: tp.Dict, storage_key: str):
|
@@ -737,16 +887,6 @@ class TracContextValidator(TracContextErrorReporter):
|
|
737
887
|
else:
|
738
888
|
self._report_error(f"Storage key [{storage_key}] refers to file storage, not data storage")
|
739
889
|
|
740
|
-
@staticmethod
|
741
|
-
def _type_name(type_: type):
|
742
|
-
|
743
|
-
module = type_.__module__
|
744
|
-
|
745
|
-
if module is None or module == str.__class__.__module__:
|
746
|
-
return type_.__qualname__
|
747
|
-
|
748
|
-
return module + '.' + type_.__name__
|
749
|
-
|
750
890
|
|
751
891
|
class TracStorageValidator(TracContextErrorReporter):
|
752
892
|
|
@@ -777,3 +917,30 @@ class TracStorageValidator(TracContextErrorReporter):
|
|
777
917
|
|
778
918
|
if _val.StorageValidator.storage_path_is_empty(storage_path):
|
779
919
|
self._report_error(f"Storage path [{storage_path}] is not allowed")
|
920
|
+
|
921
|
+
def check_table_name_is_valid(self, table_name: str):
|
922
|
+
|
923
|
+
if table_name is None:
|
924
|
+
self._report_error(f"Table name is null")
|
925
|
+
|
926
|
+
if not self._VALID_IDENTIFIER.match(table_name):
|
927
|
+
self._report_error(f"Table name {table_name} is not a valid identifier")
|
928
|
+
|
929
|
+
def check_table_name_not_reserved(self, table_name: str):
|
930
|
+
|
931
|
+
if self._RESERVED_IDENTIFIER.match(table_name):
|
932
|
+
self._report_error(f"Table name {table_name} is a reserved identifier")
|
933
|
+
|
934
|
+
def check_provided_dataset_type(self, dataset: tp.Any, expected_type: type):
|
935
|
+
|
936
|
+
if dataset is None:
|
937
|
+
self._report_error(f"Provided dataset is null")
|
938
|
+
|
939
|
+
if not isinstance(dataset, expected_type):
|
940
|
+
|
941
|
+
expected_type_name = self._type_name(expected_type)
|
942
|
+
actual_type_name = self._type_name(type(dataset))
|
943
|
+
|
944
|
+
self._report_error(
|
945
|
+
f"Provided dataset is the wrong type" +
|
946
|
+
f" (expected {expected_type_name}, got {actual_type_name})")
|