tracdap-runtime 0.6.1.dev3__py3-none-any.whl → 0.6.3__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.
Files changed (102) hide show
  1. tracdap/rt/_exec/actors.py +87 -10
  2. tracdap/rt/_exec/context.py +25 -1
  3. tracdap/rt/_exec/dev_mode.py +277 -221
  4. tracdap/rt/_exec/engine.py +79 -14
  5. tracdap/rt/_exec/functions.py +37 -8
  6. tracdap/rt/_exec/graph.py +2 -0
  7. tracdap/rt/_exec/graph_builder.py +118 -56
  8. tracdap/rt/_exec/runtime.py +108 -37
  9. tracdap/rt/_exec/server.py +345 -0
  10. tracdap/rt/_impl/config_parser.py +219 -49
  11. tracdap/rt/_impl/data.py +14 -0
  12. tracdap/rt/_impl/grpc/__init__.py +13 -0
  13. tracdap/rt/_impl/grpc/codec.py +99 -0
  14. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +51 -0
  15. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +61 -0
  16. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +183 -0
  17. tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +33 -0
  18. tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.pyi +34 -0
  19. tracdap/rt/{metadata → _impl/grpc/tracdap/metadata}/custom_pb2.py +5 -5
  20. tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.pyi +15 -0
  21. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +51 -0
  22. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +115 -0
  23. tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +28 -0
  24. tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +22 -0
  25. tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +59 -0
  26. tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.pyi +109 -0
  27. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +76 -0
  28. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +177 -0
  29. tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +63 -0
  30. tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +119 -0
  31. tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +32 -0
  32. tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +68 -0
  33. tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +40 -0
  34. tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +46 -0
  35. tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +39 -0
  36. tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.pyi +83 -0
  37. tracdap/rt/_impl/grpc/tracdap/metadata/stoarge_pb2.py +50 -0
  38. tracdap/rt/_impl/grpc/tracdap/metadata/stoarge_pb2.pyi +89 -0
  39. tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +34 -0
  40. tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.pyi +26 -0
  41. tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +30 -0
  42. tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.pyi +34 -0
  43. tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +47 -0
  44. tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +101 -0
  45. tracdap/rt/_impl/guard_rails.py +26 -6
  46. tracdap/rt/_impl/models.py +25 -0
  47. tracdap/rt/_impl/static_api.py +27 -9
  48. tracdap/rt/_impl/type_system.py +17 -0
  49. tracdap/rt/_impl/validation.py +10 -0
  50. tracdap/rt/_plugins/config_local.py +49 -0
  51. tracdap/rt/_version.py +1 -1
  52. tracdap/rt/api/hook.py +10 -3
  53. tracdap/rt/api/model_api.py +22 -0
  54. tracdap/rt/api/static_api.py +79 -19
  55. tracdap/rt/config/__init__.py +3 -3
  56. tracdap/rt/config/common.py +10 -0
  57. tracdap/rt/config/platform.py +9 -19
  58. tracdap/rt/config/runtime.py +2 -0
  59. tracdap/rt/ext/config.py +34 -0
  60. tracdap/rt/ext/embed.py +1 -3
  61. tracdap/rt/ext/plugins.py +47 -6
  62. tracdap/rt/launch/cli.py +7 -5
  63. tracdap/rt/launch/launch.py +49 -12
  64. tracdap/rt/metadata/__init__.py +24 -24
  65. tracdap/rt/metadata/common.py +7 -7
  66. tracdap/rt/metadata/custom.py +2 -0
  67. tracdap/rt/metadata/data.py +28 -5
  68. tracdap/rt/metadata/file.py +2 -0
  69. tracdap/rt/metadata/flow.py +66 -4
  70. tracdap/rt/metadata/job.py +56 -16
  71. tracdap/rt/metadata/model.py +10 -0
  72. tracdap/rt/metadata/object.py +3 -0
  73. tracdap/rt/metadata/object_id.py +9 -9
  74. tracdap/rt/metadata/search.py +35 -13
  75. tracdap/rt/metadata/stoarge.py +64 -6
  76. tracdap/rt/metadata/tag_update.py +21 -7
  77. tracdap/rt/metadata/type.py +28 -13
  78. {tracdap_runtime-0.6.1.dev3.dist-info → tracdap_runtime-0.6.3.dist-info}/METADATA +22 -19
  79. tracdap_runtime-0.6.3.dist-info/RECORD +112 -0
  80. {tracdap_runtime-0.6.1.dev3.dist-info → tracdap_runtime-0.6.3.dist-info}/WHEEL +1 -1
  81. tracdap/rt/config/common_pb2.py +0 -55
  82. tracdap/rt/config/job_pb2.py +0 -42
  83. tracdap/rt/config/platform_pb2.py +0 -71
  84. tracdap/rt/config/result_pb2.py +0 -37
  85. tracdap/rt/config/runtime_pb2.py +0 -42
  86. tracdap/rt/ext/_guard.py +0 -37
  87. tracdap/rt/metadata/common_pb2.py +0 -33
  88. tracdap/rt/metadata/data_pb2.py +0 -51
  89. tracdap/rt/metadata/file_pb2.py +0 -28
  90. tracdap/rt/metadata/flow_pb2.py +0 -55
  91. tracdap/rt/metadata/job_pb2.py +0 -76
  92. tracdap/rt/metadata/model_pb2.py +0 -51
  93. tracdap/rt/metadata/object_id_pb2.py +0 -32
  94. tracdap/rt/metadata/object_pb2.py +0 -35
  95. tracdap/rt/metadata/search_pb2.py +0 -39
  96. tracdap/rt/metadata/stoarge_pb2.py +0 -50
  97. tracdap/rt/metadata/tag_pb2.py +0 -34
  98. tracdap/rt/metadata/tag_update_pb2.py +0 -30
  99. tracdap/rt/metadata/type_pb2.py +0 -48
  100. tracdap_runtime-0.6.1.dev3.dist-info/RECORD +0 -96
  101. {tracdap_runtime-0.6.1.dev3.dist-info → tracdap_runtime-0.6.3.dist-info}/LICENSE +0 -0
  102. {tracdap_runtime-0.6.1.dev3.dist-info → tracdap_runtime-0.6.3.dist-info}/top_level.txt +0 -0
