tracdap-runtime 0.10.0.dev3__tar.gz → 0.10.0.dev4__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 (139) hide show
  1. {tracdap_runtime-0.10.0.dev3/tracdap_runtime.egg-info → tracdap_runtime-0.10.0.dev4}/PKG-INFO +1 -1
  2. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/setup.cfg +1 -1
  3. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/__init__.py +1 -1
  4. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/resources.py +24 -0
  5. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/validation.py +21 -8
  6. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/context.py +7 -17
  7. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/dev_mode.py +85 -49
  8. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/engine.py +9 -2
  9. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/functions.py +57 -32
  10. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/graph.py +14 -2
  11. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/graph_builder.py +58 -33
  12. tracdap_runtime-0.10.0.dev4/src/tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +73 -0
  13. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.pyi +17 -4
  14. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.py +16 -16
  15. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/model_pb2.pyi +4 -4
  16. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/static_api.py +1 -1
  17. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/__init__.py +1 -1
  18. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/flow.py +10 -0
  19. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/model.py +1 -1
  20. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4/tracdap_runtime.egg-info}/PKG-INFO +1 -1
  21. tracdap_runtime-0.10.0.dev3/src/tracdap/rt/_impl/grpc/tracdap/metadata/flow_pb2.py +0 -69
  22. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/LICENSE +0 -0
  23. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/README.md +0 -0
  24. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/pyproject.toml +0 -0
  25. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/__init__.py +0 -0
  26. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/__init__.py +0 -0
  27. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/config_parser.py +0 -0
  28. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/data.py +0 -0
  29. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/guard_rails.py +0 -0
  30. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/logging.py +0 -0
  31. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/models.py +0 -0
  32. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/plugins.py +0 -0
  33. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/repos.py +0 -0
  34. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/schemas.py +0 -0
  35. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/shim.py +0 -0
  36. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/storage.py +0 -0
  37. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/struct.py +0 -0
  38. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/type_system.py +0 -0
  39. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/core/util.py +0 -0
  40. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/__init__.py +0 -0
  41. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/exec/actors.py +0 -0
  42. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/ext/__init__.py +0 -0
  43. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/ext/sql.py +0 -0
  44. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/ext/storage.py +0 -0
  45. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/__init__.py +0 -0
  46. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/codec.py +0 -0
  47. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/server.py +0 -0
  48. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.py +0 -0
  49. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2.pyi +0 -0
  50. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/api/internal/runtime_pb2_grpc.py +0 -0
  51. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.py +0 -0
  52. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/common_pb2.pyi +0 -0
  53. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.py +0 -0
  54. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/config_pb2.pyi +0 -0
  55. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.py +0 -0
  56. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/custom_pb2.pyi +0 -0
  57. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.py +0 -0
  58. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/data_pb2.pyi +0 -0
  59. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.py +0 -0
  60. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/file_pb2.pyi +0 -0
  61. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.py +0 -0
  62. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/job_pb2.pyi +0 -0
  63. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.py +0 -0
  64. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_id_pb2.pyi +0 -0
  65. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.py +0 -0
  66. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/object_pb2.pyi +0 -0
  67. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.py +0 -0
  68. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/resource_pb2.pyi +0 -0
  69. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.py +0 -0
  70. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/search_pb2.pyi +0 -0
  71. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.py +0 -0
  72. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/storage_pb2.pyi +0 -0
  73. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.py +0 -0
  74. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_pb2.pyi +0 -0
  75. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.py +0 -0
  76. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/tag_update_pb2.pyi +0 -0
  77. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.py +0 -0
  78. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/grpc/tracdap/metadata/type_pb2.pyi +0 -0
  79. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_impl/runtime.py +0 -0
  80. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/__init__.py +0 -0
  81. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/_helpers.py +0 -0
  82. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/config_local.py +0 -0
  83. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/format_arrow.py +0 -0
  84. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/format_csv.py +0 -0
  85. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/format_parquet.py +0 -0
  86. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/repo_git.py +0 -0
  87. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/repo_local.py +0 -0
  88. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/repo_pypi.py +0 -0
  89. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_aws.py +0 -0
  90. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_azure.py +0 -0
  91. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_gcp.py +0 -0
  92. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_local.py +0 -0
  93. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_sql.py +0 -0
  94. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/_plugins/storage_sql_dialects.py +0 -0
  95. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/constants.py +0 -0
  96. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/experimental.py +0 -0
  97. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/hook.py +0 -0
  98. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/model_api.py +0 -0
  99. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/api/static_api.py +0 -0
  100. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/__init__.py +5 -5
  101. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/common.py +0 -0
  102. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/dynamic.py +0 -0
  103. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/job.py +0 -0
  104. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/platform.py +0 -0
  105. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/result.py +0 -0
  106. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/runtime.py +0 -0
  107. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/config/tenant.py +0 -0
  108. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/exceptions.py +0 -0
  109. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/__init__.py +0 -0
  110. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/config.py +0 -0
  111. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/embed.py +0 -0
  112. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/external.py +0 -0
  113. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/plugins.py +0 -0
  114. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/repos.py +0 -0
  115. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/storage.py +0 -0
  116. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/ext/util.py +0 -0
  117. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/launch/__init__.py +0 -0
  118. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/launch/__main__.py +0 -0
  119. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/launch/cli.py +0 -0
  120. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/launch/launch.py +0 -0
  121. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/__init__.py +20 -20
  122. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/common.py +0 -0
  123. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/config.py +0 -0
  124. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/custom.py +0 -0
  125. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/data.py +0 -0
  126. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/file.py +0 -0
  127. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/job.py +0 -0
  128. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/object.py +0 -0
  129. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/object_id.py +0 -0
  130. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/resource.py +0 -0
  131. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/search.py +0 -0
  132. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/storage.py +0 -0
  133. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/tag.py +0 -0
  134. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/tag_update.py +0 -0
  135. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/src/tracdap/rt/metadata/type.py +0 -0
  136. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/tracdap_runtime.egg-info/SOURCES.txt +0 -0
  137. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/tracdap_runtime.egg-info/dependency_links.txt +0 -0
  138. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/tracdap_runtime.egg-info/requires.txt +0 -0
  139. {tracdap_runtime-0.10.0.dev3 → tracdap_runtime-0.10.0.dev4}/tracdap_runtime.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tracdap-runtime
