mammoth-cli 2.2.31__tar.gz → 2.2.33__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 (258) hide show
  1. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/PKG-INFO +1 -1
  2. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +6 -1
  4. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +5 -1
  5. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/batch.py +26 -4
  6. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/view.py +92 -33
  7. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/errors/envelope.py +3 -0
  8. mammoth_cli-2.2.33/mammoth_cli/services/append_blank_columns.py +57 -0
  9. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/command_contract.py +8 -0
  10. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/pyproject.toml +1 -1
  11. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/batch.yaml +1 -1
  12. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/view.yaml +2 -2
  13. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/.gitignore +0 -0
  14. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/LICENSE +0 -0
  15. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/README.md +0 -0
  16. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/__main__.py +0 -0
  17. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/app.py +0 -0
  18. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
  19. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  20. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  21. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
  22. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  23. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  24. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  25. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  26. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  27. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  28. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  29. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  30. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  31. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  32. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  33. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
  34. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  35. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  36. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  37. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  38. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  39. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  40. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
  41. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-style.md +0 -0
  42. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
  43. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  44. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  45. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  46. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  47. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  48. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  49. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  50. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  51. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
  52. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  53. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  54. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  55. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  56. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  57. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  58. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  59. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  60. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  61. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  62. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  63. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  64. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  65. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  66. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  67. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
  68. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
  69. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  70. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  71. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  72. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  73. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  74. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  75. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  76. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  77. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  78. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  79. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  80. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
  81. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  82. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  83. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  84. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  85. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
  86. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  87. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  88. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  89. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  90. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  91. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  92. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  93. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/__init__.py +0 -0
  94. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/activity.py +0 -0
  95. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/addon.py +0 -0
  96. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/agent.py +0 -0
  97. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/ai.py +0 -0
  98. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/annotation.py +0 -0
  99. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/auth.py +0 -0
  100. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/automation.py +0 -0
  101. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/billing.py +0 -0
  102. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/browse.py +0 -0
  103. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/calc.py +0 -0
  104. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/capability.py +0 -0
  105. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/client_app.py +0 -0
  106. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/completion.py +0 -0
  107. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/config.py +0 -0
  108. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/connector.py +0 -0
  109. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/context.py +0 -0
  110. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/dashboard.py +0 -0
  111. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/data_app.py +0 -0
  112. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/dataset.py +0 -0
  113. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/doctor.py +0 -0
  114. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/external_key.py +0 -0
  115. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/file.py +0 -0
  116. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/folder.py +0 -0
  117. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/job.py +0 -0
  118. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/link.py +0 -0
  119. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/notification.py +0 -0
  120. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/parameter.py +0 -0
  121. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/project.py +0 -0
  122. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/registry.py +0 -0
  123. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/report.py +0 -0
  124. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/schedule.py +0 -0
  125. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/schema.py +0 -0
  126. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/skill.py +0 -0
  127. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/snippet.py +0 -0
  128. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/support.py +0 -0
  129. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/template.py +0 -0
  130. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/trash.py +0 -0
  131. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/upgrade.py +0 -0
  132. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/user.py +0 -0
  133. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/view_ops.py +0 -0
  134. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/view_profile.py +0 -0
  135. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/webhook.py +0 -0
  136. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/workflow.py +0 -0
  137. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/commands/workspace.py +0 -0
  138. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/context/__init__.py +0 -0
  139. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/context/credentials.py +0 -0
  140. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/context/endpoint.py +0 -0
  141. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/context/profiles.py +0 -0
  142. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/context/resolver.py +0 -0
  143. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/contracts/__init__.py +0 -0
  144. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/contracts/auth.py +0 -0
  145. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/embed.py +0 -0
  146. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/errors/__init__.py +0 -0
  147. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/manifest/__init__.py +0 -0
  148. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/manifest/loader.py +0 -0
  149. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/messages/__init__.py +0 -0
  150. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/__init__.py +0 -0
  151. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/envelope.py +0 -0
  152. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/normalize.py +0 -0
  153. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/policy.py +0 -0
  154. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/progress.py +0 -0
  155. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/output/render.py +0 -0
  156. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/py.typed +0 -0
  157. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/__init__.py +0 -0
  158. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/confirm.py +0 -0
  159. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/dataset_health.py +0 -0
  160. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/dryrun.py +0 -0
  161. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/dryrun_targets.py +0 -0
  162. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/embedded.py +0 -0
  163. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/executor.py +0 -0
  164. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/input_loader.py +0 -0
  165. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/intent_only.py +0 -0
  166. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/invocation.py +0 -0
  167. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/new_data.py +0 -0
  168. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/options.py +0 -0
  169. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/parents.py +0 -0
  170. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/runlog.py +0 -0
  171. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/session.py +0 -0
  172. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/state.py +0 -0
  173. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/strict.py +0 -0
  174. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/updates.py +0 -0
  175. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/validate.py +0 -0
  176. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/runtime/verify.py +0 -0
  177. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/__init__.py +0 -0
  178. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/argspec.py +0 -0
  179. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/board_values.py +0 -0
  180. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/coerce.py +0 -0
  181. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/conditional_format.py +0 -0
  182. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/conditions.py +0 -0
  183. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/dashboard_filters.py +0 -0
  184. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/dashboard_pages.py +0 -0
  185. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/dashboard_review.py +0 -0
  186. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/data_profile.py +0 -0
  187. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/data_quality.py +0 -0
  188. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/dispatch.py +0 -0
  189. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/factory.py +0 -0
  190. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/input_fields.py +0 -0
  191. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/listing.py +0 -0
  192. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/local_time.py +0 -0
  193. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/mapping.py +0 -0
  194. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/openapi_types.py +0 -0
  195. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/positionals.py +0 -0
  196. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/protocol.py +0 -0
  197. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/read_queries.py +0 -0
  198. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/sdk_service.py +0 -0
  199. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/sql_check.py +0 -0
  200. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/stored_stats.py +0 -0
  201. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/testing.py +0 -0
  202. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/text_dates.py +0 -0
  203. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/type_system.py +0 -0
  204. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/services/write_impact.py +0 -0
  205. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/skills/__init__.py +0 -0
  206. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/skills/installer.py +0 -0
  207. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/skills/steering.py +0 -0
  208. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/mammoth_cli/testing.py +0 -0
  209. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/activity.yaml +0 -0
  210. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/addon.yaml +0 -0
  211. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/agent.yaml +0 -0
  212. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/ai.yaml +0 -0
  213. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/annotation.yaml +0 -0
  214. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/auth.yaml +0 -0
  215. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/automation.yaml +0 -0
  216. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/billing.yaml +0 -0
  217. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/browse.yaml +0 -0
  218. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/calc.yaml +0 -0
  219. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/capability.yaml +0 -0
  220. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/client-app.yaml +0 -0
  221. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/completion.yaml +0 -0
  222. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/config.yaml +0 -0
  223. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/connector.yaml +0 -0
  224. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/context.yaml +0 -0
  225. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/dashboard.yaml +0 -0
  226. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/data-app.yaml +0 -0
  227. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/dataset.yaml +0 -0
  228. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/doctor.yaml +0 -0
  229. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/external-key.yaml +0 -0
  230. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/file.yaml +0 -0
  231. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/folder.yaml +0 -0
  232. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/job.yaml +0 -0
  233. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/link.yaml +0 -0
  234. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/log.yaml +0 -0
  235. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/notification.yaml +0 -0
  236. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/parameter.yaml +0 -0
  237. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/project.yaml +0 -0
  238. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/report.yaml +0 -0
  239. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/schedule.yaml +0 -0
  240. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/schema.yaml +0 -0
  241. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/skill.yaml +0 -0
  242. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/snippet.yaml +0 -0
  243. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/support.yaml +0 -0
  244. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/template.yaml +0 -0
  245. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/trash.yaml +0 -0
  246. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/upgrade.yaml +0 -0
  247. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/user.yaml +0 -0
  248. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/version.yaml +0 -0
  249. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/webhook.yaml +0 -0
  250. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/workflow.yaml +0 -0
  251. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/commands/workspace.yaml +0 -0
  252. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/edits-target.source.yaml +0 -0
  253. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/openapi-operations.yaml +0 -0
  254. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/schema-v1.json +0 -0
  255. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/sdk-catalog.source.yaml +0 -0
  256. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/manifests/sdk-methods.yaml +0 -0
  257. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/openapi/metadata.json +0 -0
  258. {mammoth_cli-2.2.31 → mammoth_cli-2.2.33}/spec/openapi/openapi.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mammoth-cli
