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
@@ -14,89 +14,216 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- import re
18
- import typing as tp
17
+ import dataclasses as _dc
19
18
  import decimal
20
19
  import enum
21
- import uuid
20
+ import io
22
21
  import inspect
23
- import dataclasses as _dc
22
+ import json
23
+ import os
24
+ import pathlib
25
+ import re
26
+ import typing as tp
27
+ import urllib.parse as _urlp
28
+ import uuid
24
29
 
30
+ import tracdap.rt.config as _config
25
31
  import tracdap.rt.exceptions as _ex
32
+ import tracdap.rt.ext.plugins as _plugins
33
+ import tracdap.rt.ext.config as _config_ext
26
34
  import tracdap.rt._impl.util as _util
27
35
 
28
- import pathlib
29
- import json
30
36
  import yaml
31
37
  import yaml.parser
32
38
 
33
-
34
39
  _T = tp.TypeVar('_T')
35
40
 
36
41
 
37
- class ConfigParser(tp.Generic[_T]):
42
+ class ConfigManager:
38
43
 
39
- # The metaclass for generic types varies between versions of the typing library
40
- # To work around this, detect the correct metaclass by inspecting a generic type variable
41
- __generic_metaclass = type(tp.List[object])
44
+ @classmethod
45
+ def for_root_config(cls, root_config_file: tp.Union[str, pathlib.Path, None]) -> ConfigManager:
46
+
47
+ if isinstance(root_config_file, pathlib.Path):
48
+ root_file_path = cls._resolve_scheme(root_config_file)
49
+ root_dir_path = cls._resolve_scheme(root_config_file.parent)
50
+ if root_dir_path[-1] not in ["/", "\\"]:
51
+ root_dir_path += os.sep
52
+ root_file_url = _urlp.urlparse(root_file_path, scheme="file")
53
+ root_dir_url = _urlp.urlparse(root_dir_path, scheme="file")
54
+ return ConfigManager(root_dir_url, root_file_url, )
55
+
56
+ elif isinstance(root_config_file, str):
57
+ root_file_with_scheme = cls._resolve_scheme(root_config_file)
58
+ root_file_url = _urlp.urlparse(root_file_with_scheme, scheme="file")
59
+ root_dir_path = str(pathlib.Path(root_file_url.path).parent)
60
+ if root_dir_path[-1] not in ["/", "\\"]:
61
+ root_dir_path += os.sep if root_file_url.scheme == "file" else "/"
62
+ root_dir_url = _urlp.urlparse(_urlp.urljoin(root_file_url.geturl(), root_dir_path))
63
+ return ConfigManager(root_dir_url, root_file_url)
42
64
 
43
- __primitive_types: tp.Dict[type, callable] = {
44
- bool: bool,
45
- int: int,
46
- float: float,
47
- str: str,
48
- decimal.Decimal: decimal.Decimal
49
- # TODO: Date (requires type system)
50
- # TODO: Datetime (requires type system)
51
- }
65
+ else:
66
+ working_dir_path = str(pathlib.Path.cwd().resolve())
67
+ working_dir_url = _urlp.urlparse(str(working_dir_path), scheme="file")
68
+ return ConfigManager(working_dir_url, None)
52
69
 
