tracdap-runtime 0.7.0rc1__tar.gz → 0.8.0__tar.gz

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 (142) hide show
  1. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/PKG-INFO +23 -17
  2. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/README.md +8 -6
  3. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/setup.cfg +15 -12
  4. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/config_parser.py +61 -36
  5. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/data.py +136 -32
  6. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core/logging.py +195 -0
  7. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/models.py +15 -12
  8. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/repos.py +12 -3
  9. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/schemas.py +5 -5
  10. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/shim.py +5 -4
  11. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/storage.py +21 -10
  12. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core/struct.py +547 -0
  13. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/type_system.py +73 -33
  14. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/util.py +1 -111
  15. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/validation.py +99 -31
  16. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/actors.py +12 -14
  17. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/context.py +228 -82
  18. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/dev_mode.py +176 -89
  19. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/engine.py +230 -105
  20. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/functions.py +191 -100
  21. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/graph.py +24 -36
  22. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/graph_builder.py +252 -115
  23. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/__init__.py +14 -0
  24. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/codec.py +1 -1
  25. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc}/server.py +7 -6
  26. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +3 -3
  27. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +1 -1
  28. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +1 -1
  29. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +40 -0
  30. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.pyi +62 -0
  31. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +1 -1
  32. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +63 -0
  33. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +48 -2
  34. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +4 -2
  35. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +8 -0
  36. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +1 -1
  37. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +130 -0
  38. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +16 -2
  39. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +67 -0
  40. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +14 -4
  41. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +4 -4
  42. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +6 -0
  43. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +9 -7
  44. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +12 -4
  45. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +42 -0
  46. tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.pyi +56 -0
  47. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +1 -1
  48. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/stoarge_pb2.py → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.py +4 -4
  49. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +1 -1
  50. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +1 -1
  51. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +1 -1
  52. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec → tracdap_runtime-0.8.0/src/tracdap/rt/_impl}/runtime.py +32 -18
  53. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/static_api.py +66 -38
  54. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/format_csv.py +1 -1
  55. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/repo_git.py +56 -11
  56. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_sql.py +13 -6
  57. tracdap_runtime-0.8.0/src/tracdap/rt/_version.py +16 -0
  58. tracdap_runtime-0.8.0/src/tracdap/rt/api/__init__.py +29 -0
  59. tracdap_runtime-0.7.0rc1/src/tracdap/rt/api/__init__.py → tracdap_runtime-0.8.0/src/tracdap/rt/api/constants.py +24 -15
  60. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/api/experimental.py +32 -0
  61. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/api/hook.py +26 -7
  62. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/api/model_api.py +16 -0
  63. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/api/static_api.py +265 -127
  64. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/__init__.py +11 -11
  65. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/common.py +2 -26
  66. tracdap_runtime-0.8.0/src/tracdap/rt/config/dynamic.py +28 -0
  67. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/platform.py +17 -31
  68. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/runtime.py +2 -0
  69. tracdap_runtime-0.8.0/src/tracdap/rt/ext/__init__.py +14 -0
  70. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/ext/embed.py +2 -2
  71. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/ext/plugins.py +3 -3
  72. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/launch/launch.py +12 -14
  73. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/__init__.py +31 -21
  74. tracdap_runtime-0.8.0/src/tracdap/rt/metadata/config.py +95 -0
  75. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/data.py +40 -0
  76. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/file.py +10 -0
  77. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/job.py +16 -0
  78. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/model.py +12 -2
  79. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/object.py +9 -1
  80. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/object_id.py +6 -0
  81. tracdap_runtime-0.8.0/src/tracdap/rt/metadata/resource.py +63 -0
  82. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/tracdap_runtime.egg-info/PKG-INFO +23 -17
  83. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/tracdap_runtime.egg-info/SOURCES.txt +32 -24
  84. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/tracdap_runtime.egg-info/requires.txt +12 -8
  85. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_exec/__init__.py +0 -0
  86. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +0 -51
  87. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +0 -128
  88. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +0 -65
  89. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +0 -29
  90. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.pyi +0 -16
  91. tracdap_runtime-0.7.0rc1/src/tracdap/rt/_version.py +0 -16
  92. tracdap_runtime-0.7.0rc1/src/tracdap/rt/metadata/resource.py +0 -23
  93. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/LICENSE +0 -0
  94. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/pyproject.toml +0 -0
  95. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/__init__.py +0 -0
  96. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/__init__.py +0 -0
  97. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/ext → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/__init__.py +0 -0
  98. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/core}/guard_rails.py +0 -0
  99. {tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc → tracdap_runtime-0.8.0/src/tracdap/rt/_impl/exec}/__init__.py +0 -0
  100. {tracdap_runtime-0.7.0rc1/src/tracdap/rt → tracdap_runtime-0.8.0/src/tracdap/rt/_impl}/ext/__init__.py +0 -0
  101. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/ext/sql.py +0 -0
  102. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/ext/storage.py +0 -0
  103. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +0 -0
  104. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.pyi +0 -0
  105. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.pyi +0 -0
  106. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.pyi +0 -0
  107. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.pyi +0 -0
  108. /tracdap_runtime-0.7.0rc1/src/tracdap/rt/_impl/grpc/tracdap/metadata/stoarge_pb2.pyi → /tracdap_runtime-0.8.0/src/tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.pyi +0 -0
  109. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.pyi +0 -0
  110. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.pyi +0 -0
  111. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +0 -0
  112. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/__init__.py +0 -0
  113. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/_helpers.py +0 -0
  114. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/config_local.py +0 -0
  115. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/format_arrow.py +0 -0
  116. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/format_parquet.py +0 -0
  117. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/repo_local.py +0 -0
  118. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/repo_pypi.py +0 -0
  119. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_aws.py +0 -0
  120. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_azure.py +0 -0
  121. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_gcp.py +0 -0
  122. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_local.py +0 -0
  123. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/_plugins/storage_sql_dialects.py +0 -0
  124. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/job.py +0 -0
  125. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/config/result.py +0 -0
  126. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/exceptions.py +0 -0
  127. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/ext/config.py +0 -0
  128. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/ext/repos.py +0 -0
  129. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/ext/storage.py +0 -0
  130. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/launch/__init__.py +0 -0
  131. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/launch/__main__.py +0 -0
  132. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/launch/cli.py +0 -0
  133. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/common.py +0 -0
  134. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/custom.py +0 -0
  135. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/flow.py +0 -0
  136. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/search.py +0 -0
  137. /tracdap_runtime-0.7.0rc1/src/tracdap/rt/metadata/stoarge.py → /tracdap_runtime-0.8.0/src/tracdap/rt/metadata/storage.py +0 -0
  138. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/tag.py +0 -0
  139. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/tag_update.py +0 -0
  140. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/src/tracdap/rt/metadata/type.py +0 -0
  141. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/tracdap_runtime.egg-info/dependency_links.txt +0 -0
  142. {tracdap_runtime-0.7.0rc1 → tracdap_runtime-0.8.0}/tracdap_runtime.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: tracdap-runtime