3
- Version: 2.2.31
3
+ Version: 2.2.33
4
4
  Summary: Command-line interface for the Mammoth Analytics platform
5
5
  Project-URL: homepage, https://mammoth.io
6
6
  Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
@@ -6,7 +6,7 @@ not implement a second HTTP client and does not call private SDK members.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "2.2.31"
9
+ __version__ = "2.2.33"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -146,7 +146,12 @@ No command:
146
146
  every source column to an existing target column. A partial mapping, or a
147
147
  destination the target lacks, is refused with `append_mapping_incomplete`
148
148
  before anything is written (the backend would append only the mapped columns
149
- and add a new column for the unknown name). An append is a standing link: it is stored
149
+ and add a new column for the unknown name). A target column the append
150
+ leaves unfilled would be NULL in every appended row: it is refused with
151
+ `append_leaves_columns_blank` unless the input names exactly those columns,
152
+ e.g. `"blank_columns": ["id","score"]` (a deliberate partial append, such as
153
+ mapping only `name`); never delete source columns or clone the source view to
154
+ get around it. An append is a standing link: it is stored
150
155
  as a pipeline step of the source view, so every later re-run of that view
151
156
  appends again into the target dataset, not just the first run. A column
152
157
  with the same name but a different type in the source view and the target
@@ -55,7 +55,11 @@ there is no separate Auto-Sync endpoint or field to find.
55
55
  `target_ds_id` writes into an existing dataset instead of creating one