53
- def __init__(self, config_class: _T.__class__, dev_mode_locations: tp.List[str] = None):
70
+ @classmethod
71
+ def for_root_dir(cls, root_config_dir: tp.Union[str, pathlib.Path]) -> ConfigManager:
72
+
73
+ if isinstance(root_config_dir, pathlib.Path):
74
+ root_dir_path = cls._resolve_scheme(root_config_dir)
75
+ if root_dir_path[-1] not in ["/", "\\"]:
76
+ root_dir_path += os.sep
77
+ root_dir_url = _urlp.urlparse(root_dir_path, scheme="file")
78
+ return ConfigManager(root_dir_url, None)
79
+
80
+ elif isinstance(root_config_dir, str):
81
+ root_dir_with_scheme = cls._resolve_scheme(root_config_dir)
82
+ if root_dir_with_scheme[-1] not in ["/", "\\"]:
83
+ root_dir_with_scheme += "/"
84
+ root_dir_url = _urlp.urlparse(root_dir_with_scheme, scheme="file")
85
+ return ConfigManager(root_dir_url, None)
86
+
87
+ # Should never happen since root dir is specified explicitly
88
+ else:
89
+ raise _ex.ETracInternal("Wrong parameter type for root_config_dir")
90
+
91
+ @classmethod
92
+ def _resolve_scheme(cls, raw_url: tp.Union[str, pathlib.Path]) -> str:
93
+
94
+ if isinstance(raw_url, pathlib.Path):
95
+ return "file:" + str(raw_url.resolve())
96
+
97
+ # Look for drive letters on Windows - these can be mis-interpreted as URL scheme
98
+ # If there is a drive letter, explicitly set scheme = file instead
99
+ if len(raw_url) > 1 and raw_url[1] == ":":
100
+ return "file:" + raw_url
101
+ else:
102
+ return raw_url
103
+
104
+ def __init__(self, root_dir_url: _urlp.ParseResult, root_file_url: tp.Optional[_urlp.ParseResult]):
54
105
  self._log = _util.logger_for_object(self)
55
- self._config_class = config_class
56
- self._dev_mode_locations = dev_mode_locations or []
57
- self._errors = []
106
+ self._root_dir_url = root_dir_url
107
+ self._root_file_url = root_file_url
108
+
109
+ def config_dir_path(self):
110
+ if self._root_dir_url.scheme == "file":
111
+ return pathlib.Path(self._root_dir_url.path).resolve()
112
+ else:
113
+ return None
114
+
115
+ def load_root_object(
116
+ self, config_class: type(_T),
117
+ dev_mode_locations: tp.List[str] = None,
118
+ config_file_name: tp.Optional[str] = None) -> _T:
58
119
 
59
- def load_raw_config(self, config_file: tp.Union[str, pathlib.Path], config_file_name: str = None):
120
+ # Root config not available normally means you're using embedded config
121
+ # In which case this method should not be called
122
+ if self._root_file_url is None:
123
+ message = f"Root config file not available"
124
+ self._log.error(message)
125
+ raise _ex.EConfigLoad(message)
126
+
127
+ resolved_url = self._root_file_url
60
128
 
61
129
  if config_file_name is not None:
62
- self._log.info(f"Loading {config_file_name} config: {str(config_file)}")
130
+ self._log.info(f"Loading {config_file_name} config: {self._url_to_str(resolved_url)}")
63
131
  else:
64
- self._log.info(f"Loading config file: {str(config_file)}")
132
+ self._log.info(f"Loading config file: {self._url_to_str(resolved_url)}")
133
+
134
+ config_dict = self._load_config_dict(resolved_url)
65
135
 
66
- # Construct a Path for config_file and make sure the file exists
67
- # (For now, config must be on a locally mounted filesystem)
136
+ parser = ConfigParser(config_class, dev_mode_locations)
137
+ return parser.parse(config_dict, resolved_url.path)
68
138
 
69
- if isinstance(config_file, str):
70
- config_path = pathlib.Path(config_file)
139
+ def load_config_object(
140
+ self, config_url: tp.Union[str, pathlib.Path],
141
+ config_class: type(_T),
142
+ dev_mode_locations: tp.List[str] = None,
143
+ config_file_name: tp.Optional[str] = None) -> _T:
71
144
 
72
- elif isinstance(config_file, pathlib.Path):
73
- config_path = config_file
145
+ resolved_url = self._resolve_config_file(config_url)
74
146
 
147
+ if config_file_name is not None:
148
+ self._log.info(f"Loading {config_file_name} config: {self._url_to_str(resolved_url)}")
75
149
  else:
76
- config_file_type = type(config_file) if config_file is not None else "None"
77
- err = f"Attempt to load an invalid config file, expected a path, got {config_file_type}"
78
- self._log.error(err)
79
- raise _ex.EConfigLoad(err)
150
+ self._log.info(f"Loading config file: {self._url_to_str(resolved_url)}")
80
151
 