3
- Version: 0.10.0.dev3
3
+ Version: 0.10.0.dev4
4
4
  Summary: Portable, production-grade models in Python
5
5
  Home-page: https://www.fintrac.co.uk/
6
6
  Author: Martin Traverse
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = tracdap-runtime
3
- version = 0.10.0.dev3
3
+ version = 0.10.0.dev4
4
4
  license = Apache-2.0
5
5
  platform = any
6
6
  description = Portable, production-grade models in Python
@@ -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.10.0.dev3"
16
+ __version__ = "0.10.0.dev4"
@@ -86,3 +86,27 @@ class ResourceManager:
86
86
  raise _ex.ETracInternal(f"External system [{system_name}] is not available")
87
87
 
88
88
  return system
89
+
90
+
91
+ class ResourceMeta(type):
92
+
93
+ # Use a placeholder type for representing resources in the execution engine
94
+ # This allows resource objects to be defined and type checked during graph processing
95
+
96
+ __RESOURCE_TYPES = [_external.IExternalSystem]
97
+
98
+ def __instancecheck__(self, instance):
99
+ for resource_type in self.__RESOURCE_TYPES:
100
+ if isinstance(instance, resource_type):
101
+ return True
102
+ return False
103
+
104
+ def __subclasscheck__(self, subclass):
105
+ for resource_type in self.__RESOURCE_TYPES:
106
+ if issubclass(subclass, resource_type):
107
+ return True
108
+ return False
109
+
110
+
111
+ class Resource(metaclass=ResourceMeta):
112
+ pass
@@ -68,24 +68,39 @@ def is_primitive_type(basic_type: meta.BasicType) -> bool:
68
68
 
69
69
  T_PLUGIN = tp.TypeVar("T_PLUGIN")
70
70
 
71
+
71
72
  def plugin_validation_wrapper(plugin_api: tp.Type[T_PLUGIN], plugin: T_PLUGIN) -> T_PLUGIN:
72
73
 
73
74
  # Create a wrapper that validates return values from plugin API calls
74
- # A dynamic type would give better type similarity, but is more fiddly to set up
75
+ # Use ABC registration to pass isinstance() checks, only register each wrapper type once
75
76
  # Since plugins are not exposed directly to client code, this approach should suffice
76
77
 
78
+ if not hasattr(plugin_validation_wrapper, "__registrations"):
79
+ plugin_validation_wrapper.__registrations = {}
80
+
81
+ if plugin_api in plugin_validation_wrapper.__registrations:
82
+ wrapper_class = plugin_validation_wrapper.__registrations[plugin_api]
83
+ return wrapper_class(plugin)
84
+
77
85
  class PluginValidationWrapper:
78
86
 
79
87
  def __init__(self, delegate: T_PLUGIN):
80
88
  self.__delegate = delegate
89
+ self.__cache = dict()
81
90
 
82
91
  def __getattr__(self, name: str) -> tp.Any:
83
92
 
93
+ # Do not create a new wrapped method object on every call
94
+ cache_method = self.__cache.get(name)
95
+ if cache_method:
96
+ return cache_method
97
+
84
98
  # Use abstract method to validate (concrete plugin may have bad annotations)
85
99
  abstract_method = getattr(plugin_api, name)
86
100
  concrete_method = getattr(self.__delegate, name)
87
101
 