56
56
  (`save_as_mode` `REPLACE_IN_DS` or `APPEND_TO_DS`). Read the delivery back in
57
57
  the target project (`view list N --project P`, `view data get`) before
58
- reporting it. Do not build this with `view export create`: the raw
58
+ reporting it. An append that leaves target columns unfilled (a partial
59
+ `column_mapping`, or a source with fewer columns) is refused with
60
+ `append_leaves_columns_blank` unless the input lists exactly those columns,
61
+ `"blank_columns": ["id","score"]`; confirm that with the user, and never
62
+ clone the source view or delete its columns to get around the refusal. Do not build this with `view export create`: the raw
59
63
  `internal_dataset` spec needs `USER_ID` and the `export_project` /
60
64
  `project_id` / `source_project_id` trio, and without them the backend answers
61
65
  `4GENR007 Validation error` with no detail; the typed command fills them.
@@ -25,6 +25,10 @@ from mammoth_cli.manifest.loader import command_by_id
25
25
  from mammoth_cli.runtime.confirm import POLICY_PROMPT_OR_YES, enforce_confirmation
26
26
  from mammoth_cli.runtime.invocation import Invocation
27
27
  from mammoth_cli.runtime.session import open_service, require_project
28
+ from mammoth_cli.services.append_blank_columns import (
29
+ BLANK_COLUMNS_FIELD,
30
+ acknowledged_blank_columns,
31
+ )
28
32
 
29
33
  HandlerResult = tuple[Any, dict[str, Any]]
30
34
 
@@ -221,15 +225,32 @@ def _reject_type_change(item: dict[str, Any], dest_col: dict[str, Any], dataset_
221
225
  )
222
226
 
223
227
 
228
+ def _filled_destination_names(
229
+ items: list[dict[str, Any]], dest: list[dict[str, Any]], source: list[dict[str, Any]]
230
+ ) -> set[str]:
231
+ """Destination column names some mapping item fills (``add_column`` items included)."""
232
+ dest_names = {col["c_name"] for col in dest}
233
+ filled: set[str] = set()
234
+ for item in items:
235
+ _, dest_col = _resolve_item(item, source, dest)
236
+ if dest_col is not None:
237
+ filled.add(dest_col["c_name"])
238
+ elif item.get("action") == "add_column" and item.get("destination_c_name") in dest_names:
239
+ filled.add(item["destination_c_name"])
240
+ return filled
241
+
242
+
224
243
  def _checked_batch_mapping(
225
- service: Any, dataset_id: int, source_id: int, mapping: Any
244
+ service: Any, dataset_id: int, source_id: int, mapping: Any, blank_columns: Any = None
226
245
  ) -> tuple[list[dict[str, Any]], list[str]]:
227
246
  """Validate a batch mapping against both schemas; return typed items and warnings.
228
247
 
229
248
  Refuses unmapped source columns and unknown destinations (the backend appends
230
249
  only the mapped columns) and stamps every item with the destination's actual
231
250
  current type, so no destination column is silently re-typed. A mapped pair
232
- whose source and destination types differ is returned as a warning.
251
+ whose source and destination types differ is returned as a warning. A
252
+ destination column no item fills is refused unless ``blank_columns`` names
253
+ exactly those columns; the confirmed blanks are returned as a warning.
233
254
  """
234
255
  source = _dataset_columns(service, source_id, "source")
235
256
  dest = _dataset_columns(service, dataset_id, "target")