81
- if not config_path.exists():
82
- msg = f"Config file not found: [{config_file}]"
83
- self._log.error(msg)
84
- raise _ex.EConfigLoad(msg)
152
+ config_dict = self._load_config_dict(resolved_url)
85
153
 
86
- if not config_path.is_file():
87
- msg = f"Config path does not point to a regular file: [{config_file}]"
88
- self._log.error(msg)
89
- raise _ex.EConfigLoad(msg)
154
+ parser = ConfigParser(config_class, dev_mode_locations)
155
+ return parser.parse(config_dict, config_url)
90
156
 
91
- return self._parse_raw_config(config_path)
157
+ def load_config_file(
158
+ self, config_url: tp.Union[str, pathlib.Path],
159
+ config_file_name: tp.Optional[str] = None) -> bytes:
92
160
 
93
- def _parse_raw_config(self, config_path: pathlib.Path):
161
+ resolved_url = self._resolve_config_file(config_url)
162
+
163
+ if config_file_name is not None:
164
+ self._log.info(f"Loading {config_file_name} config: {self._url_to_str(resolved_url)}")
165
+ else:
166
+ self._log.info(f"Loading config file: {self._url_to_str(resolved_url)}")
167
+
168
+ return self._load_config_file(resolved_url)
169
+
170
+ def _resolve_config_file(self, config_url: tp.Union[str, pathlib.Path]) -> _urlp.ParseResult:
171
+
172
+ # If the config URL defines a scheme, treat it as absolute
173
+ # (This also works for Windows paths, C:\ is an absolute path)
174
+ if ":" in str(config_url):
175
+ absolute_url = str(config_url)
176
+ # If the root URL is a path, resolve using path logic (this allows for config_url to be an absolute path)
177
+ elif self._root_dir_url.scheme == "file":
178
+ absolute_url = str(pathlib.Path(self._root_dir_url.path).joinpath(str(config_url)))
179
+ # Otherwise resolve relative to the root URL
180
+ else:
181
+ absolute_url = _urlp.urljoin(self._root_dir_url.geturl(), str(config_url))
182
+
183
+ # Look for drive letters on Windows - these can be mis-interpreted as URL scheme
184
+ # If there is a drive letter, explicitly set scheme = file instead
185
+ if len(absolute_url) > 1 and absolute_url[1] == ":":
186
+ absolute_url = "file:" + absolute_url
187
+
188
+ return _urlp.urlparse(absolute_url, scheme="file")
189
+
190
+ def _load_config_file(self, resolved_url: _urlp.ParseResult) -> bytes:
191
+
192
+ loader = self._get_loader(resolved_url)
193
+ config_url = self._url_to_str(resolved_url)
194
+
195
+ if not loader.has_config_file(config_url):
196
+ message = f"Config file not found: {config_url}"
197
+ self._log.error(message)
198
+ raise _ex.EConfigLoad(message)
199
+
200
+ return loader.load_config_file(config_url)
201
+
202
+ def _load_config_dict(self, resolved_url: _urlp.ParseResult) -> dict:
203
+
204
+ loader = self._get_loader(resolved_url)
205
+ config_url = self._url_to_str(resolved_url)
206
+
207
+ if loader.has_config_dict(config_url):
208
+ return loader.load_config_dict(config_url)
209
+
210
+ elif loader.has_config_file(config_url):
211
+ config_bytes = loader.load_config_file(config_url)
212
+ config_path = pathlib.Path(resolved_url.path)
213
+ return self._parse_config_dict(config_bytes, config_path)
214
+
215
+ else:
216
+ message = f"Config file not found: {config_url}"
217
+ self._log.error(message)
218
+ raise _ex.EConfigLoad(message)
219
+
220
+ def _parse_config_dict(self, config_bytes: bytes, config_path: pathlib.Path):
94
221
 
