salesforce-data-customcode 7.0.0rc4__tar.gz → 7.0.0rc5__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 (129) hide show
  1. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/PKG-INFO +13 -1
  2. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/README.md +12 -0
  3. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/pyproject.toml +1 -1
  4. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/client.py +19 -0
  5. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/deploy.py +8 -11
  6. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/writer/base.py +33 -1
  7. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/scan.py +26 -6
  8. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/LICENSE.txt +0 -0
  9. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/__init__.py +0 -0
  10. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/auth.py +0 -0
  11. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/cli.py +0 -0
  12. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/cmd.py +0 -0
  13. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/common_config.py +0 -0
  14. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/config.py +0 -0
  15. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/config.yaml +0 -0
  16. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/constants.py +0 -0
  17. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/credentials.py +0 -0
  18. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_platform_client.py +0 -0
  19. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_platform_config.py +0 -0
  20. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/__init__.py +0 -0
  21. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/base.py +0 -0
  22. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/errors.py +0 -0
  23. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/impl/default.py +0 -0
  24. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/spark_base.py +0 -0
  25. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/spark_default.py +0 -0
  26. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions/types.py +0 -0
  27. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/einstein_predictions_config.py +0 -0
  28. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/file/__init__.py +0 -0
  29. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/file/base.py +0 -0
  30. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/file/path/__init__.py +0 -0
  31. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/file/path/default.py +0 -0
  32. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function/__init__.py +0 -0
  33. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function/base.py +0 -0
  34. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function/feature_types/__init__.py +0 -0
  35. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function/feature_types/chunking.py +0 -0
  36. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function/runtime.py +0 -0
  37. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/function_utils.py +0 -0
  38. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/__init__.py +0 -0
  39. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/base.py +0 -0
  40. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/cdf.py +0 -0
  41. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/__init__.py +0 -0
  42. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/base.py +0 -0
  43. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/local_deltas.py +0 -0
  44. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/query_api.py +0 -0
  45. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/sf_cli.py +0 -0
  46. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/streaming_seeder.py +0 -0
  47. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/reader/utils.py +0 -0
  48. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/writer/__init__.py +0 -0
  49. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/writer/csv.py +0 -0
  50. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/writer/local_deltas.py +0 -0
  51. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/io/writer/print.py +0 -0
  52. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/lineage/__init__.py +0 -0
  53. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/lineage/_worker.py +0 -0
  54. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/lineage/capture.py +0 -0
  55. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/lineage/tracer.py +0 -0
  56. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/__init__.py +0 -0
  57. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/base.py +0 -0
  58. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/default.py +0 -0
  59. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/errors.py +0 -0
  60. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/spark_base.py +0 -0
  61. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/spark_default.py +0 -0
  62. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/types/__init__.py +0 -0
  63. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/types/generate_text_request.py +0 -0
  64. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/types/generate_text_request_builder.py +0 -0
  65. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/types/generate_text_response.py +0 -0
  66. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway/types/generate_text_response_builder.py +0 -0
  67. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/llm_gateway_config.py +0 -0
  68. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/mixin.py +0 -0
  69. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/__init__.py +0 -0
  70. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/base.py +0 -0
  71. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/default.py +0 -0
  72. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/direct/__init__.py +0 -0
  73. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/direct/auth.py +0 -0
  74. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/direct/credentials.py +0 -0
  75. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/direct/transport.py +0 -0
  76. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/direct/url_resolver.py +0 -0
  77. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/errors.py +0 -0
  78. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/spark_base.py +0 -0
  79. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/spark_default.py +0 -0
  80. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/__init__.py +0 -0
  81. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/http_method.py +0 -0
  82. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/http_request.py +0 -0
  83. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/http_request_builder.py +0 -0
  84. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/http_response.py +0 -0
  85. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential/types/http_response_builder.py +0 -0
  86. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/named_credential_config.py +0 -0
  87. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/py.typed +0 -0
  88. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/run.py +0 -0
  89. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/spark/__init__.py +0 -0
  90. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/spark/base.py +0 -0
  91. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/spark/column_hints.py +0 -0
  92. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/spark/default.py +0 -0
  93. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/template.py +0 -0
  94. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/__init__.py +0 -0
  95. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +0 -0
  96. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
  97. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/README.md +0 -0
  98. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/build_native_dependencies.sh +0 -0
  99. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/chunking/payload/config.json +0 -0
  100. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/chunking/payload/entrypoint.py +0 -0
  101. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/chunking/requirements.txt +0 -0
  102. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_llm/config.json +0 -0
  103. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +0 -0
  104. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt +0 -0
  105. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_llm/tests/test.json +0 -0
  106. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_prediction/config.json +0 -0
  107. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +0 -0
  108. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json +0 -0
  109. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/payload/config.json +0 -0
  110. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/payload/entrypoint.py +0 -0
  111. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/payload/utility.py +0 -0
  112. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
  113. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/function/requirements.txt +0 -0
  114. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +0 -0
  115. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/Dockerfile +0 -0
  116. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
  117. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/README.md +0 -0
  118. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/account.ipynb +0 -0
  119. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -0
  120. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
  121. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
  122. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py +0 -0
  123. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
  124. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/payload/config.json +0 -0
  125. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/payload/entrypoint.py +0 -0
  126. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
  127. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/templates/script/requirements.txt +0 -0
  128. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/token_provider.py +0 -0
  129. {salesforce_data_customcode-7.0.0rc4 → salesforce_data_customcode-7.0.0rc5}/src/datacustomcode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 7.0.0rc4