@@ -258,7 +279,8 @@ def _checked_batch_mapping(
258
279
  _reject_incomplete_batch_mapping(
259
280
  (dataset_id, source_id), {col["c_name"] for col in source}, mapped_sources, unknown
260
281
  )
261
- return typed, warnings
282
+ blank = sorted({col["c_name"] for col in dest} - _filled_destination_names(typed, dest, source))
283
+ return typed, acknowledged_blank_columns(dataset_id, blank, blank_columns) + warnings
262
284
 
263
285
 
264
286
  def batch_create(invocation: Invocation) -> HandlerResult:
@@ -297,7 +319,7 @@ def batch_create(invocation: Invocation) -> HandlerResult:
297
319
  )
298
320
  with open_service(invocation) as (service, auth):
299
321
  kwargs["mapping"], warnings = _checked_batch_mapping(
300
- service, dataset_id, source_id, mapping
322
+ service, dataset_id, source_id, mapping, document.get(BLANK_COLUMNS_FIELD)
301
323
  )
302
324
  data = service.call(_symbol(invocation), **kwargs)
303
325
  if warnings and isinstance(data, dict):
@@ -55,6 +55,10 @@ from mammoth_cli.runtime.invocation import Invocation
55
55
  from mammoth_cli.runtime.session import open_service, require_project
56
56
  from mammoth_cli.services import conditional_format as cf_rules
57
57
  from mammoth_cli.services import read_queries, text_dates
58
+ from mammoth_cli.services.append_blank_columns import (
59
+ BLANK_COLUMNS_FIELD,
60
+ acknowledged_blank_columns,
61
+ )
58
62
  from mammoth_cli.services.conditions import CONDITION_KWARG, compile_condition
59
63
  from mammoth_cli.services.dashboard_review import UPLOAD_NOTE, upload_hints
60
64
  from mammoth_cli.services.data_quality import column_warnings, duplicate_rows_fact
@@ -2963,8 +2967,16 @@ def view_pipeline_rerun(invocation: Invocation) -> HandlerResult:
2963
2967
  service, invocation, dataview_id, document, 1
2964
2968
  )
2965
2969
  before = _rows_before(service, dataset_id, dataview_id, invocation.project)
2966
- fired = _end_of_pipeline_exports(service, dataview_id, dataset_id)
2967
- data = service.call(_symbol(invocation), **kwargs)
2970
+ fired = _end_of_pipeline_exports(
2971
+ service, dataview_id, dataset_id, with_blank_columns=invocation.dry_run
2972
+ )
2973
+ try:
2974
+ data = service.call(_symbol(invocation), **kwargs)
2975
+ except DryRunStop as stop:
2976
+ # The dry run's report names the exports the real run would fire, and
2977
+ # the destination columns each append would leave blank.
2978
+ stop.record["exports_fired"] = fired
2979
+ raise
2968
2980
  data = _settle_async_view_write(
2969
2981
  service, dataset_id, dataview_id, invocation.project, data, before
2970
2982
  )
@@ -2974,17 +2986,29 @@ def view_pipeline_rerun(invocation: Invocation) -> HandlerResult:
2974
2986
 
2975
2987
 
2976
2988
  #: Non-secret ``target_properties`` keys that say where an export writes.
2977
- _EXPORT_TARGET_HINT_KEYS = ("table", "host", "database", "TARGET_DS_ID", "DS_NAME", "emails")
2989
+ _EXPORT_TARGET_HINT_KEYS = (
2990
+ "table",
2991
+ "host",
2992
+ "database",
2993
+ "TARGET_DS_ID",
2994
+ "DS_NAME",
2995
+ "SAVE_AS_DS_MODE",
2996
+ "emails",
2997
+ )
2998
+ _INTERNAL_DATASET_HANDLER = "internal_dataset"
2999
+ _WRITE_INTO_EXISTING_MODES = ("APPEND_TO_DS", "REPLACE_IN_DS")
2978
3000
 
2979
3001
 
2980
3002
  def _end_of_pipeline_exports(
2981
- service: Any, dataview_id: int, dataset_id: int | None
3003
+ service: Any, dataview_id: int, dataset_id: int | None, *, with_blank_columns: bool = False
2982
3004
  ) -> list[dict[str, Any]]:
2983
3005
  """The live end-of-pipeline exports a rerun of this view fires again.
2984
3006
 
2985
3007
  A rerun re-sends every one of them to its destination (database, email, REST
2986
3008
  endpoint, another dataset), so the caller is told which, not just that the
2987
- pipeline ran. Soft-deleted exports do not fire.
3009
+ pipeline ran. Soft-deleted exports do not fire. ``with_blank_columns`` (dry run
3010
+ only) also reads the schemas to name the destination columns each append would
3011
+ leave blank; a real rerun makes no such reads.
2988
3012
  """