95
222
  # Read in the raw config, use the file extension to decide which format to expect
96
223
 
97
224
  try:
98
225
 
99
- with config_path.open('r') as config_stream:
226
+ with io.BytesIO(config_bytes) as config_stream:
100
227
 
101
228
  extension = config_path.suffix.lower()
102
229
 
@@ -123,11 +250,54 @@ class ConfigParser(tp.Generic[_T]):
123
250
  self._log.error(err)
124
251
  raise _ex.EConfigParse(err) from e
125
252
 
126
- except yaml.parser.ParserError as e:
253
+ except (yaml.parser.ParserError, yaml.reader.ReaderError) as e:
127
254
  err = f"Config file contains invalid YAML ({str(e)})"
128
255
  self._log.error(err)
129
256
  raise _ex.EConfigParse(err) from e
130
257
 
258
+ def _get_loader(self, resolved_url: _urlp.ParseResult) -> _config_ext.IConfigLoader:
259
+
260
+ protocol = resolved_url.scheme
261
+ loader_config = _config.PluginConfig(protocol)
262
+
263
+ if not _plugins.PluginManager.is_plugin_available(_config_ext.IConfigLoader, protocol):
264
+ message = f"No config loader available for protocol [{protocol}]: {self._url_to_str(resolved_url)}"
265
+ self._log.error(message)
266
+ raise _ex.EConfigLoad(message)
267
+
268
+ return _plugins.PluginManager.load_config_plugin(_config_ext.IConfigLoader, loader_config)
269
+
270
+ @staticmethod
271
+ def _url_to_str(url: _urlp.ParseResult) -> str:
272
+
273
+ if url.scheme == "file" and not url.netloc:
274
+ return url.path
275
+ else:
276
+ return url.geturl()
277
+
278
+
279
+ class ConfigParser(tp.Generic[_T]):
280
+
281
+ # The metaclass for generic types varies between versions of the typing library
282
+ # To work around this, detect the correct metaclass by inspecting a generic type variable
283
+ __generic_metaclass = type(tp.List[object])
284
+
285
+ __primitive_types: tp.Dict[type, callable] = {
286
+ bool: bool,
287
+ int: int,
288
+ float: float,
289
+ str: str,
290
+ decimal.Decimal: decimal.Decimal
291
+ # TODO: Date (requires type system)
292
+ # TODO: Datetime (requires type system)
293
+ }
294
+
295
+ def __init__(self, config_class: _T.__class__, dev_mode_locations: tp.List[str] = None):
296
+ self._log = _util.logger_for_object(self)
297
+ self._config_class = config_class
298
+ self._dev_mode_locations = dev_mode_locations or []
299
+ self._errors = []
300
+
131
301
  def parse(self, config_dict: dict, config_file: tp.Union[str, pathlib.Path] = None) -> _T:
132
302
 
133
303
  # If config is empty, return a default (blank) config
tracdap/rt/_impl/data.py CHANGED
@@ -58,6 +58,13 @@ class DataItem:
58
58
  pandas: tp.Optional[pd.DataFrame] = None
59
59
  pyspark: tp.Any = None
60
60
 
61
+ def is_empty(self) -> bool:
62
+ return self.table is None and (self.batches is None or len(self.batches) == 0)
63
+
64
+ @staticmethod
65
+ def create_empty() -> DataItem:
66
+ return DataItem(pa.schema([]))
67
+
61
68
 
62
69
  @dc.dataclass(frozen=True)
63
70
  class DataView:
@@ -72,6 +79,13 @@ class DataView:
72
79
  arrow_schema = DataMapping.trac_to_arrow_schema(trac_schema)
73
80
  return DataView(trac_schema, arrow_schema, dict())
74
81
 
82
+ def is_empty(self) -> bool:
83
+ return self.parts is None or len(self.parts) == 0
84
+
85
+ @staticmethod
86
+ def create_empty() -> DataView:
87
+ return DataView(_meta.SchemaDefinition(), pa.schema([]), dict())
88
+
75
89
 