3
+ Version: 7.0.0rc5
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -183,6 +183,7 @@ For a streaming (delta) transform, use `StreamingClient`, which exposes the stre
183
183
  * `read_dlo_deltas()` – Read the streaming change feed (deltas) of a Data Lake Object as a streaming DataFrame.
184
184
  * `read_dmo_deltas()` – Read the streaming change feed (deltas) of a Data Model Object as a streaming DataFrame.
185
185
  * `write_dlo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Lake Object; returns the started `StreamingQuery`
186
+ * `write_dmo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Model Object; returns the started `StreamingQuery`
186
187
 
187
188
  `find_file_path`, `llm_gateway_generate_text`, and `einstein_predict` are available on both clients.
188
189
 
@@ -247,6 +248,15 @@ query = client.write_dlo_deltas("Output__dll", transformed)
247
248
  query.awaitTermination()
248
249
  ```
249
250
 
251
+ A streaming transform can also read and write Data Model Objects with `read_dmo_deltas()` and `write_dmo_deltas()`:
252
+
253
+ ```python
254
+ deltas = client.read_dmo_deltas()
255
+ transformed = deltas.withColumn("description__c", upper(col("description__c")))
256
+ query = client.write_dmo_deltas("Output__dlm", transformed)
257
+ query.awaitTermination()
258
+ ```
259
+
250
260
  Notes:
251
261
 
252
262
  - These methods only run inside the Data Cloud streaming (`DELTA_SYNC`) runtime. Locally (`datacustomcode run`) they raise `NotImplementedError`, since there is no change feed to stream.
@@ -408,6 +418,8 @@ Options:
408
418
  #### `datacustomcode zip`
409
419
  Zip a transformation job in preparation to upload to Data Cloud. Make sure to change directory into your code package folder (e.g., `my_package`) before running this command.
410
420
 
421
+ Symlinks inside the payload folder are not followed or packaged. Each symlinked file that is skipped is reported with a warning, and symlinked directories are not included. `datacustomcode deploy` zips the same way.
422
+
411
423
  Arguments:
412
424
  - `PATH`: Path to the code directory i.e. the payload folder (default: "payload")
413
425
 
@@ -158,6 +158,7 @@ For a streaming (delta) transform, use `StreamingClient`, which exposes the stre
158
158
  * `read_dlo_deltas()` – Read the streaming change feed (deltas) of a Data Lake Object as a streaming DataFrame.
159
159
  * `read_dmo_deltas()` – Read the streaming change feed (deltas) of a Data Model Object as a streaming DataFrame.
160
160
  * `write_dlo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Lake Object; returns the started `StreamingQuery`
161
+ * `write_dmo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Model Object; returns the started `StreamingQuery`
161
162
 
162
163
  `find_file_path`, `llm_gateway_generate_text`, and `einstein_predict` are available on both clients.
163
164
 
@@ -222,6 +223,15 @@ query = client.write_dlo_deltas("Output__dll", transformed)
222
223
  query.awaitTermination()
223
224
  ```