2989
3013
  listing = service.call(
2990
3014
  _EXPORTS_LIST_SYMBOL,
@@ -2993,23 +3017,66 @@ def _end_of_pipeline_exports(
2993
3017
  end_of_pipeline=True,
2994
3018
  )
2995
3019
  fired: list[dict[str, Any]] = []
3020
+ source_columns: Any = None
2996
3021
  for export in getattr(listing, "exports", None) or []:
2997
3022
  if export.status is ExportStatus.DELETED:
2998
3023
  continue
2999
- handler = export.handler_type
3024
+ handler = getattr(export.handler_type, "value", export.handler_type)
3000
3025
  properties = export.target_properties or {}
3001
- fired.append(
3002
- {
3003
- "export_id": export.id,
3004
- "handler_type": getattr(handler, "value", handler),
3005
- "target": {
3006
- key: properties[key] for key in _EXPORT_TARGET_HINT_KEYS if key in properties
3007
- },
3008
- }
3009
- )
3026
+ entry: dict[str, Any] = {
3027
+ "export_id": export.id,
3028
+ "handler_type": handler,
3029
+ "target": {
3030
+ key: properties[key] for key in _EXPORT_TARGET_HINT_KEYS if key in properties
3031
+ },
3032
+ }
3033
+ if with_blank_columns and _writes_into_existing_dataset(handler, properties):
3034
+ if source_columns is None:
3035
+ source_columns = _source_column_names(service, dataview_id, dataset_id)
3036
+ entry[BLANK_COLUMNS_FIELD] = _blank_destination_columns(
3037
+ source_columns,
3038
+ set(_target_dataset_schema(service, int(properties["TARGET_DS_ID"]))),
3039
+ properties.get("COLUMN_MAPPING"),
3040
+ )
3041
+ fired.append(entry)
3010
3042
  return fired
3011
3043
 
3012
3044
 
3045
+ def _writes_into_existing_dataset(handler: Any, properties: dict[str, Any]) -> bool:
3046
+ """Whether an export appends or replaces into a dataset that already exists."""
3047
+ return (
3048
+ handler == _INTERNAL_DATASET_HANDLER
3049
+ and properties.get("TARGET_DS_ID") is not None
3050
+ and properties.get("SAVE_AS_DS_MODE") in _WRITE_INTO_EXISTING_MODES
3051
+ )
3052
+
3053
+
3054
+ def _source_column_names(service: Any, dataview_id: int, dataset_id: int | None) -> set[str]:
3055
+ """The source view's column display names; unreadable is an error, never "none"."""
3056
+ columns = service.call_view(dataview_id, "columns", dataset_id=dataset_id)
3057
+ if not isinstance(columns, dict):
3058
+ raise CliError(
3059
+ code="append_schema_unreadable",
3060
+ message=f"Could not read view {dataview_id}'s columns before the append.",
3061
+ hint="Read the source view before appending from it.",
3062
+ details={"side": "source", "dataview_id": dataview_id},
3063
+ )
3064
+ return set(columns)
3065
+
3066
+
3067
+ def _blank_destination_columns(
3068
+ source_names: set[str], target_names: set[str], column_mapping: Any
3069
+ ) -> list[str]:
3070
+ """The destination columns an append leaves NULL: in the target, fed by nothing.
3071
+
3072
+ That is a target column that is neither a source column (matched by name) nor a
3073
+ ``column_mapping`` destination. The one rule behind both the export-create
3074
+ acknowledgement and the rerun report.
3075
+ """
3076
+ mapping = column_mapping if isinstance(column_mapping, dict) else {}
3077
+ return sorted(target_names - source_names - set(mapping.values()))
3078
+
3079
+
3013
3080
  #: Seconds ``view pipeline wait`` waits when the caller names no ``timeout`` (the SDK's own
3014
3081
  #: default is an hour, one request every few seconds for as long as it runs).
3015
3082
  _PIPELINE_WAIT_TIMEOUT = 300.0
@@ -3614,6 +3681,8 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3614
3681
  allowed = explicit_fields | common_fields | {_DATASET_ID_FIELD}
3615
3682
  if invocation.command_id in _DB_REPLACE_ROUTES:
3616
3683
  allowed = allowed | {_REPLACE_TABLE_FIELD}
3684
+ if invocation.command_id == "view.export.dataset":
3685
+ allowed = allowed | {BLANK_COLUMNS_FIELD}
3617
3686
  unknown = sorted(set(document) - allowed)
3618
3687
  if unknown:
3619
3688
  raise CliError(
@@ -3647,6 +3716,7 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3647
3716
  kwargs = dict(document)
3648
3717
  kwargs.pop(_DATASET_ID_FIELD, None)
3649
3718
  kwargs.pop(_REPLACE_TABLE_FIELD, None)
3719
+ blank_columns = kwargs.pop(BLANK_COLUMNS_FIELD, None)
3650
3720
  is_dataset_route = invocation.command_id == "view.export.dataset"
3651
3721
  target_ds_id = kwargs.get("target_ds_id") if is_dataset_route else None
3652
3722
  save_as_mode = kwargs.get("save_as_mode") if is_dataset_route else None
@@ -3663,6 +3733,7 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3663
3733
  int(target_ds_id) if target_ds_id is not None else None,
3664
3734
  )
3665
3735
  target_view_before = None
3736
+ blank_warnings: list[str] = []
3666
3737
  target_only_columns: list[str] = []
3667
3738
  mapped_type_warnings: list[str] = []
3668
3739
  hidden_left_out = (
@@ -3684,6 +3755,9 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3684
3755
  int(target_ds_id),
3685
3756
  kwargs.get("column_mapping"),
3686
3757
  )
3758
+ blank_warnings = acknowledged_blank_columns(
3759
+ int(target_ds_id), target_only_columns, blank_columns
3760
+ )
3687
3761
  try:
3688
3762
  data = service.call_view(dataview_id, method, dataset_id=dataset_id, **kwargs)
3689
3763
  except CliError as error:
@@ -3745,14 +3819,7 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3745
3819
  "hidden column(s) of the source view are not in the new dataset: "
3746
3820
  + ", ".join(hidden_left_out)
3747
3821
  )