3
- Version: 0.7.0rc1
3
+ Version: 0.8.0
4
4
  Summary: Runtime package for building models on the TRAC Data & Analytics Platform
5
5
  Home-page: https://tracdap.finos.org/
6
6
  Author: Martin Traverse
@@ -13,23 +13,26 @@ Platform: any
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Operating System :: OS Independent
16
- Requires-Python: <3.13,>=3.8
16
+ Requires-Python: <3.14,>=3.9
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: protobuf==5.28.2
20
- Requires-Dist: pyarrow==16.1.0
19
+ Requires-Dist: protobuf==5.29.3
20
+ Requires-Dist: pyarrow==18.1.0
21
21
  Requires-Dist: pyyaml==6.0.2
22
- Requires-Dist: dulwich==0.22.1
22
+ Requires-Dist: dulwich==0.22.7
23
23
  Requires-Dist: requests==2.32.3
24
- Requires-Dist: pandas<2.3.0,>=1.2.0
25
- Requires-Dist: numpy<2.0.0
24
+ Requires-Dist: typing_extensions<4.13
25
+ Requires-Dist: urllib3<2.4.0
26
26
  Provides-Extra: grpc
27
- Requires-Dist: grpcio==1.66.1; extra == "grpc"
28
- Requires-Dist: grpcio-status==1.66.1; extra == "grpc"
27
+ Requires-Dist: grpcio==1.70.0; extra == "grpc"
28
+ Requires-Dist: grpcio-status==1.70.0; extra == "grpc"
29
+ Provides-Extra: pandas
30
+ Requires-Dist: pandas<2.3.0,>=1.2.0; extra == "pandas"
31
+ Requires-Dist: numpy<2.3.0,>=1.20; extra == "pandas"
29
32
  Provides-Extra: polars
30
33
  Requires-Dist: polars<2.0.0,>=1.0.0; extra == "polars"
31
- Provides-Extra: spark
32
- Requires-Dist: pyspark<3.6.0,>=3.0.0; extra == "spark"
34
+ Provides-Extra: pyspark
35
+ Requires-Dist: pyspark<3.6.0,>=3.0.0; extra == "pyspark"
33
36
  Provides-Extra: sql
34
37
  Requires-Dist: sqlalchemy<2.1.0,>=2.0.0; extra == "sql"