76
90
  class _DataInternal:
77
91
  pass
@@ -0,0 +1,13 @@
1
+ # Copyright 2024 Accenture Global Solutions Limited
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,99 @@
1
+ # Copyright 2024 Accenture Global Solutions Limited
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import enum
16
+ import typing as tp
17
+
18
+ import tracdap.rt.exceptions as ex
19
+ import tracdap.rt.metadata as metadata
20
+
21
+ import tracdap.rt._impl.grpc.tracdap.metadata.type_pb2 as type_pb2
22
+ import tracdap.rt._impl.grpc.tracdap.metadata.object_id_pb2 as object_id_pb2
23
+ import tracdap.rt._impl.grpc.tracdap.metadata.object_pb2 as object_pb2
24
+ from tracdap.rt._impl.grpc.tracdap.metadata import model_pb2
25
+ import tracdap.rt._impl.grpc.tracdap.metadata.data_pb2 as data_pb2
26
+ import tracdap.rt._impl.grpc.tracdap.metadata.stoarge_pb2 as storage_pb2
27
+
28
+ from google.protobuf import message as _message
29
+
30
+
31
+ __METADATA_MAPPING = {
32
+ metadata.TypeDescriptor: type_pb2.TypeDescriptor,
33
+ metadata.Value: type_pb2.Value,
34
+ metadata.DecimalValue: type_pb2.DecimalValue,
35
+ metadata.DateValue: type_pb2.DateValue,
36
+ metadata.DatetimeValue: type_pb2.DatetimeValue,
37
+ metadata.ArrayValue: type_pb2.ArrayValue,
38
+ metadata.MapValue: type_pb2.MapValue,
39
+ metadata.TagHeader: object_id_pb2.TagHeader,
40
+ metadata.TagSelector: object_id_pb2.TagSelector,
41
+ metadata.ObjectDefinition: object_pb2.ObjectDefinition,
42
+ metadata.ModelDefinition: model_pb2.ModelDefinition,
43
+ metadata.ModelParameter: model_pb2.ModelParameter,
44
+ metadata.ModelInputSchema: model_pb2.ModelInputSchema,
45
+ metadata.ModelOutputSchema: model_pb2.ModelOutputSchema,
46
+ metadata.SchemaDefinition: data_pb2.SchemaDefinition,
47
+ metadata.TableSchema: data_pb2.TableSchema,
48
+ metadata.FieldSchema: data_pb2.FieldSchema,
49
+ metadata.PartKey: data_pb2.PartKey,
50
+ metadata.DataDefinition: data_pb2.DataDefinition,
51
+ metadata.DataDefinition.Part: data_pb2.DataDefinition.Part,
52
+ metadata.DataDefinition.Snap: data_pb2.DataDefinition.Snap,
53
+ metadata.DataDefinition.Delta: data_pb2.DataDefinition.Delta,
54
+ metadata.StorageDefinition: storage_pb2.StorageDefinition,
55
+ metadata.StorageIncarnation: storage_pb2.StorageIncarnation,
56
+ metadata.StorageCopy: storage_pb2.StorageCopy,
57
+ metadata.StorageItem: storage_pb2.StorageItem
58
+ }
59
+
60
+
61
+ _T_MSG = tp.TypeVar('_T_MSG', bound=_message.Message)
62
+
63
+
64
+ def encode_message(msg_class: _T_MSG.__class__, obj: tp.Any) -> _T_MSG:
65
+
66
+ attrs = dict((k, encode(v)) for k, v in obj.__dict__.items())
67
+
68
+ return msg_class(**attrs)
69
+
70
+
71
+ def encode(obj: tp.Any) -> tp.Any:
72
+
73
+ # Translate TRAC domain objects into generic dict / list structures
74
+ # These can be accepted by gRPC message constructors, do not try to build messages directly
75
+ # Use shallow copies and builtins to minimize performance impact
76
+
77
+ if obj is None:
78
+ return None
79
+
80
+ if isinstance(obj, str) or isinstance(obj, bool) or isinstance(obj, int) or isinstance(obj, float):
81
+ return obj
82
+
83
+ if isinstance(obj, enum.Enum):
84
+ return obj.value
85
+
86
+ if isinstance(obj, list):
87
+ return list(map(encode, obj))
88
+
89
+ if isinstance(obj, dict):
90
+ return dict((k, encode(v)) for k, v in obj.items())
91
+
92
+ msg_class = __METADATA_MAPPING.get(type(obj))
93
+
94
+ if msg_class is None:
95
+ raise ex.ETracInternal(f"No gRPC metadata mapping is available for type {type(obj).__name__}")
96
+
97
+ attrs = dict((k, encode(v)) for k, v in obj.__dict__.items() if v is not None)
98
+
99
+ return msg_class(**attrs)
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: tracdap/rt/_impl/grpc/tracdap/api/internal/runtime.proto
4
+ # Protobuf Python Version: 4.25.3
5
+ """Generated protocol buffer code."""
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
10
+ # @@protoc_insertion_point(imports)
11
+
12
+ _sym_db = _symbol_database.Default()
13
+
14
+
15
+ from tracdap.rt._impl.grpc.tracdap.metadata import object_id_pb2 as tracdap_dot_rt_dot___impl_dot_grpc_dot_tracdap_dot_metadata_dot_object__id__pb2
16
+ from tracdap.rt._impl.grpc.tracdap.metadata import job_pb2 as tracdap_dot_rt_dot___impl_dot_grpc_dot_tracdap_dot_metadata_dot_job__pb2
17
+ from tracdap.rt._impl.grpc.tracdap.metadata import object_pb2 as tracdap_dot_rt_dot___impl_dot_grpc_dot_tracdap_dot_metadata_dot_object__pb2
18
+ from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2
19
+
20
+
21
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n8tracdap/rt/_impl/grpc/tracdap/api/internal/runtime.proto\x12\x14tracdap.api.internal\x1a\x36tracdap/rt/_impl/grpc/tracdap/metadata/object_id.proto\x1a\x30tracdap/rt/_impl/grpc/tracdap/metadata/job.proto\x1a\x33tracdap/rt/_impl/grpc/tracdap/metadata/object.proto\x1a\x1cgoogle/api/annotations.proto\"6\n\x16RuntimeListJobsRequest\x12\x12\n\x05limit\x18\x01 \x01(\rH\x00\x88\x01\x01\x42\x08\n\x06_limit\"O\n\x17RuntimeListJobsResponse\x12\x34\n\x04jobs\x18\x01 \x03(\x0b\x32&.tracdap.api.internal.RuntimeJobStatus\"f\n\x15RuntimeJobInfoRequest\x12\x34\n\x0bjobSelector\x18\x01 \x01(\x0b\x32\x1d.tracdap.metadata.TagSelectorH\x00\x12\x10\n\x06jobKey\x18\x02 \x01(\tH\x00\x42\x05\n\x03job\"\x9f\x01\n\x10RuntimeJobStatus\x12*\n\x05jobId\x18\x01 \x01(\x0b\x32\x1b.tracdap.metadata.TagHeader\x12\x33\n\nstatusCode\x18\x02 \x01(\x0e\x32\x1f.tracdap.metadata.JobStatusCode\x12\x15\n\rstatusMessage\x18\x03 \x01(\t\x12\x13\n\x0b\x65rrorDetail\x18\x04 \x01(\t\"\xa4\x02\n\x10RuntimeJobResult\x12*\n\x05jobId\x18\x01 \x01(\x0b\x32\x1b.tracdap.metadata.TagHeader\x12\x33\n\nstatusCode\x18\x02 \x01(\x0e\x32\x1f.tracdap.metadata.JobStatusCode\x12\x15\n\rstatusMessage\x18\x03 \x01(\t\x12\x44\n\x07results\x18\x04 \x03(\x0b\x32\x33.tracdap.api.internal.RuntimeJobResult.ResultsEntry\x1aR\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x31\n\x05value\x18\x02 \x01(\x0b\x32\".tracdap.metadata.ObjectDefinition:\x02\x38\x01\x32\x95\x03\n\x0eTracRuntimeApi\x12{\n\x08listJobs\x12,.tracdap.api.internal.RuntimeListJobsRequest\x1a-.tracdap.api.internal.RuntimeListJobsResponse\"\x12\x82\xd3\xe4\x93\x02\x0c\x12\n/list-jobs\x12\x81\x01\n\x0cgetJobStatus\x12+.tracdap.api.internal.RuntimeJobInfoRequest\x1a&.tracdap.api.internal.RuntimeJobStatus\"\x1c\x82\xd3\xe4\x93\x02\x16\x12\x14/job-status/{jobKey}\x12\x81\x01\n\x0cgetJobResult\x12+.tracdap.api.internal.RuntimeJobInfoRequest\x1a&.tracdap.api.internal.RuntimeJobResult\"\x1c\x82\xd3\xe4\x93\x02\x16\x12\x14/job-result/{jobKey}B\"\n\x1eorg.finos.tracdap.api.internalP\x01\x62\x06proto3')
22
+
23
+ _globals = globals()
24
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
25
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tracdap.rt._impl.grpc.tracdap.api.internal.runtime_pb2', _globals)
26
+ if _descriptor._USE_C_DESCRIPTORS == False:
27
+ _globals['DESCRIPTOR']._options = None
28
+ _globals['DESCRIPTOR']._serialized_options = b'\n\036org.finos.tracdap.api.internalP\001'
29
+ _globals['_RUNTIMEJOBRESULT_RESULTSENTRY']._options = None
30
+ _globals['_RUNTIMEJOBRESULT_RESULTSENTRY']._serialized_options = b'8\001'
31
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['listJobs']._options = None
32
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['listJobs']._serialized_options = b'\202\323\344\223\002\014\022\n/list-jobs'
33
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['getJobStatus']._options = None
34
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['getJobStatus']._serialized_options = b'\202\323\344\223\002\026\022\024/job-status/{jobKey}'
35
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['getJobResult']._options = None
36
+ _globals['_TRACRUNTIMEAPI'].methods_by_name['getJobResult']._serialized_options = b'\202\323\344\223\002\026\022\024/job-result/{jobKey}'
37
+ _globals['_RUNTIMELISTJOBSREQUEST']._serialized_start=271
38
+ _globals['_RUNTIMELISTJOBSREQUEST']._serialized_end=325
39
+ _globals['_RUNTIMELISTJOBSRESPONSE']._serialized_start=327
40
+ _globals['_RUNTIMELISTJOBSRESPONSE']._serialized_end=406
41
+ _globals['_RUNTIMEJOBINFOREQUEST']._serialized_start=408
42
+ _globals['_RUNTIMEJOBINFOREQUEST']._serialized_end=510
43
+ _globals['_RUNTIMEJOBSTATUS']._serialized_start=513
44
+ _globals['_RUNTIMEJOBSTATUS']._serialized_end=672
45
+ _globals['_RUNTIMEJOBRESULT']._serialized_start=675
46
+ _globals['_RUNTIMEJOBRESULT']._serialized_end=967
47
+ _globals['_RUNTIMEJOBRESULT_RESULTSENTRY']._serialized_start=885
48
+ _globals['_RUNTIMEJOBRESULT_RESULTSENTRY']._serialized_end=967
49
+ _globals['_TRACRUNTIMEAPI']._serialized_start=970
50
+ _globals['_TRACRUNTIMEAPI']._serialized_end=1375
51
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,61 @@
1
+ from tracdap.rt._impl.grpc.tracdap.metadata import object_id_pb2 as _object_id_pb2
2
+ from tracdap.rt._impl.grpc.tracdap.metadata import job_pb2 as _job_pb2
3
+ from tracdap.rt._impl.grpc.tracdap.metadata import object_pb2 as _object_pb2
4
+ from google.api import annotations_pb2 as _annotations_pb2
5
+ from google.protobuf.internal import containers as _containers
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import message as _message
8
+ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
9
+
10
+ DESCRIPTOR: _descriptor.FileDescriptor
11
+
12
+ class RuntimeListJobsRequest(_message.Message):
13
+ __slots__ = ("limit",)
14
+ LIMIT_FIELD_NUMBER: _ClassVar[int]
15
+ limit: int
16
+ def __init__(self, limit: _Optional[int] = ...) -> None: ...
17
+
18
+ class RuntimeListJobsResponse(_message.Message):
19
+ __slots__ = ("jobs",)
20
+ JOBS_FIELD_NUMBER: _ClassVar[int]
21
+ jobs: _containers.RepeatedCompositeFieldContainer[RuntimeJobStatus]
22
+ def __init__(self, jobs: _Optional[_Iterable[_Union[RuntimeJobStatus, _Mapping]]] = ...) -> None: ...
23
+
24
+ class RuntimeJobInfoRequest(_message.Message):
25
+ __slots__ = ("jobSelector", "jobKey")
26
+ JOBSELECTOR_FIELD_NUMBER: _ClassVar[int]
27
+ JOBKEY_FIELD_NUMBER: _ClassVar[int]
28
+ jobSelector: _object_id_pb2.TagSelector
29
+ jobKey: str
30
+ def __init__(self, jobSelector: _Optional[_Union[_object_id_pb2.TagSelector, _Mapping]] = ..., jobKey: _Optional[str] = ...) -> None: ...
31
+
32
+ class RuntimeJobStatus(_message.Message):
33
+ __slots__ = ("jobId", "statusCode", "statusMessage", "errorDetail")
34
+ JOBID_FIELD_NUMBER: _ClassVar[int]
35
+ STATUSCODE_FIELD_NUMBER: _ClassVar[int]
36
+ STATUSMESSAGE_FIELD_NUMBER: _ClassVar[int]
37
+ ERRORDETAIL_FIELD_NUMBER: _ClassVar[int]
38
+ jobId: _object_id_pb2.TagHeader
39
+ statusCode: _job_pb2.JobStatusCode
40
+ statusMessage: str
41
+ errorDetail: str
42
+ def __init__(self, jobId: _Optional[_Union[_object_id_pb2.TagHeader, _Mapping]] = ..., statusCode: _Optional[_Union[_job_pb2.JobStatusCode, str]] = ..., statusMessage: _Optional[str] = ..., errorDetail: _Optional[str] = ...) -> None: ...
43
+
44
+ class RuntimeJobResult(_message.Message):
45
+ __slots__ = ("jobId", "statusCode", "statusMessage", "results")
46
+ class ResultsEntry(_message.Message):
47
+ __slots__ = ("key", "value")
48
+ KEY_FIELD_NUMBER: _ClassVar[int]
49
+ VALUE_FIELD_NUMBER: _ClassVar[int]
50
+ key: str
51
+ value: _object_pb2.ObjectDefinition
52
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[_object_pb2.ObjectDefinition, _Mapping]] = ...) -> None: ...
53
+ JOBID_FIELD_NUMBER: _ClassVar[int]
54
+ STATUSCODE_FIELD_NUMBER: _ClassVar[int]
55
+ STATUSMESSAGE_FIELD_NUMBER: _ClassVar[int]
56
+ RESULTS_FIELD_NUMBER: _ClassVar[int]
57
+ jobId: _object_id_pb2.TagHeader
58
+ statusCode: _job_pb2.JobStatusCode
59
+ statusMessage: str
60
+ results: _containers.MessageMap[str, _object_pb2.ObjectDefinition]
61
+ def __init__(self, jobId: _Optional[_Union[_object_id_pb2.TagHeader, _Mapping]] = ..., statusCode: _Optional[_Union[_job_pb2.JobStatusCode, str]] = ..., statusMessage: _Optional[str] = ..., results: _Optional[_Mapping[str, _object_pb2.ObjectDefinition]] = ...) -> None: ...