3748
- if target_only_columns:
3749
- # Allowed -- an append never has to cover every target column
3750
- # -- but worth surfacing rather than leaving silent.
3751
- warnings.insert(
3752
- 0,
3753
- "target dataset has column(s) the source view does not (kept "
3754
- "as-is): " + ", ".join(target_only_columns),
3755
- )
3822
+ warnings[:0] = blank_warnings
3756
3823
  if warnings:
3757
3824
  row_check["warnings"] = warnings
3758
3825
  data["row_check"] = row_check
@@ -4073,24 +4140,16 @@ def _reject_append_schema_mismatch(
4073
4140
  in the source, not a ``column_mapping`` destination) and the type
4074
4141
  warnings for mapped pairs -- both allowed, but worth a warning.
4075
4142
  """
4076
- source_columns = service.call_view(dataview_id, "columns", dataset_id=dataset_id)
4077
- if not isinstance(source_columns, dict):
4078
- raise CliError(
4079
- code="append_schema_unreadable",
4080
- message=f"Could not read view {dataview_id}'s columns before the append.",
4081
- hint="Read the source view before appending from it.",
4082
- details={"side": "source", "dataview_id": dataview_id},
4083
- )
4143
+ source_names = _source_column_names(service, dataview_id, dataset_id)
4084
4144
  target_schema = _target_dataset_schema(service, target_ds_id)
4085
4145
  target_names = set(target_schema)
4086
4146
  mapping = column_mapping if isinstance(column_mapping, dict) else {}
4087
4147
  # View.columns (mammoth/view.py) maps display name -> internal name; these
4088
4148
  # dict keys are display names, matching what the backend's own schema
4089
4149
  # match compares by -- never the internal ids in the dict's values.
4090
- source_names = set(source_columns)
4091
4150
  _reject_partial_append_mapping(source_names, target_names, mapping, (dataview_id, target_ds_id))
4092
4151
  source_only = sorted(source_names - target_names - set(mapping))
4093
- target_only = sorted(target_names - source_names - set(mapping.values()))
4152
+ target_only = _blank_destination_columns(source_names, target_names, mapping)
4094
4153
  source_types = service.call_view(dataview_id, "column_types", dataset_id=dataset_id)
4095
4154
  _reject_append_type_mismatch(source_types, target_schema, mapping, (dataview_id, target_ds_id))
4096
4155
  if source_only:
@@ -77,6 +77,9 @@ ERROR_SUMMARIES: dict[str, str] = {
77
77
  "More than one item matched, so the assistant needs to be more specific."
78
78
  ),
79
79
  "api_error": "Mammoth couldn't complete this step.",
80
+ "append_leaves_columns_blank": (
81
+ "This append would leave columns of the destination empty, and needs them named."
82
+ ),
80
83
  "append_mapping_incomplete": "The column mapping would leave columns out or add new ones.",
81
84
  "append_schema_mismatch": "The new data doesn't line up with the existing columns.",
82
85
  "append_type_mismatch": "A column has a different type than in the existing data.",
@@ -0,0 +1,57 @@
1
+ """The named opt-in for an append that leaves destination columns blank.
2
+
3
+ An append only writes the columns its mapping fills; every other destination
4
+ column is NULL in each appended row. The caller must name exactly those
5
+ columns in ``blank_columns`` -- the same explicit acknowledgement pattern as
6
+ ``replace_table`` on a database export -- so a partial append is deliberate.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from typing import Any
13
+
14
+ from mammoth_cli.errors.envelope import EXIT_USAGE, CliError
15
+
16
+ BLANK_COLUMNS_FIELD = "blank_columns"
17
+
18
+
19
+ def acknowledged_blank_columns(
20
+ destination_id: int, blank: list[str], acknowledged: Any
21
+ ) -> list[str]:
22
+ """Return the warnings for ``blank`` (empty when none), refusing an unnamed leave-blank.
23
+
24
+ ``acknowledged`` is the input's ``blank_columns`` value; it must list
25
+ exactly the ``blank`` names as a set. A blanket ``true`` or a mismatched
26
+ list is refused with the expected list shown.
27
+ """
28
+ if not blank:
29
+ return []
30
+ expected = sorted(blank)
31
+ given = (
32
+ {str(name) for name in acknowledged}
33
+ if isinstance(acknowledged, list) and not isinstance(acknowledged, bool)
34
+ else None
35
+ )
36
+ if given != set(expected):
37
+ listed = ", ".join(expected)
38
+ raise CliError(
39
+ code="append_leaves_columns_blank",
40
+ message=(
41
+ f"Appending into dataset {destination_id} leaves column(s) {listed} "
42
+ "blank (NULL) in every appended row."
43
+ ),
44
+ exit_status=EXIT_USAGE,
45
+ hint=(
46
+ "If that is intended, add "
47
+ f'"{BLANK_COLUMNS_FIELD}": {_json_list(expected)} to the input to confirm; '
48
+ "otherwise map a source column to each of them."
49
+ ),
50
+ details={"blank_columns": expected, "acknowledged": acknowledged},
51
+ )
52
+ return [f"appended rows leave column(s) {', '.join(expected)} blank (NULL), as confirmed"]
53
+
54
+
55
+ def _json_list(names: list[str]) -> str:
56
+ """The names as a JSON array literal, for the hint."""
57
+ return json.dumps(names)
@@ -743,6 +743,14 @@ _S7_ADDITIONAL_INPUT_FIELDS: dict[str, tuple[FieldSpec, ...]] = {
743
743
  )
