tracdap-runtime 0.7.0rc1__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.
Files changed (88) hide show
  1. tracdap/rt/_impl/core/__init__.py +14 -0
  2. tracdap/rt/_impl/{config_parser.py → core/config_parser.py} +61 -36
  3. tracdap/rt/_impl/{data.py → core/data.py} +136 -32
  4. tracdap/rt/_impl/core/logging.py +195 -0
  5. tracdap/rt/_impl/{models.py → core/models.py} +15 -12
  6. tracdap/rt/_impl/{repos.py → core/repos.py} +12 -3
  7. tracdap/rt/_impl/{schemas.py → core/schemas.py} +5 -5
  8. tracdap/rt/_impl/{shim.py → core/shim.py} +5 -4
  9. tracdap/rt/_impl/{storage.py → core/storage.py} +21 -10
  10. tracdap/rt/_impl/core/struct.py +547 -0
  11. tracdap/rt/_impl/{type_system.py → core/type_system.py} +73 -33
  12. tracdap/rt/_impl/{util.py → core/util.py} +1 -111
  13. tracdap/rt/_impl/{validation.py → core/validation.py} +99 -31
  14. tracdap/rt/_impl/exec/__init__.py +14 -0
  15. tracdap/rt/{_exec → _impl/exec}/actors.py +12 -14
  16. tracdap/rt/{_exec → _impl/exec}/context.py +228 -82
  17. tracdap/rt/{_exec → _impl/exec}/dev_mode.py +176 -89
  18. tracdap/rt/{_exec → _impl/exec}/engine.py +230 -105
  19. tracdap/rt/{_exec → _impl/exec}/functions.py +191 -100
  20. tracdap/rt/{_exec → _impl/exec}/graph.py +24 -36
  21. tracdap/rt/{_exec → _impl/exec}/graph_builder.py +252 -115
  22. tracdap/rt/_impl/grpc/codec.py +1 -1
  23. tracdap/rt/{_exec → _impl/grpc}/server.py +7 -6
  24. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +3 -3
  25. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +1 -1
  26. tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
  27. tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +40 -0
  28. tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.pyi +62 -0
  29. tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
  30. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +32 -20
  31. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +48 -2
  32. tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +4 -2
  33. tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +8 -0
  34. tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
  35. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +65 -63
  36. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +16 -2
  37. tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +28 -26
  38. tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +14 -4
  39. tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +4 -4
  40. tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +6 -0
  41. tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +9 -7
  42. tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +12 -4
  43. tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +18 -5
  44. tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.pyi +42 -2
  45. tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
  46. tracdap/rt/_impl/grpc/tracdap/metadata/{stoarge_pb2.py → storage_pb2.py} +4 -4
  47. tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +1 -1
  48. tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +1 -1
  49. tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +1 -1
  50. tracdap/rt/{_exec → _impl}/runtime.py +32 -18
  51. tracdap/rt/_impl/static_api.py +66 -38
  52. tracdap/rt/_plugins/format_csv.py +1 -1
  53. tracdap/rt/_plugins/repo_git.py +56 -11
  54. tracdap/rt/_plugins/storage_sql.py +13 -6
  55. tracdap/rt/_version.py +1 -1
  56. tracdap/rt/api/__init__.py +5 -24
  57. tracdap/rt/api/constants.py +57 -0
  58. tracdap/rt/api/experimental.py +32 -0
  59. tracdap/rt/api/hook.py +26 -7
  60. tracdap/rt/api/model_api.py +16 -0
  61. tracdap/rt/api/static_api.py +265 -127
  62. tracdap/rt/config/__init__.py +11 -11
  63. tracdap/rt/config/common.py +2 -26
  64. tracdap/rt/config/dynamic.py +28 -0
  65. tracdap/rt/config/platform.py +17 -31
  66. tracdap/rt/config/runtime.py +2 -0
  67. tracdap/rt/ext/embed.py +2 -2
  68. tracdap/rt/ext/plugins.py +3 -3
  69. tracdap/rt/launch/launch.py +12 -14
  70. tracdap/rt/metadata/__init__.py +31 -21
  71. tracdap/rt/metadata/config.py +95 -0
  72. tracdap/rt/metadata/data.py +40 -0
  73. tracdap/rt/metadata/file.py +10 -0
  74. tracdap/rt/metadata/job.py +16 -0
  75. tracdap/rt/metadata/model.py +12 -2
  76. tracdap/rt/metadata/object.py +9 -1
  77. tracdap/rt/metadata/object_id.py +6 -0
  78. tracdap/rt/metadata/resource.py +41 -1
  79. {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0.dist-info}/METADATA +33 -27
  80. tracdap_runtime-0.8.0.dist-info/RECORD +129 -0
  81. {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0.dist-info}/WHEEL +1 -1
  82. tracdap/rt/_exec/__init__.py +0 -0
  83. tracdap_runtime-0.7.0rc1.dist-info/RECORD +0 -121
  84. /tracdap/rt/_impl/{guard_rails.py → core/guard_rails.py} +0 -0
  85. /tracdap/rt/_impl/grpc/tracdap/metadata/{stoarge_pb2.pyi → storage_pb2.pyi} +0 -0
  86. /tracdap/rt/metadata/{stoarge.py → storage.py} +0 -0
  87. {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0.dist-info/licenses}/LICENSE +0 -0
  88. {tracdap_runtime-0.7.0rc1.dist-info → tracdap_runtime-0.8.0.dist-info}/top_level.txt +0 -0
