calkit-python 0.35.1__tar.gz → 0.35.3__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 (283) hide show
  1. {calkit_python-0.35.1 → calkit_python-0.35.3}/.gitignore +2 -0
  2. {calkit_python-0.35.1 → calkit_python-0.35.3}/PKG-INFO +1 -1
  3. calkit_python-0.35.3/calkit/cli/main/__init__.py +2 -0
  4. calkit_python-0.35.1/calkit/cli/main.py → calkit_python-0.35.3/calkit/cli/main/core.py +21 -501
  5. calkit_python-0.35.3/calkit/cli/main/xr.py +680 -0
  6. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/new.py +15 -0
  7. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/detect.py +29 -5
  8. calkit_python-0.35.3/calkit/docker.py +470 -0
  9. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/environments.py +10 -0
  10. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/models/core.py +1 -0
  11. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/models/pipeline.py +11 -0
  12. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/overleaf.py +73 -14
  13. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/pipeline.py +1 -0
  14. calkit_python-0.35.1/calkit/tests/cli/test_main.py → calkit_python-0.35.3/calkit/tests/cli/main/test_core.py +19 -601
  15. calkit_python-0.35.3/calkit/tests/cli/main/test_xr.py +726 -0
  16. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_overleaf.py +39 -0
  17. calkit_python-0.35.3/calkit/tests/models/__init__.py +0 -0
  18. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_detect.py +8 -0
  19. calkit_python-0.35.3/calkit/tests/test_docker.py +70 -0
  20. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_pipeline.py +6 -0
  21. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/environments.md +24 -0
  22. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/pipeline/index.md +58 -0
  23. {calkit_python-0.35.1 → calkit_python-0.35.3}/uv.lock +3 -3
  24. calkit_python-0.35.1/calkit/docker.py +0 -69
  25. {calkit_python-0.35.1 → calkit_python-0.35.3}/.pre-commit-config.yaml +0 -0
  26. {calkit_python-0.35.1 → calkit_python-0.35.3}/.prettierignore +0 -0
  27. {calkit_python-0.35.1 → calkit_python-0.35.3}/.python-version +0 -0
  28. {calkit_python-0.35.1 → calkit_python-0.35.3}/.yarnrc.yml +0 -0
  29. {calkit_python-0.35.1 → calkit_python-0.35.3}/CITATION.cff +0 -0
  30. {calkit_python-0.35.1 → calkit_python-0.35.3}/CODE_OF_CONDUCT.md +0 -0
  31. {calkit_python-0.35.1 → calkit_python-0.35.3}/CONTRIBUTING.md +0 -0
  32. {calkit_python-0.35.1 → calkit_python-0.35.3}/LICENSE +0 -0
  33. {calkit_python-0.35.1 → calkit_python-0.35.3}/Makefile +0 -0
  34. {calkit_python-0.35.1 → calkit_python-0.35.3}/README.md +0 -0
  35. {calkit_python-0.35.1 → calkit_python-0.35.3}/babel.config.js +0 -0
  36. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/__init__.py +0 -0
  37. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/__main__.py +0 -0
  38. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/calc.py +0 -0
  39. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/check.py +0 -0
  40. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/__init__.py +0 -0
  41. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/check.py +0 -0
  42. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/cloud.py +0 -0
  43. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/config.py +0 -0
  44. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/core.py +0 -0
  45. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/describe.py +0 -0
  46. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/import_.py +0 -0
  47. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/latex.py +0 -0
  48. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/list.py +0 -0
  49. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/notebooks.py +0 -0
  50. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/office.py +0 -0
  51. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/overleaf.py +0 -0
  52. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/slurm.py +0 -0
  53. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cli/update.py +0 -0
  54. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/cloud.py +0 -0
  55. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/conda.py +0 -0
  56. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/config.py +0 -0
  57. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/core.py +0 -0
  58. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/datasets.py +0 -0
  59. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/dvc.py +0 -0
  60. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/fs.py +0 -0
  61. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/git.py +0 -0
  62. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/github.py +0 -0
  63. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/gui.py +0 -0
  64. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/invenio.py +0 -0
  65. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/julia.py +0 -0
  66. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/jupyter.py +0 -0
  67. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/jupyterlab/__init__.py +0 -0
  68. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/jupyterlab/routes.py +0 -0
  69. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/package.json +0 -0
  70. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  71. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  72. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  73. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  74. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  75. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/909.651be47ca47390b78a92.js +0 -0
  76. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  77. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  78. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  79. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/remoteEntry.c091821b3d7f2d287a67.js +0 -0
  80. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/style.js +0 -0
  81. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/labextension/static/third-party-licenses.json +0 -0
  82. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/licenses.py +0 -0
  83. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/magics.py +0 -0
  84. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/matlab.py +0 -0
  85. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/models/__init__.py +0 -0
  86. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/models/io.py +0 -0
  87. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/models/iteration.py +0 -0
  88. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/notebooks.py +0 -0
  89. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/office.py +0 -0
  90. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/ops.py +0 -0
  91. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/releases.py +0 -0
  92. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/server.py +0 -0
  93. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/__init__.py +0 -0
  94. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/core.py +0 -0
  95. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/__init__.py +0 -0
  96. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/article/paper.tex +0 -0
  97. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/core.py +0 -0
  98. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/jfm/jfm.bst +0 -0
  99. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/jfm/jfm.cls +0 -0
  100. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  101. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/jfm/paper.tex +0 -0
  102. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/templates/latex/jfm/upmath.sty +0 -0
  103. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/__init__.py +0 -0
  104. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/__init__.py +0 -0
  105. {calkit_python-0.35.1/calkit/tests/jupyterlab → calkit_python-0.35.3/calkit/tests/cli/main}/__init__.py +0 -0
  106. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_check.py +0 -0
  107. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_config.py +0 -0
  108. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_import.py +0 -0
  109. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_latex.py +0 -0
  110. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_list.py +0 -0
  111. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_new.py +0 -0
  112. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/cli/test_notebooks.py +0 -0
  113. {calkit_python-0.35.1/calkit/tests/models → calkit_python-0.35.3/calkit/tests/jupyterlab}/__init__.py +0 -0
  114. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/jupyterlab/test_routes.py +0 -0
  115. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/models/test_iteration.py +0 -0
  116. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/models/test_pipeline.py +0 -0
  117. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_calc.py +0 -0
  118. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_check.py +0 -0
  119. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_conda.py +0 -0
  120. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_core.py +0 -0
  121. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_dvc.py +0 -0
  122. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_environments.py +0 -0
  123. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_fs.py +0 -0
  124. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_git.py +0 -0
  125. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_invenio.py +0 -0
  126. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_julia.py +0 -0
  127. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_jupyter.py +0 -0
  128. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_magics.py +0 -0
  129. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_matlab.py +0 -0
  130. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_notebooks.py +0 -0
  131. {calkit_python-0.35.1 → calkit_python-0.35.3}/calkit/tests/test_templates.py +0 -0
  132. {calkit_python-0.35.1 → calkit_python-0.35.3}/conftest.py +0 -0
  133. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/CNAME +0 -0
  134. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/apps.md +0 -0
  135. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/calculations.md +0 -0
  136. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/calkit-yaml.md +0 -0
  137. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/cli-reference.md +0 -0
  138. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/cloud-integration.md +0 -0
  139. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/datasets.md +0 -0
  140. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/dependencies.md +0 -0
  141. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/examples.md +0 -0
  142. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/governance.md +0 -0
  143. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/help.md +0 -0
  144. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/c-to-the-k-white.svg +0 -0
  145. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/calkit-fragmentation-compendium.png +0 -0
  146. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/calkit-no-bg.png +0 -0
  147. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/connect-zenodo.png +0 -0
  148. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab/all-green.png +0 -0
  149. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  150. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab/new-env.png +0 -0
  151. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab/new-notebook.png +0 -0
  152. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  153. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/jupyterlab-params.png +0 -0
  154. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/plos-osi-code-2024-03.png +0 -0
  155. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/img/vscode-nb-params.png +0 -0
  156. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/index.md +0 -0
  157. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/installation.md +0 -0
  158. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/jupyterlab.md +0 -0
  159. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/local-server.md +0 -0
  160. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/notebooks.md +0 -0
  161. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/overleaf.md +0 -0
  162. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/pipeline/manual-steps.md +0 -0
  163. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/pipeline/running-and-logging.md +0 -0
  164. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/pipeline/slurm.md +0 -0
  165. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/quickstart.md +0 -0
  166. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/references.md +0 -0
  167. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/releases.md +0 -0
  168. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/reproducibility.md +0 -0
  169. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  170. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/conda-envs.md +0 -0
  171. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/existing-project.md +0 -0
  172. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/first-project.md +0 -0
  173. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/github-actions.md +0 -0
  174. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  175. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  176. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  177. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  178. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  179. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  180. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  181. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  182. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  183. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  184. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  185. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  186. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  187. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  188. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  189. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/create-project.png +0 -0
  190. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  191. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/excel-chart.png +0 -0
  192. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/excel-data.png +0 -0
  193. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  194. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/needs-clone.png +0 -0
  195. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/new-stage.png +0 -0
  196. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  197. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  198. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/status-more-rows.png +0 -0
  199. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  200. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/untracked-data.png +0 -0
  201. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/updated-publication.png +0 -0
  202. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  203. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/office/workflow-page.png +0 -0
  204. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/clone.png +0 -0
  205. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/create-project.png +0 -0
  206. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  207. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  208. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  209. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/new-token.png +0 -0
  210. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/reclone.png +0 -0
  211. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  212. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/quick-actions.png +0 -0
  213. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/img/run-proc.png +0 -0
  214. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/index.md +0 -0
  215. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/jupyterlab.md +0 -0
  216. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/latex-codespaces.md +0 -0
  217. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/matlab.md +0 -0
  218. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/notebook-pipeline.md +0 -0
  219. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/office.md +0 -0
  220. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/openfoam.md +0 -0
  221. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/tutorials/procedures.md +0 -0
  222. {calkit_python-0.35.1 → calkit_python-0.35.3}/docs/version-control.md +0 -0
  223. {calkit_python-0.35.1 → calkit_python-0.35.3}/install.json +0 -0
  224. {calkit_python-0.35.1 → calkit_python-0.35.3}/jest.config.js +0 -0
  225. {calkit_python-0.35.1 → calkit_python-0.35.3}/jupyter-config/server-config/calkit.json +0 -0
  226. {calkit_python-0.35.1 → calkit_python-0.35.3}/mkdocs.yml +0 -0
  227. {calkit_python-0.35.1 → calkit_python-0.35.3}/package.json +0 -0
  228. {calkit_python-0.35.1 → calkit_python-0.35.3}/pyproject.toml +0 -0
  229. {calkit_python-0.35.1 → calkit_python-0.35.3}/schema/plugin.json +0 -0
  230. {calkit_python-0.35.1 → calkit_python-0.35.3}/scripts/install.ps1 +0 -0
  231. {calkit_python-0.35.1 → calkit_python-0.35.3}/scripts/install.sh +0 -0
  232. {calkit_python-0.35.1 → calkit_python-0.35.3}/scripts/make-calk9.sh +0 -0
  233. {calkit_python-0.35.1 → calkit_python-0.35.3}/scripts/sync-docs.py +0 -0
  234. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/__tests__/useQueries.spec.ts +0 -0
  235. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/calkit-config.ts +0 -0
  236. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/cell-output-marker.ts +0 -0
  237. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/commit-dialog.tsx +0 -0
  238. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/environment-editor.tsx +0 -0
  239. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/notebook-registration.tsx +0 -0
  240. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/notebook-toolbar.tsx +0 -0
  241. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/pipeline-status-bar.tsx +0 -0
  242. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/project-info-editor.tsx +0 -0
  243. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/sidebar-settings.tsx +0 -0
  244. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/sidebar.tsx +0 -0
  245. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/components/stage-editor.tsx +0 -0
  246. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/feature-flags.ts +0 -0
  247. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/file-browser-menu.ts +0 -0
  248. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  249. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/hooks/useQueries.ts +0 -0
  250. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/icons.ts +0 -0
  251. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/index.ts +0 -0
  252. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/io-tracker.ts +0 -0
  253. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/pipeline-state.ts +0 -0
  254. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/queryClient.ts +0 -0
  255. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/request.ts +0 -0
  256. {calkit_python-0.35.1 → calkit_python-0.35.3}/src/shims-mainmenu.d.ts +0 -0
  257. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/base.css +0 -0
  258. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/cell-output-marker.css +0 -0
  259. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/environment-editor.css +0 -0
  260. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/environment-selector.css +0 -0
  261. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/img/calkit-no-bg.png +0 -0
  262. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/index.css +0 -0
  263. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/index.js +0 -0
  264. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/notebook-toolbar.css +0 -0
  265. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/pipeline-status-bar.css +0 -0
  266. {calkit_python-0.35.1 → calkit_python-0.35.3}/style/sidebar.css +0 -0
  267. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  268. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/nb-julia.ipynb +0 -0
  269. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/nb-params.ipynb +0 -0
  270. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/nb-subdir.ipynb +0 -0
  271. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/pipeline.ipynb +0 -0
  272. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/script.py +0 -0
  273. {calkit_python-0.35.1 → calkit_python-0.35.3}/test/test-log.log +0 -0
  274. {calkit_python-0.35.1 → calkit_python-0.35.3}/tsconfig.json +0 -0
  275. {calkit_python-0.35.1 → calkit_python-0.35.3}/tsconfig.test.json +0 -0
  276. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/.gitignore +0 -0
  277. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/README.md +0 -0
  278. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/jupyter_server_test_config.py +0 -0
  279. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/package.json +0 -0
  280. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/playwright.config.js +0 -0
  281. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/tests/calkit.spec.ts +0 -0
  282. {calkit_python-0.35.1 → calkit_python-0.35.3}/ui-tests/yarn.lock +0 -0
  283. {calkit_python-0.35.1 → calkit_python-0.35.3}/yarn.lock +0 -0