744
744
  for route in ("postgres", "mysql", "mssql", "redshift", "bigquery", "publish-db")
745
745
  },
746
+ # An append that leaves destination columns unfilled needs them named in
747
+ # ``blank_columns`` (CLI-side acknowledgement, never forwarded to the SDK).
748
+ **{
749
+ route: (
750
+ FieldSpec("blank_columns", required=False, annotation=list[str] | None, default=None),
751
+ )
752
+ for route in ("view.export.dataset", "batch.create")
753
+ },
746
754
  # user.update previously pinned name/email here as a guess, from when
747
755
  # UserProfileAPI.update took **fields and so had nothing introspectable.
748
756
  # The SDK method is now typed (first_name, last_name -- the backend's
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mammoth-cli"
3
- version = "2.2.31"
3
+ version = "2.2.33"
4
4
  description = "Command-line interface for the Mammoth Analytics platform"
5
5
  authors = [{name = "Mammoth Analytics", email = "support@mammoth.io"}]
6
6
  readme = "README.md"
@@ -91,7 +91,7 @@ commands:
91
91
  - CT-BATCH-CREATE-ERROR
92
92
  draft_test: null
93
93
  undo_test: null
94
- known_restrictions: Every source column must be mapped to an existing destination column; the CLI reads both schemas and refuses (append_mapping_incomplete) an unmapped source column or a destination the dataset lacks, because the backend appends only mapped columns. The CLI sends each item's expected_destination_c_type as the destination's current type (never re-types it; an explicit different value is refused with append_type_mismatch) and warns in mapping_check.warnings when source and destination types differ. SOURCE_ID is a standalone dataset in the same project (4BATC010 INVALID_SOURCE_DATASET for a file id or an append child); to append a file use file upload with append_to_ds_id. delete_source_ds=true changes the mutation class to destructive and requires confirmation.
94
+ known_restrictions: Every source column must be mapped to an existing destination column; the CLI reads both schemas and refuses (append_mapping_incomplete) an unmapped source column or a destination the dataset lacks, because the backend appends only mapped columns. A destination column no mapping item fills (add_column items count) would be NULL in every appended row, so it is refused (append_leaves_columns_blank) unless the input lists exactly those names in blank_columns; the confirmed blanks are named in mapping_check.warnings. The CLI sends each item's expected_destination_c_type as the destination's current type (never re-types it; an explicit different value is refused with append_type_mismatch) and warns in mapping_check.warnings when source and destination types differ. SOURCE_ID is a standalone dataset in the same project (4BATC010 INVALID_SOURCE_DATASET for a file id or an append child); to append a file use file upload with append_to_ds_id. delete_source_ds=true changes the mutation class to destructive and requires confirmation.
95
95
  reviewed_by: primary
