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.
- tracdap/rt/_impl/core/config_parser.py +29 -3
- tracdap/rt/_impl/core/data.py +627 -40
- tracdap/rt/_impl/core/repos.py +17 -8
- tracdap/rt/_impl/core/storage.py +25 -13
- tracdap/rt/_impl/core/struct.py +254 -60
- tracdap/rt/_impl/core/util.py +125 -11
- tracdap/rt/_impl/exec/context.py +35 -8
- tracdap/rt/_impl/exec/dev_mode.py +169 -127
- tracdap/rt/_impl/exec/engine.py +203 -140
- tracdap/rt/_impl/exec/functions.py +228 -263
- tracdap/rt/_impl/exec/graph.py +141 -126
- tracdap/rt/_impl/exec/graph_builder.py +428 -449
- tracdap/rt/_impl/grpc/codec.py +8 -13
- tracdap/rt/_impl/grpc/server.py +7 -7
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +25 -18
- tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +27 -9
- tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +37 -35
- tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +37 -43
- tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +67 -63
- tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +11 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.py +11 -9
- tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.pyi +11 -2
- tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +1 -1
- tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +23 -19
- tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +15 -2
- tracdap/rt/_impl/runtime.py +3 -9
- tracdap/rt/_impl/static_api.py +5 -6
- tracdap/rt/_plugins/format_csv.py +2 -2
- tracdap/rt/_plugins/repo_git.py +56 -11
- tracdap/rt/_plugins/storage_aws.py +165 -150
- tracdap/rt/_plugins/storage_azure.py +17 -11
- tracdap/rt/_plugins/storage_gcp.py +35 -18
- tracdap/rt/_version.py +1 -1
- tracdap/rt/api/model_api.py +45 -0
- tracdap/rt/config/__init__.py +7 -9
- tracdap/rt/config/common.py +3 -14
- tracdap/rt/config/job.py +17 -3
- tracdap/rt/config/platform.py +9 -32
- tracdap/rt/config/result.py +8 -4
- tracdap/rt/config/runtime.py +5 -10
- tracdap/rt/config/tenant.py +28 -0
- tracdap/rt/launch/cli.py +0 -8
- tracdap/rt/launch/launch.py +1 -3
- tracdap/rt/metadata/__init__.py +35 -35
- tracdap/rt/metadata/data.py +19 -31
- tracdap/rt/metadata/job.py +3 -1
- tracdap/rt/metadata/storage.py +9 -0
- tracdap/rt/metadata/type.py +9 -5
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/METADATA +5 -3
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/RECORD +64 -63
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/WHEEL +1 -1
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/licenses/LICENSE +0 -0
- {tracdap_runtime-0.8.0rc2.dist-info → tracdap_runtime-0.9.0b2.dist-info}/top_level.txt +0 -0
tracdap/rt/_impl/core/repos.py
CHANGED
@@ -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
|
38
|
+
for resource_key, resource in sys_config.resources.items():
|
39
|
+
if resource.resourceType == meta.ResourceType.MODEL_REPOSITORY:
|
38
40
|
|
39
|
-
|
41
|
+
try:
|
40
42
|
|
41
|
-
|
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
|
-
|
48
|
+
resource.properties.update(related_props)
|
44
49
|
|
45
|
-
|
46
|
-
+ " (this could indicate a missing model repository plugin)"
|
50
|
+
self._repos[resource_key] = plugins.PluginManager.load_plugin(IModelRepository, resource)
|
47
51
|
|
48
|
-
|
49
|
-
|
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
|
|
tracdap/rt/_impl/core/storage.py
CHANGED
@@ -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.
|
85
|
+
self.__sys_config = sys_config
|
85
86
|
|
86
|
-
|
87
|
-
|
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.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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.
|
100
|
+
return self.__default_location
|
97
101
|
|
98
102
|
def default_storage_format(self):
|
99
|
-
return self.
|
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
|
113
|
-
|
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
|
|
tracdap/rt/_impl/core/struct.py
CHANGED
@@ -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.
|
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 ==
|
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 ==
|
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.
|
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(
|
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
|
-
|
125
|
-
trac_fields = dict()
|
138
|
+
try:
|
126
139
|
|
127
|
-
|
140
|
+
type_stack.append(cls._qualified_type_name(python_type))
|
128
141
|
|
129
|
-
|
130
|
-
|
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
|
-
|
133
|
-
|
147
|
+
field_name = dc_field.name
|
148
|
+
python_type = type_hints[field_name]
|
134
149
|
|
135
|
-
|
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(
|
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
|
-
|
141
|
-
trac_fields = dict()
|
176
|
+
try:
|
142
177
|
|
143
|
-
|
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
|
-
|
187
|
+
python_type = type_hints[field_name]
|
146
188
|
|
147
|
-
|
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
|
-
|
150
|
-
trac_fields[field_name] = trac_field
|
205
|
+
finally:
|
151
206
|
|
152
|
-
|
207
|
+
type_stack.pop()
|
153
208
|
|
154
209
|
@classmethod
|
155
210
|
def _define_field(
|
156
|
-
cls, python_type: type, *,
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
168
|
-
|
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
|
-
|
171
|
-
|
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
|
-
|
181
|
-
|
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
|
-
|
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
|
188
|
-
|
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
|
191
|
-
|
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
|
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
|
-
|
199
|
-
|
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
|
206
|
-
optional_type = args[0]
|
207
|
-
return cls.
|
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
|
-
|
211
|
-
|
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
|
-
|
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()
|
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
|
|