@@ -17,3 +17,5 @@ coverage
17
17
  junit.xml
18
18
  calk9
19
19
  dist
20
+ *.vsix
21
+ vscode-ext/out
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.35.1
3
+ Version: 0.35.3
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -0,0 +1,2 @@
1
+ from . import xr # noqa: F401
2
+ from .core import * # noqa: F403
@@ -1,4 +1,4 @@
1
- """Main CLI app."""
1
+ """Core functionality in the main namespace of the CLI."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -1325,10 +1325,11 @@ def run(
1325
1325
  if failed:
1326
1326
  raise_error("Pipeline failed")
1327
1327
  else:
1328
+ _enc = sys.stdout.encoding or "utf-8"
1328
1329
  typer.echo(
1329
1330
  "Pipeline completed successfully ✅".encode(
1330
- "utf-8", errors="replace"
1331
- )
1331
+ _enc, errors="replace"
1332
+ ).decode(_enc)
1332
1333
  )
1333
1334
  if save_after_run or save_message is not None:
1334
1335
  if save_message is None:
@@ -1472,6 +1473,12 @@ def run_in_env(
1472
1473
  if env["kind"] == "docker":
1473
1474
  if "image" not in env:
1474
1475
  raise_error("Image must be defined for Docker environments")
1476
+ command_mode = env.get("command_mode", "shell")
1477
+ if command_mode not in ["shell", "entrypoint"]:
1478
+ raise_error(
1479
+ "Invalid Docker environment 'command_mode': "
1480
+ f"{command_mode}; Use 'shell' or 'entrypoint'"
1481
+ )
1475
1482
  if not no_check:
1476
1483
  check_docker_env(
1477
1484
  tag=env["image"],
@@ -1489,7 +1496,6 @@ def run_in_env(
1489
1496
  args=env.get("args", []),
1490
1497
  quiet=not verbose,
1491
1498
  )
1492
- shell_cmd = _to_shell_cmd(cmd)
1493
1499
  docker_cmd = [
1494
1500
  "docker",
1495
1501
  "run",
@@ -1536,10 +1542,17 @@ def run_in_env(
1536
1542
  "-v",
1537
1543
  f"{os.getcwd()}:{docker_wdir_mount}",
1538
1544
  image_name,
1539
- shell,
1540
- "-c",
1541
- shell_cmd,
1542
1545
  ]
1546
+ if command_mode == "shell":
1547
+ shell_cmd = _to_shell_cmd(cmd)
1548
+ docker_cmd += [shell, "-c", shell_cmd]
1549
+ else:
1550
+ if not cmd:
1551
+ raise_error(
1552
+ "No command provided for Docker environment in "
1553
+ "'entrypoint' command mode"
1554
+ )
1555
+ docker_cmd += cmd
1543
1556
  if verbose:
1544
1557
  typer.echo(f"Running command: {docker_cmd}")
1545
1558
  try:
@@ -1876,499 +1889,6 @@ def run_in_env(
1876
1889
  raise_error("Environment kind not supported")
1877
1890
 
1878
1891
 
1879
- @app.command(name="xr")
1880
- def execute_and_record(
1881
- cmd: Annotated[
1882
- list[str],
1883
- typer.Argument(
1884
- help="Command to execute and record. "
1885
- "If the first argument is a script, notebook or LaTeX file, "
1886
- "it will be treated as a stage with that file as "
1887
- "the target. Any command, including arguments, is supported."
1888
- ),
1889
- ],
1890
- environment: Annotated[
1891
- str | None,
1892
- typer.Option(
1893
- "--environment",
1894
- "-e",
1895
- help="Name of or path the spec file for the environment to use.",
1896
- ),
1897
- ] = None,
1898
- inputs: Annotated[
1899
- list[str],
1900
- typer.Option(
1901
- "--input",
1902
- "-i",
1903
- help="Input paths to record.",
1904
- ),
1905
- ] = [],
1906
- outputs: Annotated[
1907
- list[str],
1908
- typer.Option(
1909
- "--output",
1910
- "-o",
1911
- help="Output paths to record.",
1912
- ),
1913
- ] = [],
1914
- no_detect_io: Annotated[
1915
- bool,
1916
- typer.Option(
1917
- "--no-detect-io",
1918
- help=(
1919
- "Don't attempt to detect inputs and outputs from the command, "
1920
- "script, or notebook."
1921
- ),
1922
- ),
1923
- ] = False,
1924
- stage_name: Annotated[
1925
- str | None,
1926
- typer.Option(
1927
- "--stage",
1928
- help=(
1929
- "Name of the DVC stage to create for this command. If not "
1930
- "provided, a name will be generated automatically."
1931
- ),
1932
- ),
1933
- ] = None,
1934
- dry_run: Annotated[
1935
- bool,
1936
- typer.Option(
1937
- "--dry-run",
1938
- "-d",
1939
- help=(
1940
- "Print the environment and stage that would be created "
1941
- "without modifying calkit.yaml or executing the command."
1942
- ),
1943
- ),
1944
- ] = False,
1945
- force: Annotated[
1946
- bool,
1947
- typer.Option(
1948
- "--force",
1949
- "-f",
1950
- help="Force running stage even if it's up-to-date.",
1951
- ),
1952
- ] = False,
1953
- verbose: Annotated[
1954
- bool, typer.Option("--verbose", "-v", help="Print verbose output.")
1955
- ] = False,
1956
- ):
1957
- """Execute a command and if successful, record in the pipeline."""
1958
- import io
1959
- import shlex
1960
-
1961
- from calkit.detect import (
1962
- detect_io,
1963
- generate_stage_name,
1964
- )
1965
- from calkit.environments import detect_env_for_stage
1966
- from calkit.models.io import PathOutput
1967
- from calkit.models.pipeline import (
1968
- JuliaCommandStage,
1969
- JuliaScriptStage,
1970
- JupyterNotebookStage,
1971
- LatexStage,
1972
- MatlabCommandStage,
1973
- MatlabScriptStage,
1974
- PythonScriptStage,
1975
- RScriptStage,
1976
- ShellCommandStage,
1977
- ShellScriptStage,
1978
- )
1979
- from calkit.pipeline import stages_are_similar
1980
-
1981
- def _determine_output_storage(
1982
- path: str,
1983
- repo: git.Repo | None = None,
1984
- dvc_paths: list[str] | None = None,
1985
- ) -> str:
1986
- """Determine storage type (git or dvc) for an output path.
1987
-
1988
- Parameters
1989
- ----------
1990
- path : str
1991
- Path to check.
1992
- repo : git.Repo | None
1993
- Git repository. If None, only extension and size are checked.
1994
- dvc_paths : list[str] | None
1995
- List of paths tracked by DVC.
1996
-
1997
- Returns
1998
- -------
1999
- str
2000
- "git" or "dvc".
2001
- """
2002
- if dvc_paths is None:
2003
- dvc_paths = []
2004
- # If already tracked by git, use git
2005
- if repo is not None and repo.git.ls_files(path):
2006
- return "git"
2007
- # If already tracked by dvc, use dvc
2008
- if path in dvc_paths:
2009
- return "dvc"
2010
- # Check extension
2011
- if os.path.splitext(path)[-1] in DVC_EXTENSIONS:
2012
- return "dvc"
2013
- # Check size if file exists
2014
- if os.path.exists(path):
2015
- try:
2016
- if calkit.get_size(path) > DVC_SIZE_THRESH_BYTES:
2017
- return "dvc"
2018
- except (OSError, IOError):
2019
- # If we cannot determine file size (e.g., permission
2020
- # or I/O issues), fall back to the default "git"
2021
- # behavior below.
2022
- pass
2023
- # Default to git for small/unknown files
2024
- return "git"
2025
-
2026
- # First, read the pipeline before running so we can revert if the stage
2027
- # fails
2028
- ck_info = calkit.load_calkit_info()
2029
- ck_info_orig = deepcopy(ck_info)
2030
- pipeline = ck_info.get("pipeline", {})
2031
- stages = pipeline.get("stages", {})
2032
- # Strip and detect uv/pixi run prefix
2033
- if len(cmd) >= 2 and cmd[0] == "uv" and cmd[1] == "run":
2034
- if environment is None and os.path.isfile("pyproject.toml"):
2035
- environment = "pyproject.toml"
2036
- cmd = cmd[2:]
2037
- elif len(cmd) >= 2 and cmd[0] == "pixi" and cmd[1] == "run":
2038
- if environment is None and os.path.isfile("pixi.toml"):
2039
- environment = "pixi.toml"
2040
- cmd = cmd[2:]
2041
- # Guard against empty command after stripping uv/pixi run
2042
- if not cmd:
2043
- raise_error(
2044
- "No command specified after stripping environment prefix. "
2045
- "Usage: calkit xr [uv|pixi run] <command> [args]"
2046
- )
2047
- # Detect what kind of stage this is based on the command
2048
- # If the first argument is a notebook, we'll treat this as a notebook stage
2049
- # If the first argument is `python`, check that the second argument is a
2050
- # script, otherwise it's a shell-command stage
2051
- # If the first argument ends with .tex, we'll treat this as a LaTeX stage
2052
- first_arg = cmd[0]
2053
- stage = {}
2054
- language = None
2055
- if first_arg.endswith(".ipynb"):
2056
- cls = JupyterNotebookStage
2057
- stage["kind"] = "jupyter-notebook"
2058
- stage["notebook_path"] = first_arg
2059
- storage = calkit.notebooks.determine_storage(first_arg)
2060
- stage["html_storage"] = storage
2061
- stage["executed_ipynb_storage"] = storage
2062
- elif first_arg.endswith(".tex"):
2063
- cls = LatexStage
2064
- stage["kind"] = "latex"
2065
- stage["target_path"] = first_arg
2066
- language = "latex"
2067
- # Determine PDF storage based on whether it's already in git
2068
- pdf_path = first_arg.removesuffix(".tex") + ".pdf"
2069
- try:
2070
- repo = git.Repo(".")
2071
- if repo.git.ls_files(pdf_path):
2072
- stage["pdf_storage"] = "git"
2073
- else:
2074
- stage["pdf_storage"] = "dvc"
2075
- except InvalidGitRepositoryError:
2076
- # Not a git repo, default to dvc
2077
- stage["pdf_storage"] = "dvc"
2078
- elif first_arg == "python" and len(cmd) > 1 and cmd[1].endswith(".py"):
2079
- cls = PythonScriptStage
2080
- stage["kind"] = "python-script"
2081
- stage["script_path"] = cmd[1]
2082
- if len(cmd) > 2:
2083
- stage["args"] = cmd[2:]
2084
- language = "python"
2085
- elif first_arg.endswith(".py"):
2086
- cls = PythonScriptStage
2087
- stage["kind"] = "python-script"
2088
- stage["script_path"] = first_arg
2089
- if len(cmd) > 1:
2090
- stage["args"] = cmd[1:]
2091
- language = "python"
2092
- elif first_arg == "julia" and len(cmd) > 1 and cmd[1].endswith(".jl"):
2093
- cls = JuliaScriptStage
2094
- stage["kind"] = "julia-script"
2095
- stage["script_path"] = cmd[1]
2096
- if len(cmd) > 2:
2097
- stage["args"] = cmd[2:]
2098
- language = "julia"
2099
- elif first_arg.endswith(".jl"):
2100
- cls = JuliaScriptStage
2101
- stage["kind"] = "julia-script"
2102
- stage["script_path"] = first_arg
2103
- if len(cmd) > 1:
2104
- stage["args"] = cmd[1:]
2105
- language = "julia"
2106
- elif first_arg == "julia" and len(cmd) > 1:
2107
- cls = JuliaCommandStage
2108
- stage["kind"] = "julia-command"
2109
- stage["command"] = " ".join(cmd[1:])
2110
- language = "julia"
2111
- elif first_arg == "matlab" and len(cmd) > 1 and cmd[1].endswith(".m"):
2112
- cls = MatlabScriptStage
2113
- stage["kind"] = "matlab-script"
2114
- stage["script_path"] = cmd[1]
2115
- language = "matlab"
2116
- elif first_arg.endswith(".m"):
2117
- cls = MatlabScriptStage
2118
- stage["kind"] = "matlab-script"
2119
- stage["script_path"] = first_arg
2120
- language = "matlab"
2121
- elif first_arg == "matlab" and len(cmd) > 1:
2122
- cls = MatlabCommandStage
2123
- stage["kind"] = "matlab-command"
2124
- stage["command"] = " ".join(cmd[1:])
2125
- language = "matlab"
2126
- elif first_arg == "Rscript" and len(cmd) > 1 and cmd[1].endswith(".R"):
2127
- cls = RScriptStage
2128
- stage["kind"] = "r-script"
2129
- stage["script_path"] = cmd[1]
2130
- if len(cmd) > 2:
2131
- stage["args"] = cmd[2:]
2132
- language = "r"
2133
- elif first_arg.endswith(".R"):
2134
- cls = RScriptStage
2135
- stage["kind"] = "r-script"
2136
- stage["script_path"] = first_arg
2137
- if len(cmd) > 1:
2138
- stage["args"] = cmd[1:]
2139
- language = "r"
2140
- elif first_arg.endswith((".sh", ".bash", ".zsh")):
2141
- cls = ShellScriptStage
2142
- stage["kind"] = "shell-script"
2143
- stage["script_path"] = first_arg
2144
- if len(cmd) > 1:
2145
- stage["args"] = cmd[1:]
2146
- # Detect shell type from extension
2147
- if first_arg.endswith(".bash"):
2148
- stage["shell"] = "bash"
2149
- elif first_arg.endswith(".zsh"):
2150
- stage["shell"] = "zsh"
2151
- else:
2152
- stage["shell"] = "sh"
2153
- language = "shell"
2154
- else:
2155
- cls = ShellCommandStage
2156
- stage["kind"] = "shell-command"
2157
- stage["command"] = " ".join(shlex.quote(arg) for arg in cmd)
2158
- language = "shell"
2159
- # Create a stage name if one isn't provided and check for existing similar
2160
- # stages
2161
- if stage_name is None:
2162
- base_stage_name = generate_stage_name(cmd)
2163
- stage_name = base_stage_name
2164
- # If a stage with this name exists and is different, auto-increment
2165
- if stage_name in stages:
2166
- existing_stage = stages[stage_name]
2167
- if not stages_are_similar(existing_stage, stage):
2168
- # Find the next available increment
2169
- counter = 2
2170
- while f"{base_stage_name}-{counter}" in stages:
2171
- candidate_stage = stages[f"{base_stage_name}-{counter}"]
2172
- if stages_are_similar(candidate_stage, stage):
2173
- # Found a matching stage with this incremented name
2174
- stage_name = f"{base_stage_name}-{counter}"
2175
- break
2176
- counter += 1
2177
- else:
2178
- # No matching stage found, use the next available number
2179
- stage_name = f"{base_stage_name}-{counter}"
2180
- typer.echo(
2181
- f"Stage '{base_stage_name}' already exists with different "
2182
- f"configuration; using '{stage_name}' instead"
2183
- )
2184
- # Check if a similar stage already exists and reuse its environment if not
2185
- # specified
2186
- if stage_name in stages:
2187
- existing_stage = stages[stage_name]
2188
- if not stages_are_similar(existing_stage, stage):
2189
- raise_error(
2190
- f"A stage named '{stage_name}' already exists with "
2191
- "different configuration; "
2192
- f"Please specify a unique stage name with --stage"
2193
- )
2194
- return
2195
- # If no environment was specified and a similar stage exists,
2196
- # reuse its environment
2197
- if environment is None and "environment" in existing_stage:
2198
- environment = existing_stage["environment"]
2199
- typer.echo(
2200
- f"Reusing environment '{environment}' from existing "
2201
- f"stage '{stage_name}'"
2202
- )
2203
- # Detect or create environment for this stage
2204
- env_result = detect_env_for_stage(
2205
- stage=stage,
2206
- environment=environment,
2207
- ck_info=ck_info,
2208
- language=language,
2209
- )
2210
- # If we created an environment from dependencies, write the spec file
2211
- if env_result.created_from_dependencies and not dry_run:
2212
- typer.echo(
2213
- "No existing environment detected; "
2214
- "Attempting to create one based on detected dependencies"
2215
- )
2216
- if env_result.dependencies:
2217
- typer.echo(
2218
- f"Detected {language or 'code'} dependencies: "
2219
- f"{', '.join(env_result.dependencies)}"
2220
- )
2221
- if env_result.spec_path and env_result.spec_content is not None:
2222
- # Create the spec file
2223
- spec_dir = os.path.dirname(env_result.spec_path)
2224
- if spec_dir:
2225
- os.makedirs(spec_dir, exist_ok=True)
2226
- with open(env_result.spec_path, "w") as f:
2227
- f.write(env_result.spec_content)
2228
- typer.echo(f"Created environment spec: {env_result.spec_path}")
2229
- # Add environment to calkit.yaml if it doesn't exist
2230
- if not env_result.exists and not dry_run:
2231
- envs = ck_info.get("environments", {})
2232
- envs[env_result.name] = env_result.env
2233
- ck_info["environments"] = envs
2234
- with open("calkit.yaml", "w") as f:
2235
- calkit.ryaml.dump(ck_info, f)
2236
- ck_info = calkit.load_calkit_info()
2237
- env_name = env_result.name
2238
- stage["environment"] = env_name
2239
- # Detect inputs and outputs if not disabled
2240
- detected_inputs = []
2241
- detected_outputs = []
2242
- if not no_detect_io:
2243
- try:
2244
- io_info = detect_io(stage)
2245
- detected_inputs = io_info["inputs"]
2246
- detected_outputs = io_info["outputs"]
2247
- except Exception as e:
2248
- typer.echo(
2249
- f"Warning: Failed to detect inputs/outputs: {e}",
2250
- err=True,
2251
- )
2252
- # Initialize git repo and get DVC paths for storage determination
2253
- try:
2254
- repo = git.Repo(".")
2255
- except InvalidGitRepositoryError:
2256
- repo = None
2257
- dvc_paths = []
2258
- if repo is not None:
2259
- try:
2260
- dvc_paths = calkit.dvc.list_paths()
2261
- except Exception:
2262
- # DVC might not be initialized
2263
- pass
2264
- # Merge user-specified inputs/outputs with detected ones
2265
- # User-specified take precedence and detected ones are added if not already
2266
- # present
2267
- all_inputs = list(inputs) # Start with user-specified
2268
- for detected in detected_inputs:
2269
- if detected not in all_inputs:
2270
- all_inputs.append(detected)
2271
- # Convert detected outputs to PathOutput models with storage determination
2272
- detected_output_models: list[str | PathOutput] = []
2273
- for detected in detected_outputs:
2274
- storage = _determine_output_storage(detected, repo, dvc_paths)
2275
- detected_output_models.append(
2276
- PathOutput(
2277
- path=detected,
2278
- storage=storage, # type: ignore[arg-type]
2279
- )
2280
- )
2281
- # Merge outputs
2282
- all_outputs: list[str | PathOutput] = list(
2283
- outputs
2284
- ) # Start with user-specified
2285
- for detected in detected_output_models:
2286
- # Check if this output path is already present
2287
- detected_path = (
2288
- detected.path if isinstance(detected, PathOutput) else detected
2289
- )
2290
- already_present = False
2291
- for existing in all_outputs:
2292
- existing_path = (
2293
- existing.path if isinstance(existing, PathOutput) else existing
2294
- )
2295
- if existing_path == detected_path:
2296
- already_present = True
2297
- break
2298
- if not already_present:
2299
- all_outputs.append(detected)
2300
- # Add inputs and outputs to stage
2301
- if all_inputs:
2302
- stage["inputs"] = all_inputs
2303
- if all_outputs:
2304
- # Convert PathOutput objects to dicts for serialization
2305
- serialized_outputs = []
2306
- for output in all_outputs:
2307
- if isinstance(output, PathOutput):
2308
- serialized_outputs.append(
2309
- output.model_dump(exclude_unset=True)
2310
- )
2311
- else:
2312
- serialized_outputs.append(output)
2313
- stage["outputs"] = serialized_outputs
2314
- # Create the stage, write to calkit.yaml, and run it to see if
2315
- # it's successful
2316
- try:
2317
- cls.model_validate(stage)
2318
- except Exception as e:
2319
- raise_error(f"Failed to create stage: {e}")
2320
- # If dry-run, print environment and stage then return
2321
- if dry_run:
2322
- # Print environment info
2323
- if env_result.created_from_dependencies:
2324
- typer.echo("Environment (would be created):")
2325
- if env_result.spec_path and env_result.spec_content is not None:
2326
- typer.echo(f" Spec file: {env_result.spec_path}")
2327
- typer.echo(" Content:")
2328
- for line in env_result.spec_content.split("\n"):
2329
- typer.echo(f" {line}")
2330
- elif not env_result.exists:
2331
- typer.echo("Environment (would be added to calkit.yaml):")
2332
- yaml_output = io.StringIO()
2333
- calkit.ryaml.dump({env_result.name: env_result.env}, yaml_output)
2334
- for line in yaml_output.getvalue().rstrip().split("\n"):
2335
- typer.echo(f" {line}")
2336
- else:
2337
- typer.echo(f"Environment: {env_name} (already exists)")
2338
- # Print stage
2339
- typer.echo("\nStage (would be added to pipeline):")
2340
- yaml_output = io.StringIO()
2341
- calkit.ryaml.dump({stage_name: stage}, yaml_output)
2342
- for line in yaml_output.getvalue().rstrip().split("\n"):
2343
- typer.echo(f" {line}")
2344
- return
2345
- stages[stage_name] = stage
2346
- pipeline["stages"] = stages
2347
- ck_info["pipeline"] = pipeline
2348
- with open("calkit.yaml", "w") as f:
2349
- calkit.ryaml.dump(ck_info, f)
2350
- try:
2351
- # Format stage as YAML for display
2352
- yaml_output = io.StringIO()
2353
- calkit.ryaml.dump({stage_name: stage}, yaml_output)
2354
- # Indent YAML by 2 spaces
2355
- indented_yaml = "\n".join(
2356
- " " + line if line.strip() else line
2357
- for line in yaml_output.getvalue().rstrip().split("\n")
2358
- )
2359
- typer.echo(
2360
- f"Adding stage to pipeline and attempting to execute:"
2361
- f"\n{indented_yaml}"
2362
- )
2363
- run(targets=[stage_name], force=force, verbose=verbose)
2364
- except Exception as e:
2365
- # If the stage failed, write the old ck_info back to calkit.yaml to
2366
- # remove the stage that we added
2367
- with open("calkit.yaml", "w") as f:
2368
- calkit.ryaml.dump(ck_info_orig, f)
2369
- raise_error(f"Failed to execute stage: {e}")
2370
-
2371
-
2372
1892
  @app.command(name="runproc", help="Execute a procedure (alias for 'xproc').")
2373
1893
  @app.command(name="xproc", help="Execute a procedure.")
2374
1894
  def run_procedure(
@@ -2388,7 +1908,7 @@ def run_procedure(
2388
1908
  typer.echo(out, nl=False)
2389
1909
  time.sleep(dt)
2390
1910
  seconds -= dt
2391
- typer.echo()
1911
+ typer.echo("Done")
2392
1912
 
2393
1913
  def convert_value(value, dtype):
2394
1914
  if dtype == "int":