224
225
 
226
+ A streaming transform can also read and write Data Model Objects with `read_dmo_deltas()` and `write_dmo_deltas()`:
227
+
228
+ ```python
229
+ deltas = client.read_dmo_deltas()
230
+ transformed = deltas.withColumn("description__c", upper(col("description__c")))
231
+ query = client.write_dmo_deltas("Output__dlm", transformed)
232
+ query.awaitTermination()
233
+ ```
234
+
225
235
  Notes:
226
236
 
227
237
  - These methods only run inside the Data Cloud streaming (`DELTA_SYNC`) runtime. Locally (`datacustomcode run`) they raise `NotImplementedError`, since there is no change feed to stream.
@@ -383,6 +393,8 @@ Options:
383
393
  #### `datacustomcode zip`
384
394
  Zip a transformation job in preparation to upload to Data Cloud. Make sure to change directory into your code package folder (e.g., `my_package`) before running this command.
385
395
 
396
+ Symlinks inside the payload folder are not followed or packaged. Each symlinked file that is skipped is reported with a warning, and symlinked directories are not included. `datacustomcode deploy` zips the same way.
397
+
386
398
  Arguments:
387
399
  - `PATH`: Path to the code directory i.e. the payload folder (default: "payload")
388
400
 
@@ -18,7 +18,7 @@ license = "Apache-2.0"
18
18
  name = "salesforce-data-customcode"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.10,<3.12"
21
- version = "7.0.0rc4"
21
+ version = "7.0.0rc5"
22
22
 
23
23
  [tool.black]