96
96
  readback:
97
97
  command: batch.list
@@ -2261,7 +2261,7 @@ commands:
2261
2261
  - CT-VIEW-EXPORT-DATASET-ERROR
2262
2262
  draft_test: null
2263
2263
  undo_test: null
2264
- known_restrictions: 'Mutation class dynamic: benign_mutation for a new target, destructive when replacing an existing target. The export is appended to the source pipeline (end_of_pipeline) and re-runs with it; target_project_id sends the dataset into another project (cross-project branch-out, proven on release). Result: {dataset_id, project_id, source_view_id, next}. Not a plain copy: this leaves a live export on the source and the new dataset refreshes from the source view, so keep that working view and name it as the new dataset''s source. For an independent copy of a dataset use dataset create with ds_creation_type clone. Writing into an existing dataset (target_ds_id, save_as_mode APPEND_TO_DS or REPLACE_IN_DS; both run the same backend append code, so both get these guards): omit column_mapping and every column is mapped by name; a non-empty column_mapping is used as-is, so it must map every source column to an existing target column, and a partial mapping or an unknown destination is refused (append_mapping_incomplete) before anything is written. A mapped pair of different types is reported in row_check.warnings. A new dataset (no target_ds_id) leaves out the view''s hidden columns; they are named in row_check.warnings.'
2264
+ known_restrictions: 'Mutation class dynamic: benign_mutation for a new target, destructive when replacing an existing target. The export is appended to the source pipeline (end_of_pipeline) and re-runs with it; target_project_id sends the dataset into another project (cross-project branch-out, proven on release). Result: {dataset_id, project_id, source_view_id, next}. Not a plain copy: this leaves a live export on the source and the new dataset refreshes from the source view, so keep that working view and name it as the new dataset''s source. For an independent copy of a dataset use dataset create with ds_creation_type clone. Writing into an existing dataset (target_ds_id, save_as_mode APPEND_TO_DS or REPLACE_IN_DS; both run the same backend append code, so both get these guards): omit column_mapping and every column is mapped by name; a non-empty column_mapping is used as-is, so it must map every source column to an existing target column, and a partial mapping or an unknown destination is refused (append_mapping_incomplete) before anything is written. A target column that nothing fills (a column the source view lacks and column_mapping does not target) would be NULL in every appended row: refused (append_leaves_columns_blank) unless the input lists exactly those column names in blank_columns (a blanket true or a different list is refused); the confirmed blanks are named in row_check.warnings. Do not reshape the source view to dodge this. A mapped pair of different types is reported in row_check.warnings. A new dataset (no target_ds_id) leaves out the view''s hidden columns; they are named in row_check.warnings.'
2265
2265
  reviewed_by: primary
2266
2266
  - command_id: view.export.delete
2267
2267
  command_path: view export delete
@@ -3752,7 +3752,7 @@ commands:
3752
3752
  - CT-VIEW-PIPELINE-RERUN-ERROR
3753
3753
  draft_test: null
3754
3754
  undo_test: null
3755
- known_restrictions: 'A rerun fires every end-of-pipeline export of the view again (database, email, REST, dataset); the result lists them in exports_fired (export_id, handler_type, target) so the external effect is visible.'
3755
+ known_restrictions: 'A rerun fires every end-of-pipeline export of the view again (database, email, REST, dataset); the result lists them in exports_fired (export_id, handler_type, target) so the external effect is visible. --dry-run returns the same exports_fired without running. An internal_dataset export that appends or replaces into an existing dataset also carries blank_columns: the sorted destination column names the write would leave NULL ([] when none).'
3756
3756
  reviewed_by: primary
3757
3757
  - command_id: view.pipeline.wait
3758
3758
  command_path: view pipeline wait
File without changes
File without changes
File without changes