35
38
  Provides-Extra: aws
@@ -45,6 +48,7 @@ Requires-Dist: azure-core==1.30.1; extra == "azure"
45
48
  Requires-Dist: azure-identity==1.16.1; extra == "azure"
46
49
  Requires-Dist: azure-storage-blob==12.19.1; extra == "azure"
47
50
  Requires-Dist: adlfs==2024.4.1; extra == "azure"
51
+ Dynamic: license-file
48
52
 
49
53
  # TRAC Model Runtime for Python
50
54
 
@@ -64,12 +68,14 @@ Documentation for the TRAC platform is available on our website at
64
68
 
65
69
  The TRAC runtime for Python has these requirements:
66
70
 
67
- * Python: 3.8 up to 3.12
68
- * Pandas: 1.2 up to 2.2
69
- * PySpark 3.0 up to 3.5 (optional)
71
+ * Python: 3.9 up to 3.13
72
+ * Pandas: 1.2 up to 2.2 (optional)
73
+ * NumPy: 1.2 up to 2.2 (optional, required by Pandas)
74
+ * Polars: 1.X (optional)
70
75
 
71
- 3rd party libraries may impose additional constraints on supported versions of Python, Pandas or PySpark.
72
- As of February 2024, the Python libraries for GCP do not yet support Python 3.12.
76
+ 3rd party libraries may impose additional constraints on supported versions of key libraries.
77
+ For example, Pandas 1.5 is not available for Python 3.12 or 3.13, while NumPy 2.0 is only
78
+ compatible with Pandas 2.1 and later.
73
79
 
74
80
  ## Installing the runtime
75
81
 
@@ -16,12 +16,14 @@ Documentation for the TRAC platform is available on our website at
16
16
 
17
17
  The TRAC runtime for Python has these requirements:
18
18
 
19
- * Python: 3.8 up to 3.12
20
- * Pandas: 1.2 up to 2.2
21
- * PySpark 3.0 up to 3.5 (optional)
22
-
23
- 3rd party libraries may impose additional constraints on supported versions of Python, Pandas or PySpark.
24
- As of February 2024, the Python libraries for GCP do not yet support Python 3.12.
19
+ * Python: 3.9 up to 3.13
20
+ * Pandas: 1.2 up to 2.2 (optional)
21
+ * NumPy: 1.2 up to 2.2 (optional, required by Pandas)
22
+ * Polars: 1.X (optional)
23
+
24
+ 3rd party libraries may impose additional constraints on supported versions of key libraries.
25
+ For example, Pandas 1.5 is not available for Python 3.12 or 3.13, while NumPy 2.0 is only
26
+ compatible with Pandas 2.1 and later.
25
27
 
26
28
  ## Installing the runtime
27
29
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = tracdap-runtime
3
- version = 0.7.0rc1
3
+ version = 0.8.0
4
4
  description = Runtime package for building models on the TRAC Data & Analytics Platform
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -26,8 +26,9 @@ packages =
26
26
  tracdap.rt.config
27
27
  tracdap.rt.launch
28
28
  tracdap.rt.ext
29
- tracdap.rt._exec
30
29
  tracdap.rt._impl
30
+ tracdap.rt._impl.core
31
+ tracdap.rt._impl.exec
31
32
  tracdap.rt._impl.ext
32
33
  tracdap.rt._impl.grpc
33
34
  tracdap.rt._impl.grpc.tracdap
@@ -37,24 +38,26 @@ packages =
37
38
  package_dir =
38
39
  tracdap = src/tracdap
39
40
  tracdap.rt = src/tracdap/rt
40
- python_requires = >= 3.8, < 3.13
41
+ python_requires = >= 3.9, < 3.14
41
42
  install_requires =
42
- protobuf == 5.28.2
43
- pyarrow == 16.1.0
43
+ protobuf == 5.29.3
44
+ pyarrow == 18.1.0
44
45
  pyyaml == 6.0.2
45
- dulwich == 0.22.1
46
+ dulwich == 0.22.7
46
47
  requests == 2.32.3
47
-
48
- pandas >= 1.2.0, < 2.3.0
49
- numpy < 2.0.0
48
+ typing_extensions < 4.13
49
+ urllib3 < 2.4.0
50
50
 
51
51
  [options.extras_require]
52
52
  grpc =
53
- grpcio == 1.66.1
54
- grpcio-status == 1.66.1
53
+ grpcio == 1.70.0
54
+ grpcio-status == 1.70.0
55
+ pandas =
56
+ pandas >= 1.2.0, < 2.3.0
57
+ numpy >= 1.20, < 2.3.0
55
58
  polars =