@@ -19,10 +19,11 @@ import types as _ts
19
19
  import tracdap.rt.api.experimental as _api
20
20
  import tracdap.rt.metadata as _meta
21
21
  import tracdap.rt.exceptions as _ex
22
- import tracdap.rt._impl.data as _data
23
- import tracdap.rt._impl.schemas as _schemas
24
- import tracdap.rt._impl.type_system as _type_system
25
- import tracdap.rt._impl.validation as _val
22
+ import tracdap.rt._impl.core.data as _data
23
+ import tracdap.rt._impl.core.schemas as _schemas
24
+ import tracdap.rt._impl.core.struct as _struct
25
+ import tracdap.rt._impl.core.type_system as _type_system
26
+ import tracdap.rt._impl.core.validation as _val
26
27
 
27
28
  # Import hook interfaces into this module namespace
28
29
  from tracdap.rt.api.hook import _StaticApiHook # noqa
@@ -55,7 +56,7 @@ class StaticApiImpl(_StaticApiHook):
55
56
  if not _val.is_primitive_type(entry_type):
56
57
  raise _ex.EModelValidation(f"Maps can only contain primitive types, [{entry_type}] is not primitive")
57
58
 
58
- return _meta.TypeDescriptor(_meta.BasicType.MAP, arrayType=_meta.TypeDescriptor(entry_type))
59
+ return _meta.TypeDescriptor(_meta.BasicType.MAP, mapType=_meta.TypeDescriptor(entry_type))
59
60
 
60
61
  def define_attribute(
61
62
  self, attr_name: str, attr_value: _tp.Any,
@@ -150,16 +151,27 @@ class StaticApiImpl(_StaticApiHook):
150
151
  notNull=not_null,
151
152
  formatCode=format_code)
152
153
 
154
+ def define_struct(self, python_type: type[_api.STRUCT_TYPE]):
155
+
156
+ _val.validate_signature(self.define_struct, python_type)
157
+
158
+ struct_schema = _struct.StructProcessor.define_struct(python_type)
159
+ return _meta.SchemaDefinition(schemaType=_meta.SchemaType.STRUCT, struct=struct_schema)
160
+
153
161
  def define_schema(
154
162
  self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
155
- schema_type: _meta.SchemaType = _meta.SchemaType.TABLE) \
163
+ schema_type: _meta.SchemaType = _meta.SchemaType.TABLE, dynamic: bool = False) \
156
164
  -> _meta.SchemaDefinition:
157
165
 
158
- _val.validate_signature(self.define_schema, *fields, schema_type=schema_type)
166
+ _val.validate_signature(self.define_schema, *fields, schema_type=schema_type, dynamic=dynamic)
159
167
 
160
168
  if schema_type == _meta.SchemaType.TABLE:
161
169
 
162
- table_schema = self._build_table_schema(*fields)
170
+ if dynamic and not fields:
171
+ table_schema = None
172
+ else:
173
+ table_schema = self._build_table_schema(*fields)
174
+
163
175
  return _meta.SchemaDefinition(_meta.SchemaType.TABLE, table=table_schema)
164
176
 
165
177
  raise _ex.ERuntimeValidation(f"Invalid schema type [{schema_type.name}]")
@@ -182,51 +194,67 @@ class StaticApiImpl(_StaticApiHook):
182
194
 
