tracdap-runtime 0.7.1__py3-none-any.whl → 0.8.0__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/__init__.py +14 -0
- tracdap/rt/_impl/{config_parser.py → core/config_parser.py} +36 -19
- tracdap/rt/_impl/{data.py → core/data.py} +136 -32
- tracdap/rt/_impl/core/logging.py +195 -0
- tracdap/rt/_impl/{models.py → core/models.py} +15 -12
- tracdap/rt/_impl/{repos.py → core/repos.py} +12 -3
- tracdap/rt/_impl/{schemas.py → core/schemas.py} +5 -5
- tracdap/rt/_impl/{shim.py → core/shim.py} +5 -4
- tracdap/rt/_impl/{storage.py → core/storage.py} +21 -10
- tracdap/rt/_impl/core/struct.py +547 -0
- tracdap/rt/_impl/{util.py → core/util.py} +1 -111
- tracdap/rt/_impl/{validation.py → core/validation.py} +99 -31
- tracdap/rt/_impl/exec/__init__.py +14 -0
- tracdap/rt/{_exec → _impl/exec}/actors.py +12 -14
- tracdap/rt/{_exec → _impl/exec}/context.py +228 -82
- tracdap/rt/{_exec → _impl/exec}/dev_mode.py +163 -81
- tracdap/rt/{_exec → _impl/exec}/engine.py +230 -105
- tracdap/rt/{_exec → _impl/exec}/functions.py +191 -100
- tracdap/rt/{_exec → _impl/exec}/graph.py +24 -36
- tracdap/rt/{_exec → _impl/exec}/graph_builder.py +252 -115
- tracdap/rt/_impl/grpc/codec.py +1 -1
- tracdap/rt/{_exec → _impl/grpc}/server.py +7 -6
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +3 -3
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +40 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.pyi +62 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +32 -20
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +48 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +4 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +8 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +65 -63
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +16 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +28 -26
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +14 -4
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +4 -4
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +6 -0
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +9 -7
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +12 -4
- tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +18 -5
- tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.pyi +42 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/{stoarge_pb2.py → storage_pb2.py} +4 -4
- 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 +1 -1
- tracdap/rt/{_exec → _impl}/runtime.py +32 -18
- tracdap/rt/_impl/static_api.py +65 -37
- tracdap/rt/_plugins/format_csv.py +1 -1
- tracdap/rt/_plugins/repo_git.py +56 -11
- tracdap/rt/_plugins/storage_sql.py +1 -1
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/__init__.py +5 -24
- tracdap/rt/api/constants.py +57 -0
- tracdap/rt/api/experimental.py +32 -0
- tracdap/rt/api/hook.py +26 -7
- tracdap/rt/api/model_api.py +16 -0
- tracdap/rt/api/static_api.py +265 -127
- tracdap/rt/config/__init__.py +11 -11
- tracdap/rt/config/common.py +2 -26
- tracdap/rt/config/dynamic.py +28 -0
- tracdap/rt/config/platform.py +17 -31
- tracdap/rt/config/runtime.py +2 -0
- tracdap/rt/ext/embed.py +2 -2
- tracdap/rt/ext/plugins.py +3 -3
- tracdap/rt/launch/launch.py +12 -14
- tracdap/rt/metadata/__init__.py +28 -18
- tracdap/rt/metadata/config.py +95 -0
- tracdap/rt/metadata/data.py +40 -0
- tracdap/rt/metadata/file.py +10 -0
- tracdap/rt/metadata/job.py +16 -0
- tracdap/rt/metadata/model.py +12 -2
- tracdap/rt/metadata/object.py +9 -1
- tracdap/rt/metadata/object_id.py +6 -0
- tracdap/rt/metadata/resource.py +41 -1
- {tracdap_runtime-0.7.1.dist-info → tracdap_runtime-0.8.0.dist-info}/METADATA +23 -17
- tracdap_runtime-0.8.0.dist-info/RECORD +129 -0
- {tracdap_runtime-0.7.1.dist-info → tracdap_runtime-0.8.0.dist-info}/WHEEL +1 -1
- tracdap/rt/_exec/__init__.py +0 -0
- tracdap_runtime-0.7.1.dist-info/RECORD +0 -121
- /tracdap/rt/_impl/{guard_rails.py → core/guard_rails.py} +0 -0
- /tracdap/rt/_impl/{type_system.py → core/type_system.py} +0 -0
- /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.7.1.dist-info → tracdap_runtime-0.8.0.dist-info/licenses}/LICENSE +0 -0
- {tracdap_runtime-0.7.1.dist-info → tracdap_runtime-0.8.0.dist-info}/top_level.txt +0 -0
@@ -22,7 +22,8 @@ import pathlib
|
|
22
22
|
|
23
23
|
import tracdap.rt.metadata as meta
|
24
24
|
import tracdap.rt.exceptions as ex
|
25
|
-
import tracdap.rt._impl.
|
25
|
+
import tracdap.rt._impl.core.logging as log
|
26
|
+
import tracdap.rt._impl.core.util as util
|
26
27
|
|
27
28
|
# _Named placeholder type from API hook is needed for API type checking
|
28
29
|
from tracdap.rt.api.hook import _Named # noqa
|
@@ -66,15 +67,23 @@ class SkipValidation(tp.Generic[T_SKIP_VAL]):
|
|
66
67
|
|
67
68
|
class _TypeValidator:
|
68
69
|
|
69
|
-
#
|
70
|
-
#
|
71
|
-
__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)
|
72
81
|
|
73
82
|
# Cache method signatures to avoid inspection on every call
|
74
83
|
# Inspecting a function signature can take ~ half a second in Python 3.7
|
75
84
|
__method_cache: tp.Dict[str, tp.Tuple[inspect.Signature, tp.Any]] = dict()
|
76
85
|
|
77
|
-
_log: logging.Logger =
|
86
|
+
_log: logging.Logger = log.logger_for_namespace(__name__)
|
78
87
|
|
79
88
|
@classmethod
|
80
89
|
def validate_signature(cls, method: tp.Callable, *args, **kwargs):
|
@@ -203,7 +212,7 @@ class _TypeValidator:
|
|
203
212
|
if value.skip_type == expected_type:
|
204
213
|
return True
|
205
214
|
|
206
|
-
if isinstance(expected_type, cls.__generic_metaclass):
|
215
|
+
if any(map(lambda _t: isinstance(expected_type, _t), cls.__generic_metaclass)):
|
207
216
|
|
208
217
|
origin = util.get_origin(expected_type)
|
209
218
|
args = util.get_args(expected_type)
|
@@ -239,6 +248,33 @@ class _TypeValidator:
|
|
239
248
|
all(map(lambda k: cls._validate_type(key_type, k), value.keys())) and \
|
240
249
|
all(map(lambda v: cls._validate_type(value_type, v), value.values()))
|
241
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
|
+
|
242
278
|
if origin.__module__.startswith("tracdap.rt.api."):
|
243
279
|
return isinstance(value, origin)
|
244
280
|
|
@@ -273,7 +309,7 @@ class _TypeValidator:
|
|
273
309
|
@classmethod
|
274
310
|
def _type_name(cls, type_var: tp.Type, qualified: bool = False) -> str:
|
275
311
|
|
276
|
-
if isinstance(type_var, cls.__generic_metaclass):
|
312
|
+
if any(map(lambda _t: isinstance(type_var, _t), cls.__generic_metaclass)):
|
277
313
|
|
278
314
|
origin = util.get_origin(type_var)
|
279
315
|
args = util.get_args(type_var)
|
@@ -290,7 +326,11 @@ class _TypeValidator:
|
|
290
326
|
|
291
327
|
if origin is list:
|
292
328
|
list_type = cls._type_name(args[0])
|
293
|
-
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}]"
|
294
334
|
|
295
335
|
raise ex.ETracInternal(f"Validation of [{origin.__name__}] generic parameters is not supported yet")
|
296
336
|
|
@@ -306,6 +346,9 @@ class StaticValidator:
|
|
306
346
|
__reserved_identifier_pattern = re.compile("\\A(_|trac_)", re.ASCII)
|
307
347
|
__label_length_limit = 4096
|
308
348
|
|
349
|
+
__file_extension_pattern = re.compile('\\A[a-zA-Z0-9]+\\Z')
|
350
|
+
__mime_type_pattern = re.compile('\\A\\w+/[-.\\w]+(?:\\+[-.\\w]+)?\\Z')
|
351
|
+
|
309
352
|
__PRIMITIVE_TYPES = [
|
310
353
|
meta.BasicType.BOOLEAN,
|
311
354
|
meta.BasicType.INTEGER,
|
@@ -321,7 +364,7 @@ class StaticValidator:
|
|
321
364
|
meta.BasicType.INTEGER,
|
322
365
|
meta.BasicType.DATE]
|
323
366
|
|
324
|
-
_log: logging.Logger =
|
367
|
+
_log: logging.Logger = log.logger_for_namespace(__name__)
|
325
368
|
|
326
369
|
@classmethod
|
327
370
|
def is_primitive_type(cls, basic_type: meta.BasicType) -> bool:
|
@@ -418,25 +461,49 @@ class StaticValidator:
|
|
418
461
|
cls._valid_identifiers(param.paramProps.keys(), "entry in param props")
|
419
462
|
|
420
463
|
@classmethod
|
421
|
-
def _check_inputs_or_outputs(cls,
|
464
|
+
def _check_inputs_or_outputs(cls, sockets):
|
422
465
|
|
423
|
-
for
|
466
|
+
for socket_name, socket in sockets.items():
|
424
467
|
|
425
|
-
|
468
|
+
if socket.objectType == meta.ObjectType.DATA:
|
469
|
+
cls._check_socket_schema(socket_name, socket)
|
470
|
+
elif socket.objectType == meta.ObjectType.FILE:
|
471
|
+
cls._check_socket_file_type(socket_name, socket)
|
472
|
+
else:
|
473
|
+
raise ex.EModelValidation(f"Invalid object type [{socket.objectType.name}] for [{socket_name}]")
|
426
474
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
475
|
+
label = socket.label
|
476
|
+
cls._check_label(label, socket_name)
|
477
|
+
|
478
|
+
if isinstance(socket, meta.ModelInputSchema):
|
479
|
+
if socket.inputProps is not None:
|
480
|
+
cls._valid_identifiers(socket.inputProps.keys(), "entry in input props")
|
481
|
+
else:
|
482
|
+
if socket.outputProps is not None:
|
483
|
+
cls._valid_identifiers(socket.outputProps.keys(), "entry in output props")
|
433
484
|
|
434
|
-
|
485
|
+
@classmethod
|
486
|
+
def _check_socket_schema(cls, socket_name, socket):
|
487
|
+
|
488
|
+
if socket.schema is None:
|
489
|
+
cls._fail(f"Missing schema requirement for [{socket_name}]")
|
490
|
+
return
|
491
|
+
|
492
|
+
if socket.dynamic:
|
493
|
+
if socket.schema and socket.schema.table:
|
494
|
+
error = "Dynamic schemas must have schema.table = None"
|
495
|
+
cls._fail(f"Invalid schema for [{socket_name}]: {error}")
|
496
|
+
else:
|
497
|
+
return
|
498
|
+
|
499
|
+
if socket.schema.schemaType == meta.SchemaType.TABLE:
|
500
|
+
|
501
|
+
fields = socket.schema.table.fields
|
435
502
|
field_names = list(map(lambda f: f.fieldName, fields))
|
436
|
-
property_type = f"field in [{
|
503
|
+
property_type = f"field in [{socket_name}]"
|
437
504
|
|
438
505
|
if len(fields) == 0:
|
439
|
-
cls._fail(f"Invalid schema for [{
|
506
|
+
cls._fail(f"Invalid schema for [{socket_name}]: No fields defined")
|
440
507
|
|
441
508
|
cls._valid_identifiers(field_names, property_type)
|
442
509
|
cls._case_insensitive_duplicates(field_names, property_type)
|
@@ -444,23 +511,24 @@ class StaticValidator:
|
|
444
511
|
for field in fields:
|
445
512
|
cls._check_single_field(field, property_type)
|
446
513
|
|
447
|
-
|
448
|
-
|
514
|
+
@classmethod
|
515
|
+
def _check_socket_file_type(cls, socket_name, socket):
|
449
516
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
517
|
+
if socket.fileType is None:
|
518
|
+
cls._fail(f"Missing file type requirement for [{socket_name}]")
|
519
|
+
return
|
520
|
+
|
521
|
+
if not cls.__file_extension_pattern.match(socket.fileType.extension):
|
522
|
+
cls._fail(f"Invalid extension [{socket.fileType.extension}] for [{socket_name}]")
|
523
|
+
|
524
|
+
if not cls.__mime_type_pattern.match(socket.fileType.mimeType):
|
525
|
+
cls._fail(f"Invalid mime type [{socket.fileType.mimeType}] for [{socket_name}]")
|
456
526
|
|
457
527
|
@classmethod
|
458
528
|
def _check_single_field(cls, field: meta.FieldSchema, property_type):
|
459
529
|
|
460
530
|
# Valid identifier and not trac reserved checked separately
|
461
531
|
|
462
|
-
cls._log.info(field.fieldName)
|
463
|
-
|
464
532
|
if field.fieldOrder < 0:
|
465
533
|
cls._fail(f"Invalid {property_type}: [{field.fieldName}] fieldOrder < 0")
|
466
534
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Licensed to the Fintech Open Source Foundation (FINOS) under one or
|
2
|
+
# more contributor license agreements. See the NOTICE file distributed
|
3
|
+
# with this work for additional information regarding copyright ownership.
|
4
|
+
# FINOS licenses this file to you under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
@@ -13,8 +13,6 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
from __future__ import annotations
|
17
|
-
|
18
16
|
import logging
|
19
17
|
import threading
|
20
18
|
import functools as func
|
@@ -25,8 +23,8 @@ import inspect
|
|
25
23
|
import queue
|
26
24
|
import time
|
27
25
|
|
28
|
-
import tracdap.rt._impl.
|
29
|
-
import tracdap.rt._impl.validation as _val
|
26
|
+
import tracdap.rt._impl.core.logging as _logging
|
27
|
+
import tracdap.rt._impl.core.validation as _val
|
30
28
|
import tracdap.rt.exceptions as _ex
|
31
29
|
|
32
30
|
|
@@ -118,7 +116,7 @@ class Actor:
|
|
118
116
|
def error(self) -> tp.Optional[Exception]:
|
119
117
|
return self.__ctx.get_error()
|
120
118
|
|
121
|
-
def actors(self) -> ActorContext:
|
119
|
+
def actors(self) -> "ActorContext":
|
122
120
|
return self.__ctx
|
123
121
|
|
124
122
|
def on_start(self):
|
@@ -134,7 +132,7 @@ class Actor:
|
|
134
132
|
class ActorContext:
|
135
133
|
|
136
134
|
def __init__(
|
137
|
-
self, node: ActorNode, message: str,
|
135
|
+
self, node: "ActorNode", message: str,
|
138
136
|
current_actor: ActorId, parent: ActorId, sender: tp.Optional[ActorId]):
|
139
137
|
|
140
138
|
self.__node = node
|
@@ -188,13 +186,13 @@ class ThreadsafeActor(Actor):
|
|
188
186
|
super().__init__()
|
189
187
|
self.__threadsafe: tp.Optional[ThreadsafeContext] = None
|
190
188
|
|
191
|
-
def threadsafe(self) -> ThreadsafeContext:
|
189
|
+
def threadsafe(self) -> "ThreadsafeContext":
|
192
190
|
return self.__threadsafe
|
193
191
|
|
194
192
|
|
195
193
|
class ThreadsafeContext:
|
196
194
|
|
197
|
-
def __init__(self, node: ActorNode):
|
195
|
+
def __init__(self, node: "ActorNode"):
|
198
196
|
self.__node = node
|
199
197
|
self.__id = node.actor_id
|
200
198
|
self.__parent = node.parent.actor_id if node.parent is not None else None
|
@@ -235,7 +233,7 @@ class EventLoop:
|
|
235
233
|
self.__shutdown = False
|
236
234
|
self.__shutdown_now = False
|
237
235
|
self.__done = False
|
238
|
-
self.__log =
|
236
|
+
self.__log = _logging.logger_for_object(self)
|
239
237
|
|
240
238
|
def post_message(self, msg: _T_MSG, processor: tp.Callable[[_T_MSG], None]) -> bool:
|
241
239
|
with self.__msg_lock:
|
@@ -365,12 +363,12 @@ class FunctionCache:
|
|
365
363
|
|
366
364
|
class ActorNode:
|
367
365
|
|
368
|
-
_log =
|
366
|
+
_log = _logging.logger_for_class(Actor)
|
369
367
|
|
370
368
|
def __init__(
|
371
369
|
self, actor_id: ActorId, actor: Actor,
|
372
|
-
parent: tp.Optional[ActorNode],
|
373
|
-
system: ActorSystem,
|
370
|
+
parent: "tp.Optional[ActorNode]",
|
371
|
+
system: "ActorSystem",
|
374
372
|
event_loop: EventLoop):
|
375
373
|
|
376
374
|
self.actor_id = actor_id
|
@@ -483,7 +481,7 @@ class ActorNode:
|
|
483
481
|
|
484
482
|
target_node._accept(msg)
|
485
483
|
|
486
|
-
def _lookup_node(self, target_id: ActorId) -> tp.Optional[ActorNode]:
|
484
|
+
def _lookup_node(self, target_id: ActorId) -> "tp.Optional[ActorNode]":
|
487
485
|
|
488
486
|
# Check self first
|
489
487
|
|
@@ -904,7 +902,7 @@ class ActorSystem:
|
|
904
902
|
|
905
903
|
super().__init__()
|
906
904
|
|
907
|
-
self._log =
|
905
|
+
self._log = _logging.logger_for_object(self)
|
908
906
|
|
909
907
|
# self.__actors: tp.Dict[ActorId, ActorNode] = {self.__ROOT_ID: ActorNode("", self.__ROOT_ID, None)}
|
910
908
|
# self.__message_queue: tp.List[Msg] = list()
|