56
59
  polars >= 1.0.0, < 2.0.0
57
- spark =
60
+ pyspark =
58
61
  pyspark >= 3.0.0, < 3.6.0
59
62
  sql =
60
63
  sqlalchemy >= 2.0.0, < 2.1.0
@@ -13,8 +13,6 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from __future__ import annotations
17
-
18
16
  import dataclasses as _dc
19
17
  import decimal
20
18
  import enum
@@ -24,6 +22,7 @@ import json
24
22
  import os
25
23
  import pathlib
26
24
  import re
25
+ import types as ts
27
26
  import typing as tp
28
27
  import urllib.parse as _urlp
29
28
  import uuid
@@ -32,18 +31,24 @@ import tracdap.rt.config as _config
32
31
  import tracdap.rt.exceptions as _ex
33
32
  import tracdap.rt.ext.plugins as _plugins
34
33
  import tracdap.rt.ext.config as _config_ext
35
- import tracdap.rt._impl.util as _util
34
+ import tracdap.rt._impl.core.logging as _logging
35
+ import tracdap.rt._impl.core.util as _util
36
36
 
37
37
  import yaml
38
38
  import yaml.parser
39
39
 
40
+ try:
41
+ import pydantic as _pyd # noqa
42
+ except ModuleNotFoundError:
43
+ _pyd = None
44
+
40
45
  _T = tp.TypeVar('_T')
41
46
 
42
47
 
43
48
  class ConfigManager:
44
49
 
45
50
  @classmethod
46
- def for_root_config(cls, root_config_file: tp.Union[str, pathlib.Path, None]) -> ConfigManager:
51
+ def for_root_config(cls, root_config_file: tp.Union[str, pathlib.Path, None]) -> "ConfigManager":
47
52
 
48
53
  if isinstance(root_config_file, pathlib.Path):
49
54
  root_file_path = cls._resolve_scheme(root_config_file)
@@ -69,7 +74,7 @@ class ConfigManager:
69
74
  return ConfigManager(working_dir_url, None)
70
75
 
71
76
  @classmethod
72
- def for_root_dir(cls, root_config_dir: tp.Union[str, pathlib.Path]) -> ConfigManager:
77
+ def for_root_dir(cls, root_config_dir: tp.Union[str, pathlib.Path]) -> "ConfigManager":
73
78
 
74
79
  if isinstance(root_config_dir, pathlib.Path):
75
80
  root_dir_path = cls._resolve_scheme(root_config_dir)
@@ -103,7 +108,7 @@ class ConfigManager:
103
108
  return raw_url
104
109
 
105
110
  def __init__(self, root_dir_url: _urlp.ParseResult, root_file_url: tp.Optional[_urlp.ParseResult]):
106
- self._log = _util.logger_for_object(self)
111
+ self._log = _logging.logger_for_object(self)
107
112
  self._root_dir_url = root_dir_url
108
113
  self._root_file_url = root_file_url
109
114
 
@@ -279,9 +284,17 @@ class ConfigManager:
279
284
 
280
285
  class ConfigParser(tp.Generic[_T]):
281
286
 
282
- # The metaclass for generic types varies between versions of the typing library
283
- # To work around this, detect the correct metaclass by inspecting a generic type variable
284
- __generic_metaclass = type(tp.List[object])
287
+ # Support both new and old styles for generic, union and optional types
288
+ # Old-style annotations are still valid, even when the new style is fully supported
289
+ __generic_types: list[type] = [
290
+ ts.GenericAlias,
291
+ type(tp.List[int]),
292
+ type(tp.Optional[int])
293
+ ]
294
+
295
+ # UnionType was added to the types module in Python 3.10, we support 3.9 (Jan 2025)
296
+ if hasattr(ts, "UnionType"):
297
+ __generic_types.append(ts.UnionType)
285
298
 
286
299
  __primitive_types: tp.Dict[type, callable] = {
287
300
  bool: bool,
@@ -294,7 +307,7 @@ class ConfigParser(tp.Generic[_T]):
294
307
  }
295
308
 
296
309
  def __init__(self, config_class: _T.__class__, dev_mode_locations: tp.List[str] = None):
297
- self._log = _util.logger_for_object(self)
310
+ self._log = _logging.logger_for_object(self)
298
311
  self._config_class = config_class
299
312
  self._dev_mode_locations = dev_mode_locations or []
300
313
  self._errors = []
@@ -321,6 +334,23 @@ class ConfigParser(tp.Generic[_T]):
321
334
 
322
335
  def _parse_value(self, location: str, raw_value: tp.Any, annotation: type):
323
336
 