183
195
  return converter.infer_schema(dataset)
184
196
 
185
- def define_input_table(
186
- self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
187
- label: _tp.Optional[str] = None, optional: bool = False, dynamic: bool = False,
188
- input_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) \
189
- -> _meta.ModelInputSchema:
197
+ def define_file_type(self, extension: str, mime_type: str) -> _meta.FileType:
198
+
199
+ _val.validate_signature(self.define_file_type, extension, mime_type)
200
+
201
+ return _meta.FileType(extension=extension, mimeType=mime_type)
202
+
203
+ def define_input(
204
+ self, requirement: _tp.Union[_meta.SchemaDefinition, _meta.FileType], *,
205
+ label: _tp.Optional[str] = None,
206
+ optional: bool = False, dynamic: bool = False,
207
+ input_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None):
190
208
 
191
209
  _val.validate_signature(
192
- self.define_input_table, *fields,
210
+ self.define_input, requirement,
193
211
  label=label, optional=optional, dynamic=dynamic,
194
212
  input_props=input_props)
195
213
 
196
- # Do not define details for dynamic schemas
214
+ if isinstance(requirement, _meta.SchemaDefinition):
197
215
 
198
- if dynamic:
199
- schema_def = _meta.SchemaDefinition(_meta.SchemaType.TABLE)
200
- else:
201
- schema_def = self.define_schema(*fields, schema_type=_meta.SchemaType.TABLE)
216
+ return _meta.ModelInputSchema(
217
+ objectType=_meta.ObjectType.DATA, schema=requirement,
218
+ label=label, optional=optional, dynamic=dynamic,
219
+ inputProps=input_props)
202
220
 
203
- return _meta.ModelInputSchema(
204
- schema=schema_def, label=label,
205
- optional=optional, dynamic=dynamic,
206
- inputProps=input_props)
221
+ elif isinstance(requirement, _meta.FileType):
207
222
 
208
- def define_output_table(
209
- self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
210
- label: _tp.Optional[str] = None, optional: bool = False, dynamic: bool = False,
211
- output_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) \
212
- -> _meta.ModelOutputSchema:
223
+ return _meta.ModelInputSchema(
224
+ objectType=_meta.ObjectType.FILE, fileType=requirement,
225
+ label=label, optional=optional, dynamic=dynamic,
226
+ inputProps=input_props)
227
+
228
+ else:
229
+ raise _ex.EUnexpected()
230
+
231
+ def define_output(
232
+ self, requirement: _tp.Union[_meta.SchemaDefinition, _meta.FileType], *,
233
+ label: _tp.Optional[str] = None,
234
+ optional: bool = False, dynamic: bool = False,
235
+ output_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None):
213
236
 
214
237
  _val.validate_signature(
215
- self.define_output_table, *fields,
238
+ self.define_output, requirement,
216
239
  label=label, optional=optional, dynamic=dynamic,
217
240
  output_props=output_props)
218
241
 
219
- # Do not define details for dynamic schemas
242
+ if isinstance(requirement, _meta.SchemaDefinition):
220
243
 
221
- if dynamic:
222
- schema_def = _meta.SchemaDefinition(_meta.SchemaType.TABLE)
223
- else:
224
- schema_def = self.define_schema(*fields, schema_type=_meta.SchemaType.TABLE)
244
+ return _meta.ModelOutputSchema(
245
+ objectType=_meta.ObjectType.DATA, schema=requirement,
246
+ label=label, optional=optional, dynamic=dynamic,
247
+ outputProps=output_props)
248
+
249
+ elif isinstance(requirement, _meta.FileType):
225
250
 
226
- return _meta.ModelOutputSchema(
227
- schema=schema_def, label=label,
228
- optional=optional, dynamic=dynamic,
229
- outputProps=output_props)
251
+ return _meta.ModelOutputSchema(
252
+ objectType=_meta.ObjectType.FILE, fileType=requirement,
253
+ label=label, optional=optional, dynamic=dynamic,
254
+ outputProps=output_props)
255
+
256
+ else:
257
+ raise _ex.EUnexpected()
230
258
 
231
259
  @staticmethod