24
24
  exclude = '''
@@ -726,6 +726,25 @@ class StreamingClient(_BaseClient):
726
726
  self._validate_data_layer_history_does_not_contain(DataCloudObjectType.DMO)
727
727
  return self._writer.write_dlo_deltas(name, dataframe, **kwargs) # type: ignore[no-any-return]
728
728
 
729
+ def write_dmo_deltas(
730
+ self, name: str, dataframe: PySparkDataFrame, **kwargs
731
+ ) -> StreamingQuery:
732
+ """Write a streaming DataFrame of deltas to a DMO in Data Cloud.
733
+
734
+ Starts a streaming query that writes each micro-batch to the
735
+ target DMO and returns the ``StreamingQuery`` handle; the caller
736
+ typically calls ``query.awaitTermination()``.
737
+
738
+ Args:
739
+ name: The name of the DMO to write to.
740
+ dataframe: The streaming PySpark DataFrame to write.
741
+
742
+ Returns:
743
+ The started ``StreamingQuery``.
744
+ """
745
+ self._validate_data_layer_history_does_not_contain(DataCloudObjectType.DLO)
746
+ return self._writer.write_dmo_deltas(name, dataframe, **kwargs) # type: ignore[no-any-return]
747
+
729
748
  def auto_write_to_dlo(self, name: str, dataframe: PySparkDataFrame) -> None:
730
749
  """Write a PySpark DataFrame to a DLO in Data Cloud automatically picking
731
750
  the WriteMode.
@@ -415,13 +415,7 @@ class DataTransformConfig(BaseConfig):
415
415
  def _validate_layers(self) -> "DataTransformConfig":
416
416
  read_is_dlo = isinstance(self.permissions.read, DloPermission)
417
417
  write_is_dlo = isinstance(self.permissions.write, DloPermission)
418
- if self.is_streaming:
419
- if not write_is_dlo:
420
- raise ValueError(
421
- "A streaming transform must write to a DLO "
422
- "(permissions.write must be a 'dlo' entry)."
423
- )
424
- elif read_is_dlo != write_is_dlo:
418
+ if read_is_dlo != write_is_dlo:
425
419
  raise ValueError(
426
420
  "permissions.read and permissions.write must both reference "
427
421
  "DLOs or both reference DMOs (got "
@@ -630,12 +624,15 @@ def zip(
630
624
  logger.debug(f"Zipping directory... {directory}")
631
625
 
632
626
  with zipfile.ZipFile(ZIP_FILE_NAME, "w", zipfile.ZIP_DEFLATED) as zipf:
633
- for root, dirs, files in os.walk(directory):
634
- # Skip .DS_Store and local credentials.
627
+ for root, dirs, files in os.walk(directory, followlinks=False):
628
+ # Skip symlinks, .DS_Store and local credentials.
635
629
  for file in files:
630
+ abs_path = os.path.join(root, file)
631
+ arcname = os.path.relpath(abs_path, directory)
632
+ if os.path.islink(abs_path):
633
+ logger.warning(f"Skipping symlink file {arcname}")
634
+ continue
636
635
  if file not in EXCLUDED_FILES:
637
- abs_path = os.path.join(root, file)
638
- arcname = os.path.relpath(abs_path, directory)
639
636
  zipf.write(abs_path, arcname)
640
637
 
641
638
  logger.debug(f"Created zip file: {ZIP_FILE_NAME}")
@@ -87,7 +87,7 @@ class BaseDataCloudWriter(BaseDataAccessLayer):
87
87
  Args:
88
88
  name: Target Data Lake Object name.
89
89
  dataframe: Streaming PySpark DataFrame produced from a
90
- ``read_dlo_deltas`` / ``read_dmo_deltas`` source.
90
+ ``read_dlo_deltas`` source.
91
91
 
92
92
  Returns:
93
93
  The started ``StreamingQuery``; the caller drives its lifecycle
@@ -102,3 +102,35 @@ class BaseDataCloudWriter(BaseDataAccessLayer):
102
102
  "streaming runtime; the local writer does not support streaming "
103
103
  "deltas."
104
104
  )
105
+
106
+ def write_dmo_deltas(
107
+ self, name: str, dataframe: PySparkDataFrame
108
+ ) -> StreamingQuery:
109
+ """Write a streaming DataFrame of deltas to a Data Model Object.
110
+
111
+ Streaming counterpart to :meth:`write_to_dmo`. Starts a streaming query
112
+ that writes each micro-batch to the target DMO via the Data Cloud
113
+ streaming sink and returns the resulting ``StreamingQuery`` handle. The
114
+ runtime owns the trigger and checkpoint location; callers pass only the
115
+ table name. Concrete streaming behavior is provided by the deployed
116
+ Data Cloud runtime; the base implementation raises
117
+ :class:`NotImplementedError`.
118
+
119
+ Args:
120
+ name: Target Data Model Object name.
121
+ dataframe: Streaming PySpark DataFrame produced from a
122
+ ``read_dmo_deltas`` source.
123
+
124
+ Returns:
125
+ The started ``StreamingQuery``; the caller drives its lifecycle
126
+ (typically ``query.awaitTermination()``).
127
+
128
+ Raises:
129
+ NotImplementedError: If the active writer does not support streaming
130
+ deltas (e.g. the local development writers).
131
+ """
132
+ raise NotImplementedError(
133
+ "write_dmo_deltas is only supported when running in the Data Cloud "
134
+ "streaming runtime; the local writer does not support streaming "
135
+ "deltas."
136
+ )
@@ -34,7 +34,7 @@ from datacustomcode.version import get_version
34
34
 
35
35
  DATA_ACCESS_METHODS = ["read_dlo", "read_dmo", "write_to_dlo", "write_to_dmo"]
36
36
  STREAMING_READ_METHODS = ["read_dlo_deltas", "read_dmo_deltas"]
37
- STREAMING_WRITE_METHODS = ["write_dlo_deltas"]
37
+ STREAMING_WRITE_METHODS = ["write_dlo_deltas", "write_dmo_deltas"]
38
38
 
39
39
  DATA_TRANSFORM_CONFIG_TEMPLATE = {
40
40
  "sdkVersion": get_version(),
@@ -181,6 +181,7 @@ class StreamingDataAccessLayerCalls(pydantic.BaseModel):
181
181
  read_dlo_deltas: bool
182
182
  read_dmo_deltas: bool
183
183
  write_dlo_deltas: frozenset[str]
184
+ write_dmo_deltas: frozenset[str]
184
185
 
185
186
  @pydantic.model_validator(mode="after")
186
187
  def validate_access_layer(self) -> StreamingDataAccessLayerCalls:
@@ -193,10 +194,19 @@ class StreamingDataAccessLayerCalls(pydantic.BaseModel):
193
194
  "A streaming transform must read from at least one DLO or DMO "
194
195
  "delta stream (read_dlo_deltas / read_dmo_deltas)."
195
196
  )
196
- if not self.write_dlo_deltas:
197
+ if not self.write_dlo_deltas and not self.write_dmo_deltas:
197
198
  raise ValueError(
198
- "A streaming transform must write to at least one DLO via "
199
- "write_dlo_deltas."
199
+ "A streaming transform must write to at least one DLO or DMO "
200
+ "via write_dlo_deltas / write_dmo_deltas."
201
+ )
202
+ if self.write_dlo_deltas and self.write_dmo_deltas:
203
+ raise ValueError(
204
+ "Cannot write DLO and DMO deltas in the same streaming transform."
205
+ )
206
+ if self.read_layer != self.write_layer:
207
+ raise ValueError(
208
+ "A streaming transform must read and write the same layer "
209
+ f"(got read={self.read_layer}, write={self.write_layer})."
200
210
  )
201
211
  return self
202
212
 
@@ -205,6 +215,11 @@ class StreamingDataAccessLayerCalls(pydantic.BaseModel):
205
215
  """Return the read source layer, ``"dlo"`` or ``"dmo"``."""
206
216
  return "dlo" if self.read_dlo_deltas else "dmo"
207
217
 
218
+ @property
219
+ def write_layer(self) -> str:
220
+ """Return the write target layer, ``"dlo"`` or ``"dmo"``."""
221
+ return "dlo" if self.write_dlo_deltas else "dmo"
222
+
208
223
 
209
224
  class ClientMethodVisitor(ast.NodeVisitor):
210
225
  """AST Visitor that finds all instances of Client read/write method calls."""
@@ -217,6 +232,7 @@ class ClientMethodVisitor(ast.NodeVisitor):
217
232
  self._read_dlo_deltas: bool = False
218
233
  self._read_dmo_deltas: bool = False
219
234
  self._write_dlo_deltas_instances: set[str] = set()
235
+ self._write_dmo_deltas_instances: set[str] = set()
220
236
  self.variable_values: Dict[str, Union[str, None]] = {}
221
237
 
222
238
  def visit_Assign(self, node: ast.Assign) -> None:
@@ -261,6 +277,8 @@ class ClientMethodVisitor(ast.NodeVisitor):
261
277
  name = self._resolve_name_arg(node.args[0])
262
278
  if name and method_name == "write_dlo_deltas":
263
279
  self._write_dlo_deltas_instances.add(name)
280
+ elif name and method_name == "write_dmo_deltas":
281
+ self._write_dmo_deltas_instances.add(name)
264
282
  self.generic_visit(node)
265
283
 
266
284
  def _resolve_name_arg(self, arg: ast.expr) -> Union[str, None]:
@@ -278,6 +296,7 @@ class ClientMethodVisitor(ast.NodeVisitor):
278
296
  self._read_dlo_deltas
279
297
  or self._read_dmo_deltas
280
298
  or bool(self._write_dlo_deltas_instances)
299
+ or bool(self._write_dmo_deltas_instances)
281
300
  )
282
301
 
283
302
  def found(self) -> DataAccessLayerCalls:
@@ -293,6 +312,7 @@ class ClientMethodVisitor(ast.NodeVisitor):
293
312
  read_dlo_deltas=self._read_dlo_deltas,
294
313
  read_dmo_deltas=self._read_dmo_deltas,
295
314
  write_dlo_deltas=frozenset(self._write_dlo_deltas_instances),
315
+ write_dmo_deltas=frozenset(self._write_dmo_deltas_instances),
296
316
  )
297
317
 
298
318
 
@@ -516,10 +536,10 @@ def _update_streaming_config(existing_config: dict[str, Any], file_path: str) ->
516
536
  )
517
537
 
518
538
  read_names = [source_name] if source_name else []
519
- write_names = list(output.write_dlo_deltas)
539
+ write_names = list(output.write_dlo_deltas or output.write_dmo_deltas)
520
540
  existing_config["permissions"] = {
521
541
  "read": {read_layer: read_names},
522
- "write": {"dlo": write_names},
542
+ "write": {output.write_layer: write_names},
523
543
  }
524
544
 
525
545