337
+ if self._is_dev_mode_location(location):
338
+
339
+ if type(raw_value) in ConfigParser.__primitive_types:
340
+ return self._parse_primitive(location, raw_value, type(raw_value))
341
+
342
+ if isinstance(raw_value, list):
343
+ if len(raw_value) == 0:
344
+ return []
345
+ items = iter((self._child_location(location, i), x) for i, x in enumerate(raw_value))
346
+ return list(self._parse_value(loc, x, tp.Any) for loc, x in items)
347
+
348
+ if isinstance(raw_value, dict):
349
+ if len(raw_value) == 0:
350
+ return {}
351
+ items = iter((self._child_location(location, k), k, v) for k, v in raw_value.items())
352
+ return dict((k, self._parse_value(loc, v, tp.Any)) for loc, k, v in items)
353
+
324
354
  if raw_value is None:
325
355
  return None
326
356
 
@@ -339,24 +369,17 @@ class ConfigParser(tp.Generic[_T]):
339
369
  return self._parse_enum(location, raw_value, annotation)
340
370
 
341
371
  if _dc.is_dataclass(annotation):
372
+ return self._parse_simple_class(location, raw_value, annotation)
342
373
 
343
- if isinstance(raw_value, tp.Dict):
344
- return self._parse_simple_class(location, raw_value, annotation)
374
+ # Basic support for Pydantic, if it is installed
375
+ if _pyd and isinstance(annotation, type) and issubclass(annotation, _pyd.BaseModel):
376
+ return self._parse_simple_class(location, raw_value, annotation)
345
377
 
346
- if self._is_dev_mode_location(location):
347
- if type(raw_value) in ConfigParser.__primitive_types:
348
- return self._parse_primitive(location, raw_value, type(raw_value))
349
- if isinstance(raw_value, list):
350
- if len(raw_value) == 0:
351
- return []
352
- list_type = type(raw_value[0])
353
- return list(map(lambda x: self._parse_primitive(location, x, list_type), raw_value))
354
-
355
- return self._error(location, f"Expected type {annotation.__name__}, got '{str(raw_value)}'")
356
-
357
- if isinstance(annotation, self.__generic_metaclass):
378
+ if any(map(lambda _t: isinstance(annotation, _t), self.__generic_types)):
358
379
  return self._parse_generic_class(location, raw_value, annotation) # noqa
359
380
 
381
+ return self._error(location, f"Cannot parse value of type {annotation.__name__}")
382
+
360
383
  def _is_dev_mode_location(self, location):
361
384
 
362
385
  return any(map(lambda pattern: re.match(pattern, location), self._dev_mode_locations))
@@ -416,14 +439,14 @@ class ConfigParser(tp.Generic[_T]):
416
439
  def _parse_simple_class(self, location: str, raw_dict: tp.Any, metaclass: type) -> object:
417
440
 
418
441
  if raw_dict is not None and not isinstance(raw_dict, dict):
419
- pass
442
+ return self._error(location, f"Expected type {metaclass.__name__}, got '{str(raw_dict)}'")
420
443
 
421
444
  obj = metaclass.__new__(metaclass, object()) # noqa
422
445
 
423
446
  init_signature = inspect.signature(metaclass.__init__)
424
447
  init_types = tp.get_type_hints(metaclass.__init__)
425
448
  init_params = iter(init_signature.parameters.items())
426
- init_values: tp.List[tp.Any] = list()
449
+ init_values: tp.Dict[str, tp.Any] = dict()
427
450
 
428
451
  # Do not process 'self'
429
452
  next(init_params)
@@ -437,20 +460,20 @@ class ConfigParser(tp.Generic[_T]):
437
460
  message = f"Class {metaclass.__name__} does not support config decoding: " + \
438
461
  f"Missing type information for init parameter '{param_name}'"
439
462
  self._error(location, message)
440
- init_values.append(None)
463
+ init_values[param_name] = None
441
464
 
442
465
  elif param_name in raw_dict and raw_dict[param_name] is not None:
443
466
  param_value = self._parse_value(param_location, raw_dict[param_name], param_type)
444
- init_values.append(param_value)
467
+ init_values[param_name] = param_value
445
468
 
446
469
  elif param.default != inspect._empty: # noqa
447
- init_values.append(param.default)
470
+ init_values[param_name] = param.default
448
471
 
449
472
  else:
450
473
  self._error(location, f"Missing required value '{param_name}'")
451
- init_values.append(None)
474
+ init_values[param_name] = None
452
475
 
453
- binding = init_signature.bind(obj, *init_values)
476
+ binding = init_signature.bind(obj, **init_values)
454
477
  metaclass.__init__(*binding.args, **binding.kwargs)
455
478
 
456
479
  # Now go back over the members and look for any that weren't declared in __init__