88
102
  if abstract_method is None or not callable(abstract_method):
103
+ self.__cache[name] = concrete_method
89
104
  return concrete_method
90
105
 
91
106
  def wrapped_method(*args, **kwargs):
@@ -100,13 +115,11 @@ def plugin_validation_wrapper(plugin_api: tp.Type[T_PLUGIN], plugin: T_PLUGIN) -
100
115
  message = f"Invalid plugin: [{util.qualified_type_name(type(plugin))}] {detail}"
101
116
  raise ex.EPluginConformance(message) from e
102
117
 
118
+ self.__cache[name] = wrapped_method
103
119
  return wrapped_method
104
120
 
105
- def __instancecheck__(self, instance: tp.Any) -> bool:
106
- return isinstance(instance, plugin_api)
107
-
108
- def __subclasscheck__(self, subclass: tp.Any) -> bool:
109
- return issubclass(subclass, self._interface)
121
+ plugin_validation_wrapper.__registrations[plugin_api] = PluginValidationWrapper
122
+ plugin_api.register(PluginValidationWrapper)
110
123
 
111
124
  return PluginValidationWrapper(plugin)
112
125
 
@@ -609,9 +622,9 @@ class StaticValidator:
609
622
  if resource.resourceType == meta.ResourceType.EXTERNAL_SYSTEM:
610
623
  if resource.protocol is None or len(resource.protocol) == 0:
611
624
  cls._fail(f"Invalid model resource: [{resource_name}] protocol is missing")
612
- if resource.system.client_type is None or len(resource.system.client_type) == 0:
625
+ if resource.system.clientType is None or len(resource.system.clientType) == 0:
613
626
  cls._fail(f"Invalid model resource: [{resource_name}] client type is missing")
614
- if not cls.__qualified_identifier_pattern.match(resource.system.client_type):
627
+ if not cls.__qualified_identifier_pattern.match(resource.system.clientType):
615
628
  cls._fail(f"Invalid model resource: [{resource_name}] client type is not a valid Python type")
616
629
 
617
630
  else:
@@ -122,9 +122,8 @@ class TracContextImpl(_api.TracContext):
122
122
 
123
123
  :param model_def: Definition object for the model that will run in this context
124
124
  :param model_class: Type for the model that will run in this context
