tracdap-runtime 0.8.0rc2__py3-none-any.whl → 0.9.0b2__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 (64) hide show
  1. tracdap/rt/_impl/core/config_parser.py +29 -3
  2. tracdap/rt/_impl/core/data.py +627 -40
  3. tracdap/rt/_impl/core/repos.py +17 -8
  4. tracdap/rt/_impl/core/storage.py +25 -13
  5. tracdap/rt/_impl/core/struct.py +254 -60
  6. tracdap/rt/_impl/core/util.py +125 -11
  7. tracdap/rt/_impl/exec/context.py +35 -8
  8. tracdap/rt/_impl/exec/dev_mode.py +169 -127
  9. tracdap/rt/_impl/exec/engine.py +203 -140
  10. tracdap/rt/_impl/exec/functions.py +228 -263
  11. tracdap/rt/_impl/exec/graph.py +141 -126
  12. tracdap/rt/_impl/exec/graph_builder.py +428 -449
  13. tracdap/rt/_impl/grpc/codec.py +8 -13
  14. tracdap/rt/_impl/grpc/server.py +7 -7
  15. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +25 -18
  16. tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +27 -9
  17. tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
  18. tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +1 -1
  19. tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
  20. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +37 -35
  21. tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +37 -43
  22. tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +1 -1
  23. tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
  24. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +67 -63
  25. tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +11 -2
  26. tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +1 -1
  27. tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +1 -1
  28. tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +1 -1
  29. tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +1 -1
  30. tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
  31. tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.py +11 -9
  32. tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.pyi +11 -2
  33. tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +1 -1
  34. tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +1 -1
  35. tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +23 -19
  36. tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +15 -2
  37. tracdap/rt/_impl/runtime.py +3 -9
  38. tracdap/rt/_impl/static_api.py +5 -6
  39. tracdap/rt/_plugins/format_csv.py +2 -2
  40. tracdap/rt/_plugins/repo_git.py +56 -11
  41. tracdap/rt/_plugins/storage_aws.py +165 -150
  42. tracdap/rt/_plugins/storage_azure.py +17 -11
  43. tracdap/rt/_plugins/storage_gcp.py +35 -18
  44. tracdap/rt/_version.py +1 -1
  45. tracdap/rt/api/model_api.py +45 -0
  46. tracdap/rt/config/__init__.py +7 -9
  47. tracdap/rt/config/common.py +3 -14
  48. tracdap/rt/config/job.py +17 -3
  49. tracdap/rt/config/platform.py +9 -32
  50. tracdap/rt/config/result.py +8 -4
  51. tracdap/rt/config/runtime.py +5 -10
  52. tracdap/rt/config/tenant.py +28 -0
  53. tracdap/rt/launch/cli.py +0 -8
  54. tracdap/rt/launch/launch.py +1 -3
  55. tracdap/rt/metadata/__init__.py +35 -35
  56. tracdap/rt/metadata/data.py +19 -31
  57. tracdap/rt/metadata/job.py +3 -1
  58. tracdap/rt/metadata/storage.py +9 -0
  59. tracdap/rt/metadata/type.py +9 -5
  60. {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/METADATA +5 -3
  61. {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/RECORD +64 -63
  62. {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/WHEEL +1 -1
  63. {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/licenses/LICENSE +0 -0
  64. {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,7 @@ import typing as _tp
17
17
 
18
18
  import tracdap.rt.ext.plugins as plugins
19
19
  import tracdap.rt.config as cfg
20
+ import tracdap.rt.metadata as meta
20
21
  import tracdap.rt.exceptions as ex
21
22
  import tracdap.rt._impl.core.logging as _logging
22
23
 
@@ -34,19 +35,27 @@ class RepositoryManager:
34
35
  # Initialize all repos in the system config
35
36
  # Any errors for missing repo types (plugins) will be raised during startup
36
37
 
37
- for repo_name, repo_config in sys_config.repositories.items():
38
+ for resource_key, resource in sys_config.resources.items():
39
+ if resource.resourceType == meta.ResourceType.MODEL_REPOSITORY:
38
40
 
39
- try:
41
+ try:
40
42
 
41
- self._repos[repo_name] = plugins.PluginManager.load_plugin(IModelRepository, repo_config)
43
+ # Add global properties related to the repo protocol
44
+ related_props = {
45
+ k: v for (k, v) in sys_config.properties.items()
46
+ if k.startswith(f"{resource.protocol}.")}
42
47
 
43
- except ex.EPluginNotAvailable as e:
48
+ resource.properties.update(related_props)
44
49
 
45
- msg = f"Model repository type [{repo_config.protocol}] is not recognised" \
46
- + " (this could indicate a missing model repository plugin)"
50
+ self._repos[resource_key] = plugins.PluginManager.load_plugin(IModelRepository, resource)
47
51
 
48
- self._log.error(msg)
49
- raise ex.EStartup(msg) from e
52
+ except ex.EPluginNotAvailable as e:
53
+
54
+ msg = f"Model repository type [{resource.protocol}] is not recognised" \
55
+ + " (this could indicate a missing model repository plugin)"
56
+
57
+ self._log.error(msg)
58
+ raise ex.EStartup(msg) from e
50
59
 
51
60
  def get_repository(self, repo_name: str) -> IModelRepository:
52
61
 
@@ -29,6 +29,7 @@ import tracdap.rt.metadata as _meta
29
29
  import tracdap.rt.config as _cfg
30
30
  import tracdap.rt.exceptions as _ex
31
31
  import tracdap.rt.ext.plugins as plugins
32
+ import tracdap.rt._impl.core.config_parser as _cfg_p
32
33
  import tracdap.rt._impl.core.data as _data
33
34
  import tracdap.rt._impl.core.logging as _logging
34
35
  import tracdap.rt._impl.core.util as _util
@@ -81,25 +82,35 @@ class StorageManager:
81
82
  self.__file_storage: tp.Dict[str, IFileStorage] = dict()
82
83
  self.__data_storage: tp.Dict[str, IDataStorage] = dict()
83
84
  self.__external: tp.List[str] = list()
84
- self.__settings = sys_config.storage
85
+ self.__sys_config = sys_config
85
86
 
86
- for storage_key, storage_config in sys_config.storage.buckets.items():
87
- self.create_storage(storage_key, storage_config)
87
+ self.__default_location = _util.read_property(sys_config.properties, _cfg_p.ConfigKeys.STORAGE_DEFAULT_LOCATION)
88
+ self.__default_format = _util.read_property(sys_config.properties, _cfg_p.ConfigKeys.STORAGE_DEFAULT_FORMAT, "CSV")
88
89
 
89
- for storage_key, storage_config in sys_config.storage.external.items():
90
- if storage_key in self.__file_storage or storage_key in self.__data_storage:
91
- raise _ex.EConfig(f"Storage key [{storage_key}] is defined as both internal and external storage")
92
- self.__external.append(storage_key)
93
- self.create_storage(storage_key, storage_config)
90
+ for storage_key, storage_config in sys_config.resources.items():
91
+
92
+ if storage_config.resourceType == _meta.ResourceType.INTERNAL_STORAGE:
93
+ self.create_storage(storage_key, storage_config)
94
+
95
+ elif storage_config.resourceType == _meta.ResourceType.EXTERNAL_STORAGE:
96
+ self.__external.append(storage_key)
97
+ self.create_storage(storage_key, storage_config)
94
98
 
95
99
  def default_storage_key(self):
96
- return self.__settings.defaultBucket
100
+ return self.__default_location
97
101
 
98
102
  def default_storage_format(self):
99
- return self.__settings.defaultFormat
103
+ return self.__default_format
100
104
 
101
105
  def create_storage(self, storage_key: str, storage_config: _cfg.PluginConfig):
102
106
 
107
+ # Add global properties related to the storage protocol
108
+ related_props = {
109
+ k: v for (k, v) in self.__sys_config.properties.items()
110
+ if k.startswith(f"{storage_config.protocol}.")}
111
+
112
+ storage_config.properties.update(related_props)
113
+
103
114
  if plugins.PluginManager.is_plugin_available(IStorageProvider, storage_config.protocol):
104
115
  self._create_storage_from_provider(storage_key, storage_config)
105
116
  else:
@@ -109,11 +120,12 @@ class StorageManager:
109
120
 
110
121
  provider = plugins.PluginManager.load_plugin(IStorageProvider, storage_config)
111
122
 
112
- if provider.has_file_storage():
113
- file_storage = provider.get_file_storage()
114
- elif provider.has_arrow_native():
123
+ # Prefer arrow native FS if it is available
124
+ if provider.has_arrow_native():
115
125
  fs = provider.get_arrow_native()
116
126
  file_storage = CommonFileStorage(storage_key, storage_config, fs)
127
+ elif provider.has_file_storage():
128
+ file_storage = provider.get_file_storage()
117
129
  else:
118
130
  file_storage = None
119
131
 
@@ -37,6 +37,12 @@ import tracdap.rt._impl.core.type_system as _meta_types
37
37
 
38
38
  class StructProcessor:
39
39
 
40
+ JSON_FORMAT = "text/json"
41
+ JSON_ALT_FORMATS = ["json", ".json"]
42
+
43
+ YAML_FORMAT = "text/yaml"
44
+ YAML_ALT_FORMATS = ["yaml", ".yaml", "yml"]
45
+
40
46
  __primitive_types: dict[type, _meta.BasicType] = {
41
47
  bool: _meta.BasicType.BOOLEAN,
42
48
  int: _meta.BasicType.INTEGER,
@@ -68,13 +74,16 @@ class StructProcessor:
68
74
 
69
75
 
70
76
  @classmethod
71
- def define_struct(cls, python_type: type) -> _meta.StructSchema:
77
+ def define_struct(cls, python_type: type) -> _meta.SchemaDefinition:
78
+
79
+ named_types = dict()
80
+ named_enums = dict()
72
81
 
73
82
  if _dc.is_dataclass(python_type):
74
- return cls._define_struct_for_dataclass(python_type)
83
+ return cls._define_struct_for_dataclass(python_type, named_types, named_enums, type_stack=[])
75
84
 
76
85
  if _pyd and issubclass(python_type, _pyd.BaseModel):
77
- return cls._define_struct_for_pydantic(python_type)
86
+ return cls._define_struct_for_pydantic(python_type, named_types, named_enums, type_stack=[])
78
87
 
79
88
  raise _ex.EUnexpected()
80
89
 
@@ -83,10 +92,10 @@ class StructProcessor:
83
92
 
84
93
  try:
85
94
 
86
- if src_format == "yaml" or src_format == "yml":
95
+ if src_format == cls.YAML_FORMAT or src_format.lower() in cls.YAML_ALT_FORMATS:
87
96
  config_dict = _yaml.safe_load(src)
88
97
 
89
- elif src_format == "json":
98
+ elif src_format == cls.JSON_FORMAT or src_format.lower() in cls.JSON_ALT_FORMATS:
90
99
  config_dict = _json.load(src)
91
100
 
92
101
  else:
@@ -113,111 +122,297 @@ class StructProcessor:
113
122
  StructQuoter.quote(struct, dst, dst_format)
114
123
 
115
124
  @classmethod
116
- def parse_struct(cls, data: dict, schema: _meta.StructSchema = None, python_type: type = None) -> object:
125
+ def parse_struct(cls, data: dict, schema: _meta.SchemaDefinition = None, python_type: type = None) -> object:
117
126
 
118
127
  parser = StructParser()
119
128
  return parser.parse(python_type, data)
120
129
 
121
130
  @classmethod
122
- def _define_struct_for_dataclass(cls, python_type: _dc.dataclass) -> _meta.StructSchema:
131
+ def _define_struct_for_dataclass(
132
+ cls, python_type: _dc.dataclass,
133
+ named_types: _tp.Dict[str, _meta.SchemaDefinition],
134
+ named_enums: _tp.Dict[str, _meta.EnumValues],
135
+ type_stack: _tp.List[str]) \
136
+ -> _meta.SchemaDefinition:
123
137
 
124
- type_hints = _tp.get_type_hints(python_type)
125
- trac_fields = dict()
138
+ try:
126
139
 
127
- for dc_field in _dc.fields(python_type):
140
+ type_stack.append(cls._qualified_type_name(python_type))
128
141
 
129
- field_name = dc_field.name
130
- python_type = type_hints[field_name]
142
+ type_hints = _tp.get_type_hints(python_type)
143
+ trac_fields = list()
144
+
145
+ for field_index, dc_field in enumerate(_dc.fields(python_type)):
131
146
 
132
- trac_field = cls._define_field(python_type, dc_field=dc_field)
133
- trac_fields[field_name] = trac_field
147
+ field_name = dc_field.name
148
+ python_type = type_hints[field_name]
134
149
 
135
- return _meta.StructSchema(fields=trac_fields)
150
+ trac_field = cls._define_field(
151
+ field_name, field_index, python_type, dc_field=dc_field,
152
+ named_types=named_types, named_enums=named_enums, type_stack=type_stack)
153
+
154
+ trac_fields.append(trac_field)
155
+
156
+ if len(type_stack) == 1:
157
+ return _meta.SchemaDefinition(
158
+ schemaType=_meta.SchemaType.STRUCT_SCHEMA, fields=trac_fields,
159
+ namedTypes=named_types, namedEnums=named_enums)
160
+ else:
161
+ return _meta.SchemaDefinition(
162
+ schemaType=_meta.SchemaType.STRUCT_SCHEMA, fields=trac_fields)
163
+
164
+ finally:
165
+
166
+ type_stack.pop()
136
167
 
137
168
  @classmethod
138
- def _define_struct_for_pydantic(cls, python_type: "type[_pyd.BaseModel]") -> _meta.StructSchema:
169
+ def _define_struct_for_pydantic(
170
+ cls, python_type: "type[_pyd.BaseModel]",
171
+ named_types: _tp.Dict[str, _meta.SchemaDefinition],
172
+ named_enums: _tp.Dict[str, _meta.EnumValues],
173
+ type_stack: _tp.List[str]) \
174
+ -> _meta.SchemaDefinition:
139
175
 
140
- type_hints = _tp.get_type_hints(python_type)
141
- trac_fields = dict()
176
+ try:
142
177
 
143
- for field_name, pyd_field in python_type.model_fields.items():
178
+ type_stack.append(cls._qualified_type_name(python_type))
179
+
180
+ type_hints = _tp.get_type_hints(python_type)
181
+ trac_fields = list()
182
+
183
+ field_index = 0
184
+
185
+ for field_name, pyd_field in python_type.model_fields.items():
144
186
 
145
- python_type = type_hints[field_name]
187
+ python_type = type_hints[field_name]
146
188
 
147
- trac_field = cls._define_field(python_type, pyd_field=pyd_field)
189
+ trac_field = cls._define_field(
190
+ field_name, field_index, python_type, pyd_field=pyd_field,
191
+ named_types=named_types, named_enums=named_enums, type_stack=type_stack)
192
+
193
+ if trac_field is not None:
194
+ trac_fields.append(trac_field)
195
+ field_index += 1
196
+
197
+ if len(type_stack) == 1:
198
+ return _meta.SchemaDefinition(
199
+ schemaType=_meta.SchemaType.STRUCT_SCHEMA, fields=trac_fields,
200
+ namedTypes=named_types, namedEnums=named_enums)
201
+ else:
202
+ return _meta.SchemaDefinition(
203
+ schemaType=_meta.SchemaType.STRUCT_SCHEMA, fields=trac_fields)
148
204
 
149
- if trac_field is not None:
150
- trac_fields[field_name] = trac_field
205
+ finally:
151
206
 
152
- return _meta.StructSchema(fields=trac_fields)
207
+ type_stack.pop()
153
208
 
154
209
  @classmethod
155
210
  def _define_field(
156
- cls, python_type: type, *,
157
- dc_field: _dc.Field = None,
158
- pyd_field: "_pyd.fields.FieldInfo" = None) \
159
- -> _meta.StructField:
211
+ cls, name, index, python_type: type, optional=False, *,
212
+ named_types: _tp.Dict[str, _meta.SchemaDefinition],
213
+ named_enums: _tp.Dict[str, _meta.EnumValues],
214
+ type_stack: _tp.List[str],
215
+ dc_field: _dc.Field = None, pyd_field: "_pyd.fields.FieldInfo" = None) \
216
+ -> _meta.FieldSchema:
160
217
 
161
218
  if python_type in cls.__primitive_types:
162
- return cls._define_primitive_field(python_type, dc_field=dc_field, pyd_field=pyd_field)
219
+
220
+ return cls._define_primitive_field(
221
+ name, index, python_type, optional,
222
+ dc_field=dc_field, pyd_field=pyd_field)
163
223
 
164
224
  elif any(map(lambda _t: isinstance(python_type, _t), cls.__generic_types)):
165
- return cls._define_generic_field(python_type, pyd_field=pyd_field)
166
225
 
167
- elif dc_field is not None and _dc.is_dataclass(python_type):
168
- pass
226
+ return cls._define_generic_field(
227
+ name, index, python_type,
228
+ dc_field=dc_field, pyd_field=pyd_field,
229
+ named_types=named_types, named_enums=named_enums,
230
+ type_stack=type_stack)
231
+
232
+ elif isinstance(python_type, _enum.EnumMeta):
233
+
234
+ type_name = cls._qualified_type_name(python_type)
235
+
236
+ if type_name not in named_enums:
237
+ enum_values = cls._define_enum_values(python_type)
238
+ named_enums[type_name] = enum_values
239
+
240
+ return cls._define_enum_field(
241
+ name, index, python_type, optional,
242
+ dc_field=dc_field, pyd_field=pyd_field)
243
+
244
+ elif _dc.is_dataclass(python_type):
169
245
 
170
- elif pyd_field is not None and issubclass(python_type, _pyd.BaseModel):
171
- pass
246
+ type_name = cls._qualified_type_name(python_type)
247
+
248
+ if type_name in type_stack:
249
+ raise _ex.EValidation("Recursive types are not supported")
250
+
251
+ if type_name not in named_types:
252
+ struct_type = cls._define_struct_for_dataclass(python_type, named_types, named_enums, type_stack)
253
+ named_types[type_name] = struct_type
254
+
255
+ return _meta.FieldSchema(
256
+ fieldName=name,
257
+ fieldOrder=index,
258
+ fieldType=_meta.BasicType.STRUCT,
259
+ notNull=not optional,
260
+ namedType=type_name)
261
+
262
+ elif issubclass(python_type, _pyd.BaseModel):
263
+
264
+ type_name = cls._qualified_type_name(python_type)
265
+
266
+ if type_name in type_stack:
267
+ raise _ex.EValidation("Recursive types are not supported")
268
+
269
+ if type_name not in named_types:
270
+ struct_type = cls._define_struct_for_pydantic(python_type, named_types, named_enums, type_stack)
271
+ named_types[type_name] = struct_type
272
+
273
+ return _meta.FieldSchema(
274
+ fieldName=name,
275
+ fieldOrder=index,
276
+ fieldType=_meta.BasicType.STRUCT,
277
+ notNull=not optional,
278
+ namedType=type_name)
172
279
 
173
280
  else:
174
281
  raise _ex.ETracInternal("Cannot encode field type: " + str(python_type))
175
282
 
176
283
  @classmethod
177
284
  def _define_primitive_field(
178
- cls, python_type: type, optional=False, *,
179
- dc_field: _dc.Field = None,
180
- pyd_field: "_pyd.fields.FieldInfo" = None) \
181
- -> _meta.StructField:
285
+ cls, name: str, index: int, python_type: type, optional=False, *,
286
+ dc_field: _dc.Field = None, pyd_field: "_pyd.fields.FieldInfo" = None) \
287
+ -> _meta.FieldSchema:
288
+
289
+ default_value = None
290
+
291
+ if dc_field is not None:
292
+ if dc_field.default not in [_dc.MISSING, None]:
293
+ default_value = _meta_types.MetadataCodec.encode_value(dc_field.default)
294
+ elif dc_field.default_factory not in [_dc.MISSING, None]:
295
+ native_value = dc_field.default_factory()
296
+ default_value = _meta_types.MetadataCodec.encode_value(native_value)
297
+
298
+ elif pyd_field is not None:
299
+ if pyd_field.default not in [_pyd.fields.PydanticUndefined, None]:
300
+ default_value = _meta_types.MetadataCodec.encode_value(pyd_field.default)
301
+ elif pyd_field.default_factory not in [_pyd.fields.PydanticUndefined, None]:
302
+ native_value = pyd_field.default_factory()
303
+ default_value = _meta_types.MetadataCodec.encode_value(native_value)
304
+
305
+ return _meta.FieldSchema(
306
+ fieldName=name,
307
+ fieldOrder=index,
308
+ fieldType=cls.__primitive_types[python_type],
309
+ notNull=not optional,
310
+ defaultValue=default_value)
311
+
312
+ @classmethod
313
+ def _define_enum_field(
314
+ cls, name: str, index: int, enum_type: _enum.EnumMeta, optional=False, *,
315
+ dc_field: _dc.Field = None, pyd_field: "_pyd.fields.FieldInfo" = None) \
316
+ -> _meta.FieldSchema:
182
317
 
183
- struct_field = _meta.StructField()
184
- struct_field.fieldType = _meta.TypeDescriptor(basicType=cls.__primitive_types[python_type])
185
- struct_field.notNull = not optional
318
+ default_value = None
186
319
 
187
- if dc_field is not None and dc_field.default is not _dc.MISSING:
188
- struct_field.defaultValue = _meta_types.MetadataCodec.encode_value(dc_field.default)
320
+ if dc_field is not None and dc_field.default not in [_dc.MISSING, None]:
321
+ default_value = _meta_types.MetadataCodec.encode_value(dc_field.default.name)
189
322
 
190
- if pyd_field is not None and pyd_field.default is not _pyd.fields.PydanticUndefined:
191
- struct_field.defaultValue = _meta_types.MetadataCodec.encode_value(pyd_field.default)
323
+ if pyd_field is not None and pyd_field.default not in [_pyd.fields.PydanticUndefined, None]:
324
+ default_value = _meta_types.MetadataCodec.encode_value(pyd_field.default.name)
192
325
 
193
- return struct_field
326
+ return _meta.FieldSchema(
327
+ fieldName=name,
328
+ fieldOrder=index,
329
+ fieldType=_meta.BasicType.STRING,
330
+ categorical=True,
331
+ notNull=not optional,
332
+ namedEnum=cls._qualified_type_name(enum_type),
333
+ defaultValue=default_value)
334
+
335
+ @classmethod
336
+ def _define_enum_values(cls, enum_type: _enum.EnumMeta) -> _meta.EnumValues:
337
+
338
+ values = list(map(lambda value: value.name, enum_type))
339
+ return _meta.EnumValues(values=values)
194
340
 
195
341
  @classmethod
196
342
  def _define_generic_field(
197
- cls, python_type: type, *,
198
- dc_field: _dc.Field = None,
199
- pyd_field: "_pyd.fields.FieldInfo" = None) -> _meta.StructField:
343
+ cls, name, index, python_type: type, *,
344
+ named_types: _tp.Dict[str, _meta.SchemaDefinition],
345
+ named_enums: _tp.Dict[str, _meta.EnumValues],
346
+ type_stack: _tp.List[str],
347
+ dc_field: _dc.Field = None, pyd_field: "_pyd.fields.FieldInfo" = None) \
348
+ -> _meta.FieldSchema:
200
349
 
201
350
  origin = _tp.get_origin(python_type)
202
351
  args = _tp.get_args(python_type)
203
352
 
204
353
  # types.NoneType not available in Python 3.9, so use type(None) instead
205
- if origin in cls.__union_types and len(args) == 2 and args[1] is type(None):
206
- optional_type = args[0]
207
- return cls._define_primitive_field(optional_type, optional=True, dc_field=dc_field, pyd_field=pyd_field)
354
+ if origin in cls.__union_types and len(args) == 2 and type(None) in args:
355
+ optional_type = args[0] if args[1] is type(None) else args[1]
356
+ return cls._define_field(
357
+ name, index, optional_type, optional=True,
358
+ dc_field=dc_field, pyd_field=pyd_field,
359
+ named_types=named_types, named_enums=named_enums,
360
+ type_stack=type_stack)
208
361
 
209
362
  elif origin in [list, _tp.List]:
210
- list_type = args[0]
211
- pass
363
+
364
+ item_type = args[0]
365
+ item_field = cls._define_field(
366
+ "item", 0, item_type, optional=False,
367
+ named_types=named_types, named_enums=named_enums,
368
+ type_stack=type_stack)
369
+
370
+ return _meta.FieldSchema(
371
+ fieldName=name,
372
+ fieldOrder=index,
373
+ fieldType=_meta.BasicType.ARRAY,
374
+ notNull=True,
375
+ children=[item_field])
212
376
 
213
377
  elif origin in [dict, _tp.Dict]:
378
+
214
379
  key_type = args[0]
380
+ key_field = _meta.FieldSchema(
381
+ fieldName="key",
382
+ fieldOrder=0,
383
+ fieldType=_meta.BasicType.STRING,
384
+ notNull=True)
385
+
215
386
  value_type = args[1]
216
- pass
387
+ value_field = cls._define_field(
388
+ "value", 1, value_type, optional=False,
389
+ named_types=named_types, named_enums=named_enums,
390
+ type_stack=type_stack)
391
+
392
+ return _meta.FieldSchema(
393
+ fieldName=name,
394
+ fieldOrder=index,
395
+ fieldType=_meta.BasicType.MAP,
396
+ notNull=True,
397
+ children=[key_field, value_field])
217
398
 
218
399
  else:
219
400
  raise _ex.ETracInternal("Cannot encode field type: " + str(python_type))
220
401
 
402
+ @classmethod
403
+ def _qualified_type_name(cls, python_type: type):
404
+
405
+ name = python_type.__name__
406
+ module = python_type.__module__
407
+
408
+ if module.startswith(cls.__SHIM_PREFIX):
409
+ shim_root_index = module.index(".", len(cls.__SHIM_PREFIX)) + 1
410
+ module = module[shim_root_index:]
411
+
412
+ return f"{module}.{name}"
413
+
414
+ __SHIM_PREFIX = "tracdap.shim."
415
+
221
416
 
222
417
  class StructParser:
223
418
 
@@ -476,19 +671,18 @@ class StructQuoter:
476
671
  # New implementation of STRUCT quoting, copied from config_parser
477
672
  # After a period of stabilization, config_parser will switch to this implementation
478
673
 
479
- JSON_FORMAT = "json"
480
- YAML_FORMAT = "yaml"
481
674
  INDENT = 3
482
675
 
483
676
  @classmethod
484
677
  def quote(cls, obj: _tp.Any, dst: _tp.TextIO, dst_format: str):
485
678
 
486
- if dst_format.lower() == cls.JSON_FORMAT:
487
- return cls.quote_json(obj, dst)
488
679
 
489
- if dst_format.lower() == cls.YAML_FORMAT:
680
+ if dst_format == StructProcessor.YAML_FORMAT or dst_format.lower() in StructProcessor.YAML_ALT_FORMATS:
490
681
  return cls.quote_yaml(obj, dst)
491
682
 
683
+ if dst_format == StructProcessor.JSON_FORMAT or dst_format.lower() in StructProcessor.JSON_ALT_FORMATS:
684
+ return cls.quote_json(obj, dst)
685
+
492
686
  # TODO :This is probably an error in the user-supplied parameters
493
687
  raise _ex.ETracInternal(f"Unsupported output format [{dst_format}]")
494
688