@@ -471,7 +494,7 @@ class ConfigParser(tp.Generic[_T]):
471
494
  self._error(location, message)
472
495
 
473
496
  # Generic members must be declared in __init__ since that is the only way to get the full annotation
474
- if isinstance(type(default_value), self.__generic_metaclass):
497
+ if any(map(lambda _t: isinstance(type(default_value), _t), self.__generic_types)):
475
498
  message = f"Class {metaclass.__name__} does not support config decoding: " + \
476
499
  f"Members with no default value must be declared in __init__: '{member_name}'"
477
500
  self._error(location, message)
@@ -497,7 +520,7 @@ class ConfigParser(tp.Generic[_T]):
497
520
 
498
521
  return obj
499
522
 
500
- def _parse_generic_class(self, location: str, raw_value: tp.Any, metaclass: __generic_metaclass):
523
+ def _parse_generic_class(self, location: str, raw_value: tp.Any, metaclass: type):
501
524
 
502
525
  origin = _util.get_origin(metaclass)
503
526
  args = _util.get_args(metaclass)
@@ -510,7 +533,7 @@ class ConfigParser(tp.Generic[_T]):
510
533
  return self._error(location, f"Expected a list, got {type(raw_value)}")
511
534
 
512
535
  return [
513
- self._parse_value(self._child_location(location, str(idx)), item, list_type)
536
+ self._parse_value(self._child_location(location, idx), item, list_type)
514
537
  for (idx, item) in enumerate(raw_value)]
515
538
 
516
539
  if origin == tp.Dict or origin == dict:
@@ -541,12 +564,14 @@ class ConfigParser(tp.Generic[_T]):
541
564
  return None
542
565
 
543
566
  @staticmethod
544
- def _child_location(parent_location: str, item: str):
567
+ def _child_location(parent_location: str, item: tp.Union[str, int]):
545
568
 
546
569
  if parent_location is None or parent_location == "":
547
570
  return item
571
+ elif isinstance(item, int):
572
+ return f"{parent_location}[{item}]"
548
573
  else:
549
- return parent_location + "." + item
574
+ return f"{parent_location}.{item}"
550
575
 
551
576
 
552
577
  class ConfigQuoter:
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import abc
17
+ import copy
17
18
  import dataclasses as dc
18
19
  import typing as tp
19
20
  import datetime as dt
@@ -36,17 +37,62 @@ except ModuleNotFoundError:
36
37
  import tracdap.rt.api.experimental as _api
37
38
  import tracdap.rt.metadata as _meta
38
39
  import tracdap.rt.exceptions as _ex
39
- import tracdap.rt._impl.util as _util
40
+ import tracdap.rt._impl.core.logging as _log
40
41
 
41
42
 
42
43
  @dc.dataclass(frozen=True)
43
44
  class DataSpec:
44
45
 
46
+ object_type: _meta.ObjectType
47
+ schema_type: _meta.SchemaType
45
48
  data_item: str
49
+
46
50
  data_def: _meta.DataDefinition
51
+ file_def: _meta.FileDefinition
47
52
  storage_def: _meta.StorageDefinition
48
53
  schema_def: tp.Optional[_meta.SchemaDefinition]
49
54
 
55
+ @staticmethod
56
+ def create_data_spec(
57
+ data_item: str,
58
+ data_def: _meta.DataDefinition,
59
+ storage_def: _meta.StorageDefinition,
60
+ schema_def: tp.Optional[_meta.SchemaDefinition] = None) -> "DataSpec":
61
+
62
+ if schema_def:
63
+ schema_type = schema_def.schemaType
64
+ elif data_def.schema:
65
+ schema_type = data_def.schema.schemaType
66
+ else:
67
+ schema_type = _meta.SchemaType.SCHEMA_TYPE_NOT_SET
68
+
69
+ return DataSpec(
70
+ _meta.ObjectType.DATA, schema_type, data_item,
71
+ data_def,
72
+ storage_def=storage_def,
73
+ schema_def=schema_def,
74
+ file_def=None)
75
+
76
+ @staticmethod
77
+ def create_file_spec(
78
+ data_item: str,
79
+ file_def: _meta.FileDefinition,
80
+ storage_def: _meta.StorageDefinition) -> "DataSpec":
81
+
82
+ return DataSpec(
83
+ _meta.ObjectType.FILE, _meta.SchemaType.SCHEMA_TYPE_NOT_SET, data_item,
84
+ file_def=file_def,
85
+ storage_def=storage_def,
86
+ data_def=None,
87
+ schema_def=None)
88
+
89
+ @staticmethod
90
+ def create_empty_spec(object_type: _meta.ObjectType, schema_type: _meta.SchemaType):
91
+ return DataSpec(object_type, schema_type, None, None, None, None, None)
92
+
93
+ def is_empty(self):
94
+ return self.data_item is None or len(self.data_item) == 0
95
+
50
96
 