125
- :param local_ctx: Dictionary of all parameters, inputs and outputs that will be available to the model
126
- Parameters are supplied as python native types.
127
- Output views will contain schemas but no data.
125
+ :param local_ctx: Dictionary of all parameters, inputs, outputs and resources that will be available to the model.
126
+ Parameters are supplied as python native types. Output views will contain schemas but no data.
128
127
  """
129
128
 
130
129
  def __init__(self,
@@ -132,7 +131,6 @@ class TracContextImpl(_api.TracContext):
132
131
  model_class: _api.TracModel.__class__,
133
132
  local_ctx: tp.Dict[str, tp.Any],
134
133
  dynamic_outputs: tp.List[str] = None,
135
- resources: tp.Dict[str, tp.Any] = None,
136
134
  log_provider: _logging.LogProvider = None,
137
135
  checkout_directory: pathlib.Path = None):
138
136
 
@@ -147,14 +145,12 @@ class TracContextImpl(_api.TracContext):
147
145
  self.__model_class = model_class
148
146
  self.__local_ctx = local_ctx if local_ctx is not None else {}
149
147
  self.__dynamic_outputs = dynamic_outputs if dynamic_outputs is not None else []
150
- self.__resources = resources if resources is not None else {}
151
148
  self.__rct = _util.RuntimeContextTracking()
152
149
 
153
150
  self.__val = TracContextValidator(
154
151
  self.__ctx_log,
155
152
  self.__model_def,
156
153
  self.__local_ctx,
157
- self.__resources,
158
154
  self.__dynamic_outputs,
159
155
  checkout_directory)
160
156
 
@@ -327,7 +323,7 @@ class TracContextImpl(_api.TracContext):
327
323
  self.__val.check_item_defined_in_model(system_name, TracContextValidator.SYSTEM)
328
324
  self.__val.check_item_available_in_context(system_name, TracContextValidator.SYSTEM)
329
325
 
330
- system = self.__resources.get(system_name)
326
+ system = self.__local_ctx.get(system_name)
331
327
 
332
328
  self.__val.check_context_object_type(system_name, system, TracExternalSystemWrapper)
333
329
  self.__val.check_external_system_client_type(system_name, system, client_type)
@@ -547,7 +543,7 @@ class TracDataContextImpl(TracContextImpl, _eapi.TracDataContext):
547
543
  storage_map: tp.Dict[str, tp.Union[_eapi.TracFileStorage, _eapi.TracDataStorage]],
548
544
  log_provider: _logging.LogProvider = None, checkout_directory: pathlib.Path = None):
549
545
 
550
- super().__init__(model_def, model_class, local_ctx, dynamic_outputs, storage_map, log_provider, checkout_directory)
546
+ super().__init__(model_def, model_class, local_ctx, dynamic_outputs, log_provider, checkout_directory)
551
547
 
552
548
  self.__model_def = model_def
553
549
  self.__local_ctx = local_ctx
@@ -939,7 +935,6 @@ class TracContextValidator(TracContextErrorReporter):
939
935
  self, log: logging.Logger,
940
936
  model_def: _meta.ModelDefinition,
941
937
  local_ctx: tp.Dict[str, tp.Any],
942
- resources: tp.Dict[str, tp.Any],
943
938
  dynamic_outputs: tp.List[str],
944
939
  checkout_directory: pathlib.Path):
945
940
 
@@ -947,7 +942,6 @@ class TracContextValidator(TracContextErrorReporter):
947
942
 
948
943
  self.__model_def = model_def
949
944
  self.__local_ctx = local_ctx
950
- self.__resources = resources
951
945
  self.__dynamic_outputs = dynamic_outputs
952
946
 
953
947
  def check_item_valid_identifier(self, item_name: str, item_type: str):
@@ -985,16 +979,12 @@ class TracContextValidator(TracContextErrorReporter):
985
979
 
986
980
  def check_item_available_in_context(self, item_name: str, item_type: str):
987
981
 
988
- ctx = self.__resources if item_type in [self.SYSTEM] else self.__local_ctx
989
-
990
- if item_name not in ctx:
982
+ if item_name not in self.__local_ctx:
991
983
  self._report_error(f"{item_type} {item_name} is not available in the current context")
992
984
 
993
985
  def check_item_not_available_in_context(self, item_name: str, item_type: str):
994
986
 
995
- ctx = self.__resources if item_type in [self.SYSTEM] else self.__local_ctx
996
-
997
- if item_name in ctx:
987
+ if item_name in self.__local_ctx:
998
988
  self._report_error(f"{item_type} {item_name} already exists in the current context")
999
989
 
1000
990
  def check_dataset_is_dynamic_output(self, dataset_name: str):
@@ -1161,7 +1151,7 @@ class TracContextValidator(TracContextErrorReporter):
1161
1151
  def check_external_system_client_type(self, system_name, system, client_type):
1162
1152
 
1163
1153
  model_system = self.__model_def.resources.get(system_name)
1164
- model_type_name = model_system.system.client_type if model_system and model_system.system else None
1154
+ model_type_name = model_system.system.clientType if model_system and model_system.system else None
1165
1155
  client_type_name = _util.qualified_type_name(client_type)
1166
1156
 
1167
1157
  if client_type_name != model_type_name:
@@ -551,7 +551,7 @@ class DevModeTranslator:
551
551
  errors[target_key] = f"Flow target {target_name} is not provided by any node"
552
552
 
553
553
  for node_name, node in flow.nodes.items():
554
- if node.nodeType == _meta.FlowNodeType.INPUT_NODE or node.nodeType == _meta.FlowNodeType.PARAMETER_NODE:
554
+ if node.nodeType in [_meta.FlowNodeType.INPUT_NODE, _meta.FlowNodeType.PARAMETER_NODE, _meta.FlowNodeType.RESOURCE_NODE]:
555
555
  add_source(node_name, _meta.FlowSocket(node_name))
556
556
  if node.nodeType == _meta.FlowNodeType.MODEL_NODE:
557
557
  for model_output in node.outputs:
@@ -574,6 +574,8 @@ class DevModeTranslator:
574
574
  add_edge(_meta.FlowSocket(node_name, model_input))
575
575
  for model_param in node.parameters:
576
576
  add_edge(_meta.FlowSocket(node_name, model_param))
577
+ for model_resource in node.resources:
578
+ add_edge(_meta.FlowSocket(node_name, model_resource))
577
579
 
578
580
  if any(errors):
579
581
 
@@ -613,6 +615,11 @@ class DevModeTranslator:
613
615
 
614
616
  for node_name, node in flow.nodes.items():
615
617
 
618
+ if node.nodeType == _meta.FlowNodeType.RESOURCE_NODE and node_name not in flow.resources:
619
+ targets = edges_by_source.get(node_name) or []
620
+ model_resource = cls._infer_resource(node_name, targets, job_def, job_config)
621
+ updated_flow.resources[node_name] = model_resource
622
+
616
623
  if node.nodeType == _meta.FlowNodeType.PARAMETER_NODE and node_name not in flow.parameters:
617
624
  targets = edges_by_source.get(node_name) or []
618
625
  model_parameter = cls._infer_parameter(node_name, targets, job_def, job_config)
@@ -631,28 +638,56 @@ class DevModeTranslator:
631
638
  return updated_flow
632
639
 
633
640
  @classmethod
634
- def _infer_parameter(
635
- cls, param_name: str, targets: tp.List[_meta.FlowSocket],
641
+ def _infer_resource(
642
+ cls, resource_name: str, targets: tp.List[_meta.FlowSocket],
636
643
  job_def: _meta.JobDefinition, job_config: _cfg.JobConfig) \
637
- -> _meta.ModelParameter:
644
+ -> _meta.ModelResource:
638
645
 
639
- model_params = []
646
+ model_resources = cls._infer_targets(
647
+ resource_name, targets, lambda model: model.resources,
648
+ job_def, job_config)
640
649
 
641
- for target in targets:
650
+ model_resource: _meta.ModelResource = model_resources[0]
642
651
 
643
- model_selector = job_def.runFlow.models.get(target.node)
644
- model_obj = _util.get_job_metadata(model_selector, job_config)
645
- model_param = model_obj.model.parameters.get(target.socket)
646
- model_params.append(model_param)
652
+ for i in range(1, len(targets)):
647
653
 
648
- if len(model_params) == 0:
649
- err = f"Flow parameter [{param_name}] is not connected to any models, type information cannot be inferred" \
650
- + f" (either remove the parameter or connect it to a model)"
651
- cls._log.error(err)
652
- raise _ex.EJobValidation(err)
654
+ next_resource: _meta.ModelResource = model_resources[i]
655
+
656
+ if next_resource.resourceType != model_resource.resourceType:
657
+ err = f"Resource is ambiguous for [{resource_name}]: " + \
658
+ f"Resource types are different for [{cls._socket_key(targets[0])}] and [{cls._socket_key(targets[i])}]"
659
+ raise _ex.EJobValidation(err)
660
+
661
+ if model_resource.protocol is None:
662
+ model_resource.protocol = next_resource.protocol
663
+ elif model_resource.protocol != next_resource.protocol:
664
+ err = f"Resource is ambiguous for [{resource_name}]: " + \
665
+ f"Protocols are different for [{cls._socket_key(targets[0])}] and [{cls._socket_key(targets[i])}]"
666
+ raise _ex.EJobValidation(err)
667
+
668
+ if model_resource.subProtocol is None:
669
+ model_resource.subProtocol = next_resource.subProtocol
670
+ elif model_resource.subProtocol != next_resource.subProtocol:
671
+ err = f"Resource is ambiguous for [{resource_name}]: " + \
672
+ f"Sub protocols are different for [{cls._socket_key(targets[0])}] and [{cls._socket_key(targets[i])}]"
673
+ raise _ex.EJobValidation(err)
674
+
675
+ # System details are informational, safe to remove if they are not a match
676
+ if model_resource.system is not None and next_resource.system is not None:
677
+ if model_resource.system != next_resource.system:
678
+ model_resource.system = None
679
+
680
+ return model_resource
681
+
682
+ @classmethod
683
+ def _infer_parameter(
684
+ cls, param_name: str, targets: tp.List[_meta.FlowSocket],
685
+ job_def: _meta.JobDefinition, job_config: _cfg.JobConfig) \
686
+ -> _meta.ModelParameter:
653
687
 
654
- if len(model_params) == 1:
655
- return model_params[0]
688
+ model_params = cls._infer_targets(
689
+ param_name, targets, lambda model: model.parameters,
690
+ job_def, job_config)
656
691
 
657
692
  model_param = model_params[0]
658
693
 
@@ -673,23 +708,9 @@ class DevModeTranslator:
673
708
  job_def: _meta.JobDefinition, job_config: _cfg.JobConfig) \
674
709
  -> _meta.ModelInputSchema:
675
710
 
676
- model_inputs = []
677
-
678
- for target in targets:
679
-
680
- model_selector = job_def.runFlow.models.get(target.node)
681
- model_obj = _util.get_job_metadata(model_selector, job_config)
682
- model_input = model_obj.model.inputs.get(target.socket)
683
- model_inputs.append(model_input)
684
-
685
- if len(model_inputs) == 0:
686
- err = f"Flow input [{input_name}] is not connected to any models, schema cannot be inferred" \
687
- + f" (either remove the input or connect it to a model)"
688
- cls._log.error(err)
689
- raise _ex.EJobValidation(err)
690
-
691
- if len(model_inputs) == 1:
692
- return model_inputs[0]
711
+ model_inputs = cls._infer_targets(
712
+ input_name, targets, lambda model: model.inputs,
713
+ job_def, job_config)
693
714
 
694
715
  model_input = model_inputs[0]
695
716
 
@@ -708,29 +729,44 @@ class DevModeTranslator:
708
729
  job_def: _meta.JobDefinition, job_config: _cfg.JobConfig) \
709
730
  -> _meta.ModelOutputSchema:
710
731
 
711
- model_outputs = []
712
-
713
- for source in sources:
714
-
715
- model_selector = job_def.runFlow.models.get(source.node)
716
- model_obj = _util.get_job_metadata(model_selector, job_config)
717
- model_input = model_obj.model.outputs.get(source.socket)
718
- model_outputs.append(model_input)
719
-
720
- if len(model_outputs) == 0:
721
- err = f"Flow output [{output_name}] is not connected to any models, schema cannot be inferred" \
722
- + f" (either remove the output or connect it to a model)"
723
- cls._log.error(err)
724
- raise _ex.EJobValidation(err)
732
+ # Lookup logic is the same for source and target sockets
733
+ model_outputs = cls._infer_targets(
734
+ output_name, sources, lambda model: model.outputs,
735
+ job_def, job_config)
725
736
 
726
737
  if len(model_outputs) > 1:
727
- err = f"Flow output [{output_name}] is not to multiple models" \
738
+ err = f"Flow output [{output_name}] is connected to multiple models" \
728
739
  + f" (only one model can supply one output)"
729
740
  cls._log.error(err)
730
741
  raise _ex.EJobValidation(err)
731
742
 
732
743
  return model_outputs[0]
733
744
 
745
+ @classmethod
746
+ def _infer_targets(
747
+ cls, node_name: str, targets, target_func: tp.Callable[[_meta.ModelDefinition], dict],
748
+ job_def: _meta.JobDefinition, job_config: _cfg.JobConfig):
749
+
750
+ # Common logic to look up inference targets
751
+
752
+ target_objects = []
753
+
754
+ for targets in targets:
755
+
756
+ model_selector = job_def.runFlow.models.get(targets.node)
757
+ model_obj = _util.get_job_metadata(model_selector, job_config)
758
+ target_map = target_func(model_obj.model)
759
+ target_obj = target_map.get(targets.socket)
760
+ target_objects.append(target_obj)
761
+
762
+ if len(target_objects) == 0:
763
+ err = f"Flow node [{node_name}] is not connected to any models, type inference is not possible" \
764
+ + f" (either remove this node or connect it to a model)"
765
+ cls._log.error(err)
766
+ raise _ex.EJobValidation(err)
767
+
768
+ return target_objects
769
+
734
770
  @classmethod
735
771
  def _socket_key(cls, socket):
736
772
  return f"{socket.node}.{socket.socket}" if socket.socket else socket.node
@@ -1190,6 +1190,7 @@ class NodeLogger:
1190
1190
  PUSH_POP = 2
1191
1191
  SIMPLE_MAPPING = 3
1192
1192
  MODEL = 4
1193
+ RESOURCE = 5
1193
1194
 
1194
1195
  def log_node_start(self, node: _EngineNode):
1195
1196
 
@@ -1203,6 +1204,9 @@ class NodeLogger:
1203
1204
  elif logging_type in [self.LoggingType.SIMPLE_MAPPING]:
1204
1205
  self._log.info(f"MAP {self._value_type(node)} [{self._mapping_source(node)}] -> [{node_name}] / {namespace}")
1205
1206
 
1207
+ elif logging_type == self.LoggingType.RESOURCE:
1208
+ self._log.info(f"USE {self._func_type(node)} [{node_name}] / {namespace}")
1209
+
1206
1210
  else:
1207
1211
  self._log.info(f"START {self._func_type(node)} [{node_name}] / {namespace}")
1208
1212
 
@@ -1212,7 +1216,7 @@ class NodeLogger:
1212
1216
  node_name = node.node.id.name
1213
1217
  namespace = node.node.id.namespace
1214
1218
 
1215
- if logging_type in [self.LoggingType.STATIC_VALUE, self.LoggingType.SIMPLE_MAPPING]:
1219
+ if logging_type in [self.LoggingType.STATIC_VALUE, self.LoggingType.SIMPLE_MAPPING, self.LoggingType.RESOURCE]:
1216
1220
  return
1217
1221
 
1218
1222
  if logging_type == self.LoggingType.PUSH_POP:
@@ -1237,7 +1241,7 @@ class NodeLogger:
1237
1241
  node_name = node.node.id.name
1238
1242
  namespace = node.node.id.namespace
1239
1243
 
1240
- if logging_type in [self.LoggingType.STATIC_VALUE, self.LoggingType.SIMPLE_MAPPING]:
1244
+ if logging_type in [self.LoggingType.STATIC_VALUE, self.LoggingType.SIMPLE_MAPPING, self.LoggingType.RESOURCE]:
1241
1245
  return
1242
1246
 
1243
1247
  self._log.info(f"EVICT {self._func_type(node)} [{node_name}] / {namespace}")
@@ -1276,6 +1280,9 @@ class NodeLogger:
1276
1280
  if isinstance(node.node, _graph.RunModelNode):
1277
1281
  return cls.LoggingType.MODEL
1278
1282
 
1283
+ if isinstance(node.node, _graph.ResourceNode):
1284
+ return cls.LoggingType.RESOURCE
1285
+
1279
1286
  return cls.LoggingType.DEFAULT
1280
1287
 
1281
1288
  @classmethod
@@ -33,6 +33,7 @@ import tracdap.rt._impl.core.storage as _storage
33
33
  import tracdap.rt._impl.core.struct as _struct
34
34
  import tracdap.rt._impl.core.models as _models
35
35
  import tracdap.rt._impl.core.util as _util
36
+ import tracdap.rt.ext.external as _ext_system
36
37
 
37
38
  from tracdap.rt._impl.exec.graph import *
38
39
  from tracdap.rt._impl.exec.graph import _T
@@ -213,6 +214,21 @@ class ContextPopFunc(_ContextPushPopFunc):
213
214
  super(ContextPopFunc, self).__init__(node, self._POP)
214
215
 
215
216
 
217
+ # RESOURCE PROVISION
218
+ # ------------------
219
+
220
+
221
+ class ResourceFunc(NodeFunction):
222
+
223
+ def __init__(self, node: ResourceNode, resource: _resources.Resource):
224
+ super().__init__()
225
+ self.node = node
226
+ self.resource = resource
227
+
228
+ def _execute(self, ctx: NodeContext) -> _resources.Resource:
229
+ return self.resource
230
+
231
+
216
232
  # DATA HANDLING
217
233
  # -------------
218
234
 
@@ -599,6 +615,8 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
599
615
  if node_id.namespace == self.node.id.namespace:
600
616
  if node_id.name in model_def.parameters or node_id.name in model_def.inputs:
601
617
  local_ctx[node_id.name] = node_result
618
+ elif node_id.name in model_def.resources:
619
+ local_ctx[node_id.name] = self._wrap_resource(node_id.name, node_result)
602
620
 
603
621
  # Run the model against the mapped local context
604
622
 
@@ -610,7 +628,7 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
610
628
  else:
611
629
  trac_ctx = _ctx.TracContextImpl(
612
630
  self.node.model_def, self.model_class,
613
- local_ctx, dynamic_outputs, self.resources,
631
+ local_ctx, dynamic_outputs,
614
632
  self.log_provider, self.checkout_directory)
615
633
 
616
634
  try:
@@ -648,6 +666,13 @@ class RunModelFunc(NodeFunction[Bundle[_data.DataView]]):
648
666
 
649
667
  return results
650
668
 
669
+ def _wrap_resource(self, resource_name, resource):
670
+
671
+ if isinstance(resource, _ext_system.IExternalSystem):
672
+ return _ctx.TracExternalSystemWrapper(resource)
673
+
674
+ raise _ex.EJobValidation(f"Unsupported resource [{resource_name}] of type [{type(resource).__name__}]")
675
+
651
676
 
652
677
  # RESULTS PROCESSING
653
678
  # ------------------
@@ -828,6 +853,20 @@ class FunctionResolver:
828
853
 
829
854
  return resolve_func(self, node)
830
855
 
856
+ def resolve_resource_node(self, node: ResourceNode):
857
+
858
+ if node.model_resource is not None:
859
+ resource_def = self._resources.get_resource_definition(node.resource_key)
860
+ self._check_model_resource(node.resource_key, node.model_resource, resource_def)
861
+
862
+ if node.resource_type == _meta.ResourceType.EXTERNAL_SYSTEM:
863
+ resource = self._resources.get_external_system(node.resource_key)
864
+ else:
865
+ # Other resource types not currently used as graph nodes
866
+ raise _ex.EUnexpected()
867
+
868
+ return ResourceFunc(node, resource)
869
+
831
870
  def resolve_load_data(self, node: LoadDataNode):
832
871
  return LoadDataFunc(node, self._resources.get_storage())
833
872
 
@@ -845,10 +884,10 @@ class FunctionResolver:
845
884
  model_class = model_loader.load_model_class(node.model_scope, node.model_def)
846
885
  checkout_directory = model_loader.model_load_checkout_directory(node.model_scope, node.model_def)
847
886
 
848
- # Prepare model resources - Missing resources will raise an error during the resolve phase
849
- resources = self._resolve_model_resources(node, checkout_directory, self._log_provider)
887
+ # Prepare external storage resources - Missing resources will raise an error during the resolve phase
888
+ storage_map = self._resolve_model_storage_resources(node, checkout_directory, self._log_provider)
850
889
 
851
- return RunModelFunc(node, model_class, resources, self._log_provider, checkout_directory)
890
+ return RunModelFunc(node, model_class, storage_map, self._log_provider, checkout_directory)
852
891
 
853
892
  __basic_node_mapping: tp.Dict[Node.__class__, NodeFunction.__class__] = {
854
893
 
@@ -869,33 +908,21 @@ class FunctionResolver:
869
908
 
870
909
  __node_mapping: tp.Dict[Node.__class__, tp.Callable[["FunctionResolver", Node], NodeFunction]] = {
871
910
 
911
+ ResourceNode: resolve_resource_node,
872
912
  LoadDataNode: resolve_load_data,
873
913
  SaveDataNode: resolve_save_data,
874
914
  RunModelNode: resolve_run_model_node,
875
915
  ImportModelNode: resolve_import_model_node
876
916
  }
877
917
 
878
- def _resolve_model_resources(
918
+ def _resolve_model_storage_resources(
879
919
  self, node: RunModelNode,
880
920
  checkout_directory, log_provider) \
881
921
  -> tp.Dict[str, tp.Any]:
882
922
 
883
- resources = {}
884
-
885
- for model_resource_key, model_resource in node.model_def.resources.items():
886
-
887
- resource_key = node.resource_mapping.get(model_resource_key)
888
- resource_def = self._resources.get_resource_definition(resource_key) if resource_key else None
889
-
890
- self._check_model_resource(
891
- model_resource_key, model_resource,
892
- resource_key, resource_def)
893
-
894
- if resource_def.resourceType == _meta.ResourceType.EXTERNAL_SYSTEM:
895
- system_impl = self._resources.get_external_system(resource_key)
896
- system = _ctx.TracExternalSystemWrapper(system_impl)
897
- resources[model_resource_key] = system
923
+ storage_map = {}
898
924
 
925
+ # TODO: Switch to resource nodes for external storage
899
926
  if node.storage_access:
900
927
 
901
928
  storage_manager = self._resources.get_storage()
@@ -905,7 +932,7 @@ class FunctionResolver:
905
932
  if storage_manager.has_file_storage(storage_key, external=True):
906
933
  storage_impl = storage_manager.get_file_storage(storage_key, external=True)
907
934
  storage = _ctx.TracFileStorageImpl(storage_key, storage_impl, write_access, checkout_directory, log_provider)
908
- resources[storage_key] = storage
935
+ storage_map[storage_key] = storage
909
936
  elif storage_manager.has_data_storage(storage_key, external=True):
910
937
  storage_impl = storage_manager.get_data_storage(storage_key, external=True)
911
938
  # This is a work-around until the storage extension API can be updated / unified
@@ -913,31 +940,29 @@ class FunctionResolver:
913
940
  raise _ex.EStorageConfig(f"External storage for [{storage_key}] is using the legacy storage framework]")
914
941
  converter = _data.DataConverter.noop()
915
942
  storage = _ctx.TracDataStorageImpl(storage_key, storage_impl, converter, write_access, checkout_directory, log_provider)
916
- resources[storage_key] = storage
943
+ storage_map[storage_key] = storage
917
944
  else:
918
945
  raise _ex.EStorageConfig(f"External storage is not available: [{storage_key}]")
919
946
 
920
- return resources
947
+ return storage_map
921
948
 
922
949
  @classmethod
923
950
  def _check_model_resource(
924
- cls, model_resource_key: str, model_resource: _meta.ModelResource,
925
- resource_key: str, resource_def: _meta.ResourceDefinition):
926
-
927
- if resource_key is None:
928
- raise _ex.EJobValidation(f"Resource [{model_resource_key}] is not available (missing resource mapping)")
951
+ cls, resource_key: str,
952
+ model_resource: _meta.ModelResource,
953
+ resource_def: _meta.ResourceDefinition):
929
954
 
930
955
  if resource_def is None:
931
- raise _ex.EJobValidation(f"Resource [{model_resource_key}] is not available (missing job resource [{resource_key}])")
956
+ raise _ex.EJobValidation(f"Resource [{resource_key}] is not available)")
932
957
 
933
958
  if model_resource.resourceType != resource_def.resourceType:
934
959
  detail = f"(expected {model_resource.resourceType}, got {resource_def.resourceType})"
935
- raise _ex.EJobValidation(f"Resource [{model_resource_key}] is not compatible with the model {detail}")
960
+ raise _ex.EJobValidation(f"Resource [{resource_key}] is not compatible with the model {detail}")
936
961
 
937
962
  if model_resource.protocol and model_resource.protocol != resource_def.protocol:
938
963
  detail = f"(expected protocol {model_resource.protocol}, got protocol {resource_def.protocol})"
939
- raise _ex.EJobValidation(f"Resource [{model_resource_key}] is not compatible with the model {detail}")
964
+ raise _ex.EJobValidation(f"Resource [{resource_key}] is not compatible with the model {detail}")
940
965
 
941
966
  if model_resource.subProtocol and model_resource.subProtocol != resource_def.subProtocol:
942
967
  detail = f"(expected sub-protocol {model_resource.protocol}, got sub-protocol {resource_def.protocol})"
943
- raise _ex.EJobValidation(f"Resource [{model_resource_key}] is not compatible with the model {detail}")
968
+ raise _ex.EJobValidation(f"Resource [{resource_key}] is not compatible with the model {detail}")