@@ -25,6 +25,7 @@ import queue
25
25
  import time
26
26
 
27
27
  import tracdap.rt._impl.util as util # noqa
28
+ import tracdap.rt._impl.validation as _val # noqa
28
29
  import tracdap.rt.exceptions as _ex
29
30
 
30
31
 
@@ -180,6 +181,49 @@ class ActorContext:
180
181
  return self.__error or self.__node.error
181
182
 
182
183
 
184
+ class ThreadsafeActor(Actor):
185
+
186
+ def __init__(self):
187
+ super().__init__()
188
+ self.__threadsafe: tp.Optional[ThreadsafeContext] = None
189
+
190
+ def threadsafe(self) -> ThreadsafeContext:
191
+ return self.__threadsafe
192
+
193
+
194
+ class ThreadsafeContext:
195
+
196
+ def __init__(self, node: ActorNode):
197
+ self.__node = node
198
+ self.__id = node.actor_id
199
+ self.__parent = node.parent.actor_id if node.parent is not None else None
200
+
201
+ def spawn(self, actor: Actor):
202
+ self.__node.event_loop.post_message(
203
+ None, lambda _:
204
+ self.__node.spawn(actor) and None)
205
+
206
+ def send(self, target_id: ActorId, message: str, *args, **kwargs):
207
+ self.__node.event_loop.post_message(
208
+ None, lambda _:
209
+ self.__node.send_message(self.__id, target_id, message, args, kwargs))
210
+
211
+ def send_parent(self, message: str, *args, **kwargs):
212
+ self.__node.event_loop.post_message(
213
+ None, lambda _:
214
+ self.__node.send_message(self.__id, self.__parent, message, args, kwargs))
215
+
216
+ def stop(self):
217
+ self.__node.event_loop.post_message(
218
+ None, lambda _:
219
+ self.__node.send_signal(self.__id, self.__id, SignalNames.STOP))
220
+
221
+ def fail(self, error: Exception):
222
+ self.__node.event_loop.post_message(
223
+ None, lambda _:
224
+ self.__node.send_signal(self.__id, self.__id, SignalNames.STOP, error))
225
+
226
+
183
227
  class EventLoop:
184
228
 
185
229
  _T_MSG = tp.TypeVar("_T_MSG")
@@ -340,7 +384,7 @@ class ActorNode:
340
384
  self.state: ActorState = ActorState.NOT_STARTED
341
385
  self.error: tp.Optional[Exception] = None
342
386
 
343
- def spawn(self, child_actor: Actor):
387
+ def spawn(self, child_actor: Actor) -> ActorId:
344
388
 
345
389
  if self._log.isEnabledFor(logging.DEBUG):
346
390
  self._log.debug(f"spawn [{self.actor_id}]: [{type(child_actor)}]")
@@ -355,6 +399,11 @@ class ActorNode:
355
399
  child_node = ActorNode(child_id, child_actor, self, self.system, event_loop)
356
400
  self.children[child_id] = child_node
357
401
 
402
+ # If this is a threadsafe actor, set up the threadsafe context
403
+ if isinstance(child_actor, ThreadsafeActor):
404
+ threadsafe = ThreadsafeContext(child_node)
405
+ child_actor._ThreadsafeActor__threadsafe = threadsafe
406
+
358
407
  child_node.send_signal(self.actor_id, child_id, SignalNames.START)
359
408
 
360
409
  return child_id
@@ -542,6 +591,12 @@ class ActorNode:
542
591
  if not self._check_message_target(signal):
543
592
  return
544
593
 
594
+ # Do not process signals after the actor has stopped
595
+ # This is common with e.g. STOP signals that propagate up and down the tree
596
+
597
+ if self.state in [ActorState.STOPPED, ActorState.FAILED]:
598
+ return
599
+
545
600
  # Call the signal receiver function
546
601
  # This gives the actor a chance to respond to the signal
547
602
 
@@ -768,10 +823,12 @@ class ActorNode:
768
823
  # Positional arg types
769
824
  for pos_param, pos_arg in zip(pos_params, args):
770
825
 
826
+ # If no type hint is available, allow anything through
827
+ # Otherwise, reuse the validator logic to type check individual args
771
828
  type_hint = type_hints.get(pos_param.name)
829
+ type_check = type_hint is None or _val.check_type(type_hint, pos_arg)
772
830
 
773
- # If no type hint is available, allow anything through
774
- if type_hint is not None and not isinstance(pos_arg, type_hint):
831
+ if not type_check:
775
832
  error = f"Invalid message: [{message}] -> {target_id} (wrong parameter type for '{pos_param.name}')"
776
833
  self._log.error(error)
777
834
  raise EBadActor(error)
@@ -780,20 +837,20 @@ class ActorNode:
780
837
  for kw_param in kw_params:
781
838
 
782
839
  kw_arg = kwargs.get(kw_param.name)
783
- type_hint = type_hints.get(kw_param.name)
784
840
 
785
841
  # If param has taken a default value, no type check is needed
786
842
  if kw_arg is None:
787
843
  continue
788
844
 
789
- # If no type hint is available, allow anything through
790
- if type_hint is not None and not isinstance(kw_arg, type_hint):
845
+ # Otherwise use the same type-validation logic as positional args
846
+ type_hint = type_hints.get(kw_param.name)
847
+ type_check = type_hint is None or _val.check_type(type_hint, kw_arg)
848
+
849
+ if not type_check:
791
850
  error = f"Invalid message: [{message}] -> {target_id} (wrong parameter type for '{kw_param.name}')"