51
97
  @dc.dataclass(frozen=True)
52
98
  class DataPartKey:
@@ -61,44 +107,104 @@ class DataPartKey:
61
107
  @dc.dataclass(frozen=True)
62
108
  class DataItem:
63
109
 
64
- schema: pa.Schema
65
- table: tp.Optional[pa.Table] = None
66
- batches: tp.Optional[tp.List[pa.RecordBatch]] = None
110
+ object_type: _meta.ObjectType
111
+ schema_type: _meta.SchemaType
112
+
113
+ content: tp.Any = None
114
+ content_type: tp.Type = None
115
+ content_func: tp.Callable[[], tp.Any] = None
67
116
 
68
- pandas: "tp.Optional[pandas.DataFrame]" = None
69
- pyspark: tp.Any = None
117
+ trac_schema: _meta.SchemaDefinition = None
118
+ native_schema: tp.Any = None
119
+
120
+ # TODO: Remove legacy API and use content / native_schema instead
121
+ schema: pa.Schema = None
122
+ table: tp.Optional[pa.Table] = None
70
123
 
71
124
  def is_empty(self) -> bool:
72
- return self.table is None and (self.batches is None or len(self.batches) == 0)
125
+ return self.content is None
126
+
127
+ @staticmethod
128
+ def create_empty(
129
+ object_type: _meta.ObjectType = _meta.ObjectType.DATA,
130
+ schema_type: _meta.SchemaType = _meta.SchemaType.TABLE) -> "DataItem":
131
+
132
+ if object_type == _meta.ObjectType.DATA and schema_type == _meta.SchemaType.TABLE:
133
+ return DataItem(_meta.ObjectType.DATA, _meta.SchemaType.TABLE, schema=pa.schema([]))
134
+ else:
135
+ return DataItem(object_type, schema_type)
73
136
 
74
137
  @staticmethod
75
- def create_empty() -> "DataItem":
76
- return DataItem(pa.schema([]))
138
+ def for_table(table: pa.Table, schema: pa.Schema, trac_schema: _meta.SchemaDefinition) -> "DataItem":
139
+
140
+ return DataItem(
141
+ _meta.ObjectType.DATA, _meta.SchemaType.TABLE,
142
+ content=table, content_type=pa.Table,
143
+ trac_schema=trac_schema, native_schema=schema,
144
+ table=table, schema=schema)
145
+
146
+ @staticmethod
147
+ def for_struct(content: tp.Any):
148
+
149
+ return DataItem(
150
+ _meta.ObjectType.DATA, _meta.SchemaType.STRUCT,
151
+ content=content, content_type=type(content))
152
+
153
+ @staticmethod
154
+ def for_file_content(content: bytes):
155
+
156
+ return DataItem(
157
+ _meta.ObjectType.FILE, _meta.SchemaType.SCHEMA_TYPE_NOT_SET,
158
+ content=content, content_type=bytes)
77
159
 
78
160
 
79
161
  @dc.dataclass(frozen=True)
80
162
  class DataView:
81
163
 
82
- trac_schema: _meta.SchemaDefinition
83
- arrow_schema: pa.Schema
164
+ object_type: _meta.ObjectType
84
165
 
85
- parts: tp.Dict[DataPartKey, tp.List[DataItem]]
166
+ trac_schema: _meta.SchemaDefinition = None
167
+ arrow_schema: pa.Schema = None
168
+
169
+ parts: tp.Dict[DataPartKey, tp.List[DataItem]] = None
170
+ file_item: tp.Optional[DataItem] = None
86
171
 
87
172
  @staticmethod
88
- def create_empty() -> "DataView":
89
- return DataView(_meta.SchemaDefinition(), pa.schema([]), dict())
173
+ def create_empty(object_type: _meta.ObjectType = _meta.ObjectType.DATA) -> "DataView":
174
+ if object_type == _meta.ObjectType.DATA:
175
+ return DataView(object_type, _meta.SchemaDefinition(), pa.schema([]), dict())
176
+ else:
177
+ return DataView(object_type)
90
178
 
91
179
  @staticmethod
92
180
  def for_trac_schema(trac_schema: _meta.SchemaDefinition):