232
260
  def _build_named_dict(
@@ -34,7 +34,7 @@ from tracdap.rt.ext.storage import IDataFormat
34
34
  from . import _helpers
35
35
 
36
36
  # TODO: Remove dependency on internal code
37
- import tracdap.rt._impl.data as _data
37
+ import tracdap.rt._impl.core.data as _data
38
38
 
39
39
 
40
40
  class CsvStorageFormat(IDataFormat):
@@ -23,6 +23,7 @@ import time
23
23
  import dulwich.repo as git_repo
24
24
  import dulwich.client as git_client
25
25
  import dulwich.index as git_index
26
+ import urllib3.exceptions # noqa
26
27
 
27
28
  import tracdap.rt.metadata as meta
28
29
  import tracdap.rt.exceptions as ex
@@ -75,20 +76,45 @@ class GitRepository(IModelRepository):
75
76
 
76
77
  def do_checkout(self, model_def: meta.ModelDefinition, checkout_dir: pathlib.Path) -> pathlib.Path:
77
78
 
78
- self._log.info(
79
- f"Git checkout: repo = [{model_def.repository}], " +
80
- f"group = [{model_def.packageGroup}], package = [{model_def.package}], version = [{model_def.version}]")
79
+ try:
81
80
 
82
- self._log.info(f"Checkout location: [{checkout_dir}]")
81
+ self._log.info(
82
+ f"Git checkout: repo = [{model_def.repository}], " +
83
+ f"group = [{model_def.packageGroup}], package = [{model_def.package}], version = [{model_def.version}]")
83
84
 
84
- if self._native_git:
85
- package_path = self._do_native_checkout(model_def, checkout_dir)
86
- else:
87
- package_path = self._do_python_checkout(model_def, checkout_dir)
85
+ self._log.info(f"Checkout location: [{checkout_dir}]")
86
+
87
+ if self._native_git:
88
+ package_path = self._do_native_checkout(model_def, checkout_dir)
89
+ else:
90
+ package_path = self._do_python_checkout(model_def, checkout_dir)
91
+
92
+ self._log.info(f"Git checkout succeeded for {model_def.package} {model_def.version}")
93
+
94
+ return package_path
95
+
96
+ except Exception as e:
97
+
98
+ error = e
99
+
100
+ # For retry failures, try to find the original cause
101
+ while e.__cause__ is not None:
102
+ if isinstance(e, urllib3.exceptions.MaxRetryError):
103
+ error = e.__cause__
104
+ break
105
+ else:
106
+ e = e.__cause__
107
+
108
+ # Try to sanitize error messages from urllib3
109
+ if isinstance(error, urllib3.exceptions.HTTPError):
110
+ detail = self._clean_urllib3_error(error)
111
+ else:
112
+ detail = str(error)
88
113
 
89
- self._log.info(f"Git checkout succeeded for {model_def.package} {model_def.version}")
114
+ message = f"Failed to check out [{model_def.repository}]: {detail}"
90
115
 
91
- return package_path
116
+ self._log.error(message)
117
+ raise ex.EModelRepo(message) from error
92
118
 
93
119
  def _do_native_checkout(self, model_def: meta.ModelDefinition, checkout_dir: pathlib.Path) -> pathlib.Path:
94
120
 
@@ -150,10 +176,15 @@ class GitRepository(IModelRepository):
150
176
  for line in cmd_err:
151
177
  self._log.info(line)
152
178
 
153
- else:
179
+ elif cmd_err:
180
+
154
181
  for line in cmd_err:
155
182
  self._log.error(line)
156
183
 
184
+ raise ex.EModelRepo(cmd_err[-1])
185
+
186
+ else:
187
+
157
188
  error_msg = f"Git checkout failed for {model_def.package} {model_def.version}"
158
189
  self._log.error(error_msg)
159
190
  raise ex.EModelRepo(error_msg)
@@ -265,6 +296,20 @@ class GitRepository(IModelRepository):
265
296
  def _ref_key(key):
266
297
  return bytes(key, "ascii")
267
298
 
299
+ @classmethod
300
+ def _clean_urllib3_error(cls, error: urllib3.exceptions.HTTPError):
301
+
302
+ match = cls._URLLIB3_ERROR_PATTERN.match(str(error))
303
+
304
+ # Best efforts to clean up the message, fall back on str(error)
305
+ if match:
306
+ return match.group(1)
307
+ else:
308
+ return str(error)
309
+
310
+ # Error message format is like this:
311
+ # <pkg.ClassName object at 0xXXXXXXX>: Message
312
+ _URLLIB3_ERROR_PATTERN = re.compile(r"<[^>]*>: (.*)")
268
313
 
269
314
  # Register plugin
270
315
  plugins.PluginManager.register_plugin(IModelRepository, GitRepository, ["git"])
@@ -30,7 +30,7 @@ from tracdap.rt._impl.ext.sql import * # noqa
30
30
  import tracdap.rt._plugins._helpers as _helpers
31
31
 
32
32
  # TODO: Remove internal references
33
- import tracdap.rt._impl.data as _data
33
+ import tracdap.rt._impl.core.data as _data
34
34
 
35
35
 
36
36
  class SqlDataStorage(IDataStorageBase[pa.Table, pa.Schema]):
@@ -268,10 +268,18 @@ plugins.PluginManager.register_plugin(IStorageProvider, SqlStorageProvider, ["SQ
268
268
 
269
269
 
270
270
  try:
271
-
272
271
  import sqlalchemy as sqla # noqa
273
272
  import sqlalchemy.exc as sqla_exc # noqa
274
273
 
274
+ # Only 2.x versions of SQL Alchemy are currently supported
275
+ sqla_supported = sqla.__version__.startswith("2.")
276
+
277
+ except ModuleNotFoundError:
278
+ sqla = None
279
+ sqla_supported = False
280
+
281
+ if sqla_supported:
282
+
275
283
  class SqlAlchemyDriver(ISqlDriver):
276
284
 
277
285
  def __init__(self, properties: tp.Dict[str, str]):
@@ -336,7 +344,7 @@ try:
336
344
 
337
345
  class ConnectionWrapper(DbApiWrapper.Connection):
338
346
 
339
- def __init__(self, conn: sqla.Connection):
347
+ def __init__(self, conn: "sqla.Connection"):
340
348
  self.__conn = conn
341
349
 
342
350
  def close(self):
@@ -355,7 +363,7 @@ try:
355
363
 
356
364
  arraysize: int = 1000
357
365
 
358
- def __init__(self, conn: sqla.Connection):
366
+ def __init__(self, conn: "sqla.Connection"):
359
367
  self.__conn = conn
360
368
  self.__result: tp.Optional[sqla.CursorResult] = None
361
369
 
@@ -414,5 +422,4 @@ try:
414
422
 
415
423
  plugins.PluginManager.register_plugin(ISqlDriver, SqlAlchemyDriver, ["alchemy"])
416
424
 
417
- except ModuleNotFoundError:
418
- pass
425
+
tracdap/rt/_version.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- __version__ = "0.7.0rc1"
16
+ __version__ = "0.8.0"
@@ -17,32 +17,13 @@
17
17
  TRAC model API for Python
18
18
  """
19
19
 
20
- from .model_api import *
21
- from .static_api import *
22
-
23
20
  # Make metadata classes available to client code when importing the API package
24
21
  # Remove this import when generating docs, so metadata classes are only documented once
25
22
  from tracdap.rt.metadata import * # noqa DOCGEN_REMOVE
26
23
 
27
- # Map basic types into the root of the API package
28
-
29
- BOOLEAN = BasicType.BOOLEAN
30
- """Synonym for :py:attr:`BasicType.BOOLEAN <tracdap.rt.metadata.BasicType.BOOLEAN>`"""
31
-
32
- INTEGER = BasicType.INTEGER
33
- """Synonym for :py:attr:`BasicType.INTEGER <tracdap.rt.metadata.BasicType.INTEGER>`"""
34
-
35
- FLOAT = BasicType.FLOAT
36
- """Synonym for :py:attr:`BasicType.FLOAT <tracdap.rt.metadata.BasicType.FLOAT>`"""
37
-
38
- DECIMAL = BasicType.DECIMAL
39
- """Synonym for :py:attr:`BasicType.DECIMAL <tracdap.rt.metadata.BasicType.DECIMAL>`"""
40
-
41
- STRING = BasicType.STRING
42
- """Synonym for :py:attr:`BasicType.STRING <tracdap.rt.metadata.BasicType.STRING>`"""
43
-
44
- DATE = BasicType.DATE
45
- """Synonym for :py:attr:`BasicType.DATE <tracdap.rt.metadata.BasicType.DATE>`"""
24
+ # static_api overrides some metadata types for backwards compatibility with pre-0.8 versions
25
+ # Make sure it is last in the list
26
+ from .constants import *
27
+ from .model_api import *
28
+ from .static_api import *
46
29
 
47
- DATETIME = BasicType.DATETIME
48
- """Synonym for :py:attr:`BasicType.DATETIME <tracdap.rt.metadata.BasicType.DATETIME>`"""
@@ -0,0 +1,57 @@
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.
15
+
16
+ import tracdap.rt.metadata
17
+
18
+
19
+ class CommonFileTypes:
20
+
21
+ """
22
+ A collection of common :py:class:`FileTypes <tracdap.rt.metadata.FileType>` to use as model inputs and outputs
23
+ """
24
+
25
+ TXT = tracdap.rt.metadata.FileType("txt", "text/plain")
26
+
27
+ JPG = tracdap.rt.metadata.FileType("jpg", "image/jpeg")
28
+ PNG = tracdap.rt.metadata.FileType("png", "image/png")
29
+ SVG = tracdap.rt.metadata.FileType("svg", "image/svg+xml")
30
+
31
+ WORD = tracdap.rt.metadata.FileType("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
32
+ EXCEL = tracdap.rt.metadata.FileType("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
33
+ POWERPOINT = tracdap.rt.metadata.FileType("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
34
+
35
+
36
+ # Map basic types into the root of the API package
37
+
38
+ BOOLEAN = tracdap.rt.metadata.BasicType.BOOLEAN
39
+ """Synonym for :py:attr:`BasicType.BOOLEAN <tracdap.rt.metadata.BasicType.BOOLEAN>`"""
40
+
41
+ INTEGER = tracdap.rt.metadata.BasicType.INTEGER
42
+ """Synonym for :py:attr:`BasicType.INTEGER <tracdap.rt.metadata.BasicType.INTEGER>`"""
43
+
44
+ FLOAT = tracdap.rt.metadata.BasicType.FLOAT
45
+ """Synonym for :py:attr:`BasicType.FLOAT <tracdap.rt.metadata.BasicType.FLOAT>`"""
46
+
47
+ DECIMAL = tracdap.rt.metadata.BasicType.DECIMAL
48
+ """Synonym for :py:attr:`BasicType.DECIMAL <tracdap.rt.metadata.BasicType.DECIMAL>`"""
49
+
50
+ STRING = tracdap.rt.metadata.BasicType.STRING
51
+ """Synonym for :py:attr:`BasicType.STRING <tracdap.rt.metadata.BasicType.STRING>`"""
52
+
53
+ DATE = tracdap.rt.metadata.BasicType.DATE
54
+ """Synonym for :py:attr:`BasicType.DATE <tracdap.rt.metadata.BasicType.DATE>`"""
55
+
56
+ DATETIME = tracdap.rt.metadata.BasicType.DATETIME
57
+ """Synonym for :py:attr:`BasicType.DATETIME <tracdap.rt.metadata.BasicType.DATETIME>`"""
@@ -70,6 +70,8 @@ PANDAS = DataFramework.pandas()
70
70
  POLARS = DataFramework.polars()
71
71
  """Data framework constant for the Polars data library"""
72
72
 
73
+ STRUCT_TYPE = _tp.TypeVar('STRUCT_TYPE')
74
+
73
75
 
74
76
  class TracContext(TracContext):
75
77
 
@@ -83,6 +85,14 @@ class TracContext(TracContext):
83
85
 
84
86
  pass
85
87
 
88
+ def get_struct(self, struct_name: str, python_class: _tp.Type[STRUCT_TYPE]) -> STRUCT_TYPE:
89
+
90
+ pass
91
+
92
+ def put_struct(self, struct_name: str, struct_data: STRUCT_TYPE):
93
+
94
+ pass
95
+
86
96
 
87
97
  def init_static():
88
98
  """Ensure TRAC's static model API is available to use (for static definitions at module or class scope)"""
@@ -108,6 +118,28 @@ def map_type(entry_type: BasicType) -> TypeDescriptor:
108
118
  return sa.map_type(entry_type)
109
119
 
110
120
 
121
+ def define_struct(python_type: _tp.Type[STRUCT_TYPE]) -> SchemaDefinition:
122
+ """Build schema definition for a STRUCT"""
123
+ sa = _StaticApiHook.get_instance()
124
+ return sa.define_struct(python_type)
125
+
126
+ def define_input_struct(
127
+ python_type: _tp.Type[STRUCT_TYPE], *,
128
+ label: _tp.Optional[str] = None, optional: bool = False,
129
+ input_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) -> ModelInputSchema:
130
+
131
+ schema = define_struct(python_type)
132
+ return define_input(schema, label=label, optional=optional, input_props=input_props)
133
+
134
+ def define_output_struct(
135
+ python_type: _tp.Type[STRUCT_TYPE], *,
136
+ label: _tp.Optional[str] = None, optional: bool = False,
137
+ output_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) -> ModelOutputSchema:
138
+
139
+ schema = define_struct(python_type)
140
+ return define_output(schema, label=label, optional=optional, output_props=output_props)
141
+
142
+
111
143
  class FileType(_enum.Enum):
112
144
 
113
145
  FILE = 1
tracdap/rt/api/hook.py CHANGED
@@ -116,11 +116,16 @@ class _StaticApiHook:
116
116
  @_abc.abstractmethod
117
117
  def define_schema(
118
118
  self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
119
- schema_type: _meta.SchemaType = _meta.SchemaType.TABLE) \
119
+ schema_type: _meta.SchemaType = _meta.SchemaType.TABLE, dynamic: bool = False) \
120
120
  -> _meta.SchemaDefinition:
121
121
 
122
122
  pass
123
123
 
124
+ @_abc.abstractmethod
125
+ def define_struct(self, python_type: type) -> _meta.SchemaDefinition:
126
+
127
+ pass
128
+
124
129
  @_abc.abstractmethod
125
130
  def load_schema(
126
131
  self, package: _tp.Union[_ts.ModuleType, str], schema_file: str,
@@ -131,22 +136,36 @@ class _StaticApiHook:
131
136
 
132
137
  @_abc.abstractmethod
133
138
  def infer_schema(self, dataset: _tp.Any) -> _meta.SchemaDefinition:
139
+
134
140
  pass
135
141
 
136
142
  @_abc.abstractmethod
137
- def define_input_table(
138
- self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
139
- label: _tp.Optional[str] = None, optional: bool = False, dynamic: bool = False,
143
+ def define_file_type(self, extension: str, mime_type: str) -> _meta.FileType:
144
+
145
+ pass
146
+
147
+ @_abc.abstractmethod
148
+ def define_input(
149
+ self, requirement: _tp.Union[_meta.SchemaDefinition, _meta.FileType], *,
150
+ label: _tp.Optional[str] = None,
151
+ optional: bool = False, dynamic: bool = False,
140
152
  input_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) \
141
153
  -> _meta.ModelInputSchema:
142
154
 
143
155
  pass
144
156
 
145
157
  @_abc.abstractmethod
146
- def define_output_table(
147
- self, *fields: _tp.Union[_meta.FieldSchema, _tp.List[_meta.FieldSchema]],
148
- label: _tp.Optional[str] = None, optional: bool = False, dynamic: bool = False,
158
+ def define_output(
159
+ self, requirement: _tp.Union[_meta.SchemaDefinition, _meta.FileType], *,
160
+ label: _tp.Optional[str] = None,
161
+ optional: bool = False, dynamic: bool = False,
149
162
  output_props: _tp.Optional[_tp.Dict[str, _tp.Any]] = None) \
150
163
  -> _meta.ModelOutputSchema:
151
164
 
152
165
  pass
166
+
167
+ X = _tp.TypeVar("X")
168
+
169
+ def do_x(x: type[X]) -> X:
170
+
171
+ return x.__new__(x)
@@ -194,6 +194,14 @@ class TracContext(metaclass=_abc.ABCMeta):
194
194
 
195
195
  pass
196
196
 
197
+ def get_file(self, file_name: str) -> bytes:
198
+
199
+ pass
200
+
201
+ def get_file_stream(self, file_name: str) -> _tp.ContextManager[_tp.BinaryIO]:
202
+
203
+ pass
204
+
197
205
  def put_schema(self, dataset_name: str, schema: SchemaDefinition):
198
206
 
199
207
  """
@@ -283,6 +291,14 @@ class TracContext(metaclass=_abc.ABCMeta):
283
291
 
284
292
  pass
285
293
 
294
+ def put_file(self, file_name: str, file_content: _tp.Union[bytes, bytearray]):
295
+
296
+ pass
297
+
298
+ def put_file_stream(self, file_name: str) -> _tp.ContextManager[_tp.BinaryIO]:
299
+
300
+ pass
301
+
286
302
  def log(self) -> _logging.Logger:
287
303
 
288
304
  """