792
851
  self._log.error(error)
793
852
  raise EBadActor(error)
794
853
 
795
- # TODO: Verify generics for both args and kwargs
796
-
797
854
 
798
855
  class RootActor(Actor):
799
856
 
@@ -864,11 +921,17 @@ class ActorSystem:
864
921
 
865
922
  self.__root_started = threading.Event()
866
923
  self.__root_stopped = threading.Event()
924
+
867
925
  self.__root_actor = RootActor(main_actor, self.__root_started, self.__root_stopped)
868
926
  self.__root_node = ActorNode(self.ROOT_ID, self.__root_actor, None, self, self.__system_event_loop)
869
927
 
870
928
  # Public API
871
929
 
930
+ def main_id(self) -> ActorId:
931
+ if not self.__root_started.is_set():
932
+ raise EBadActor("System has not started yet")
933
+ return self.__root_actor.main_id
934
+
872
935
  def start(self, wait=True):
873
936
 
874
937
  self.__system_thread.start()
@@ -913,12 +976,26 @@ class ActorSystem:
913
976
 
914
977
  return self.__root_node.error
915
978
 
916
- def send(self, message: str, *args, **kwargs):
979
+ def spawn_agent(self, agent: Actor) -> ActorId:
980
+
981
+ if not self.__root_started.is_set():
982
+ raise EBadActor("System has not started yet")
983
+
984
+ return self.__root_node.spawn(agent)
985
+
986
+ def send_main(self, message: str, *args, **kwargs):
917
987
 
918
988
  if self.__root_actor.main_id is None:
919
989
  raise EBadActor("System has not started yet")
920
990
 
921
- self.__root_node.send_message("/external", self.__root_actor.main_id, message, args, kwargs)
991
+ self.__root_node.send_message("/external", self.__root_actor.main_id, message, args, kwargs) # TODO
992
+
993
+ def send(self, actor_id: ActorId, message: str, *args, **kwargs):
994
+
995
+ if not self.__root_started.is_set():
996
+ raise EBadActor("System has not started yet")
997
+
998
+ self.__root_node.send_message("/external", actor_id, message, args, kwargs)
922
999
 
923
1000
  def _setup_event_loops(self, thread_pools: tp.Dict[str, int]):
924
1001
 
@@ -79,7 +79,6 @@ class TracContextImpl(_api.TracContext):
79
79
  self.__data,
80
80
  checkout_directory)
81
81
 
82
-
83
82
  def get_parameter(self, parameter_name: str) -> tp.Any:
84
83
 
85
84
  _val.validate_signature(self.get_parameter, parameter_name)
@@ -92,6 +91,31 @@ class TracContextImpl(_api.TracContext):
92
91
 
93
92
  return _types.MetadataCodec.decode_value(value)
94
93
 
94
+ def has_dataset(self, dataset_name: str) -> bool:
95
+
96
+ _val.validate_signature(self.has_dataset, dataset_name)
97
+
98
+ part_key = _data.DataPartKey.for_root()
99
+
100
+ self.__val.check_dataset_name_not_null(dataset_name)
101
+ self.__val.check_dataset_valid_identifier(dataset_name)
102
+
103
+ data_view = self.__data.get(dataset_name)
104
+
105
+ if data_view is None:
106
+ return False
107
+
108
+ # If the item exists but is not a dataset, that is still a runtime error
109
+ # E.g. if this method is called for FILE inputs
110
+ self.__val.check_context_item_is_dataset(dataset_name)
111
+
112
+ part = data_view.parts.get(part_key)
113
+
114
+ if part is None or len(part) == 0:
115
+ return False
116
+
117
+ return True
118
+
95
119
  def get_schema(self, dataset_name: str) -> _meta.SchemaDefinition:
96
120
 
97
121
  _val.validate_signature(self.get_schema, dataset_name)