93
- arrow_schema = DataMapping.trac_to_arrow_schema(trac_schema)
94
- return DataView(trac_schema, arrow_schema, dict())
181
+ if trac_schema.schemaType == _meta.SchemaType.TABLE:
182
+ arrow_schema = DataMapping.trac_to_arrow_schema(trac_schema)
183
+ return DataView(_meta.ObjectType.DATA, trac_schema, arrow_schema, dict())
184
+ else:
185
+ return DataView(_meta.ObjectType.DATA, trac_schema, parts = dict())
186
+
187
+ @staticmethod
188
+ def for_file_item(file_item: DataItem):
189
+ return DataView(file_item.object_type, file_item=file_item)
95
190
 
96
191
  def with_trac_schema(self, trac_schema: _meta.SchemaDefinition):
97
192
  arrow_schema = DataMapping.trac_to_arrow_schema(trac_schema)
98
- return DataView(trac_schema, arrow_schema, self.parts)
193
+ return DataView(_meta.ObjectType.DATA, trac_schema, arrow_schema, self.parts)
194
+
195
+ def with_part(self, part_key: DataPartKey, part: DataItem):
196
+ new_parts = copy.copy(self.parts)
197
+ new_parts[part_key] = [part]
198
+ return DataView(self.object_type, self.trac_schema, self.arrow_schema, new_parts)
199
+
200
+ def with_file_item(self, file_item: DataItem):
201
+ return DataView(self.object_type, file_item=file_item)
99
202
 
100
203
  def is_empty(self) -> bool:
101
- return self.parts is None or not any(self.parts.values())
204
+ if self.object_type == _meta.ObjectType.FILE:
205
+ return self.file_item is None
206
+ else:
207
+ return self.parts is None or not any(self.parts.values())
102
208
 
103
209
 
104
210
  class _DataInternal:
@@ -115,7 +221,7 @@ class DataMapping:
115
221
  :py:class:`TypeMapping <tracdap.rt.impl.type_system.MetadataCodec>`.
116
222
  """
117
223
 
118
- __log = _util.logger_for_namespace(_DataInternal.__module__ + ".DataMapping")
224
+ __log = _log.logger_for_namespace(_DataInternal.__module__ + ".DataMapping")
119
225
 
120
226
  # Matches TRAC_ARROW_TYPE_MAPPING in ArrowSchema, tracdap-lib-data
121
227
 
@@ -293,7 +399,7 @@ class DataMapping:
293
399
  deltas = [*prior_deltas, item]
294
400
  parts = {**view.parts, part: deltas}
295
401
 
296
- return DataView(view.trac_schema, view.arrow_schema, parts)
402
+ return DataView(view.object_type, view.trac_schema, view.arrow_schema, parts=parts)
297
403
 
298
404
  @classmethod
299
405
  def view_to_arrow(cls, view: DataView, part: DataPartKey) -> pa.Table:
@@ -308,29 +414,27 @@ class DataMapping:
308
414
  if not deltas:
309
415
  raise _ex.ETracInternal(f"Data view for part [{part.opaque_key}] does not contain any items")
310
416
 
417
+ # For a single delta, use the existing Arrow content
311
418
  if len(deltas) == 1:
312
419
  return cls.item_to_arrow(deltas[0])
313
420
 
314
- batches = {
421
+ # For multiple deltas, construct a new table by assembling the record batches
422
+ # Atm no consideration is given to overwriting records based on business key
423
+ batches = iter(
315
424
  batch
316
425
  for delta in deltas
317
- for batch in (
318
- delta.batches
319
- if delta.batches
320
- else delta.table.to_batches())}
426
+ for batch in cls.item_to_arrow(delta).to_batches())
321
427
 
322
428
  return pa.Table.from_batches(batches) # noqa
323
429
 
324
430
  @classmethod
325
431
  def item_to_arrow(cls, item: DataItem) -> pa.Table:
326
432
 
327
- if item.table is not None:
328
- return item.table
329
-
330
- if item.batches is not None:
331
- return pa.Table.from_batches(item.batches, item.schema) # noqa
433
+ if item.content_type != pa.Table:
434
+ detail = f"expected Arrow table, got [{item.content_type}]"
435
+ raise _ex.ETracInternal(f"Data item does not contain tabular data ({detail})")
332
436
 
333
- raise _ex.ETracInternal(f"Data item does not contain any usable data")
437
+ return item.content
334
438
 
335
439
  @classmethod
336
440
  def arrow_to_pandas(
@@ -642,7 +746,7 @@ class DataConformance:
642
746
  Check and/or apply conformance between datasets and schemas.
643
747
  """
644
748
 
645
- __log = _util.logger_for_namespace(_DataInternal.__module__ + ".DataConformance")
749
+ __log = _log.logger_for_namespace(_DataInternal.__module__ + ".DataConformance")
646
750
 
647
751
  __E_FIELD_MISSING = \
648
752
  "Field [{field_name}] is missing from the data"