calkit-python 0.37.3__tar.gz → 0.37.4__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 (315) hide show
  1. {calkit_python-0.37.3 → calkit_python-0.37.4}/PKG-INFO +1 -1
  2. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/check.py +19 -6
  3. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/main/core.py +8 -5
  4. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/new.py +2 -4
  5. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/notebooks.py +22 -3
  6. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/conda.py +34 -12
  7. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/fs.py +73 -25
  8. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_check.py +36 -0
  9. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_new.py +1 -1
  10. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_conda.py +1 -1
  11. {calkit_python-0.37.3 → calkit_python-0.37.4}/.gitignore +0 -0
  12. {calkit_python-0.37.3 → calkit_python-0.37.4}/.pre-commit-config.yaml +0 -0
  13. {calkit_python-0.37.3 → calkit_python-0.37.4}/.prettierignore +0 -0
  14. {calkit_python-0.37.3 → calkit_python-0.37.4}/.python-version +0 -0
  15. {calkit_python-0.37.3 → calkit_python-0.37.4}/.vscode/launch.json +0 -0
  16. {calkit_python-0.37.3 → calkit_python-0.37.4}/.vscode/tasks.json +0 -0
  17. {calkit_python-0.37.3 → calkit_python-0.37.4}/.yarnrc.yml +0 -0
  18. {calkit_python-0.37.3 → calkit_python-0.37.4}/CITATION.cff +0 -0
  19. {calkit_python-0.37.3 → calkit_python-0.37.4}/CODE_OF_CONDUCT.md +0 -0
  20. {calkit_python-0.37.3 → calkit_python-0.37.4}/CONTRIBUTING.md +0 -0
  21. {calkit_python-0.37.3 → calkit_python-0.37.4}/LICENSE +0 -0
  22. {calkit_python-0.37.3 → calkit_python-0.37.4}/Makefile +0 -0
  23. {calkit_python-0.37.3 → calkit_python-0.37.4}/README.md +0 -0
  24. {calkit_python-0.37.3 → calkit_python-0.37.4}/babel.config.js +0 -0
  25. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/__init__.py +0 -0
  26. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/__main__.py +0 -0
  27. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/calc.py +0 -0
  28. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/check.py +0 -0
  29. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/__init__.py +0 -0
  30. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/cloud.py +0 -0
  31. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/config.py +0 -0
  32. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/core.py +0 -0
  33. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/describe.py +0 -0
  34. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/import_.py +0 -0
  35. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/latex.py +0 -0
  36. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/list.py +0 -0
  37. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/main/__init__.py +0 -0
  38. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/main/xr.py +0 -0
  39. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/office.py +0 -0
  40. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/overleaf.py +0 -0
  41. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/slurm.py +0 -0
  42. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cli/update.py +0 -0
  43. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/cloud.py +0 -0
  44. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/config.py +0 -0
  45. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/core.py +0 -0
  46. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/datasets.py +0 -0
  47. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/detect.py +0 -0
  48. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/docker.py +0 -0
  49. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/dvc/__init__.py +0 -0
  50. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/dvc/core.py +0 -0
  51. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/dvc/zip.py +0 -0
  52. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/environments.py +0 -0
  53. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/git.py +0 -0
  54. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/github.py +0 -0
  55. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/gui.py +0 -0
  56. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/invenio.py +0 -0
  57. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/julia.py +0 -0
  58. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/jupyter.py +0 -0
  59. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/jupyterlab/__init__.py +0 -0
  60. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/jupyterlab/routes.py +0 -0
  61. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/package.json +0 -0
  62. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  63. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  64. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  65. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  66. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  67. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  68. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  69. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  70. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  71. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/remoteEntry.65469af996e7a96aa983.js +0 -0
  72. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/style.js +0 -0
  73. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/labextension/static/third-party-licenses.json +0 -0
  74. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/licenses.py +0 -0
  75. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/magics.py +0 -0
  76. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/matlab.py +0 -0
  77. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/models/__init__.py +0 -0
  78. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/models/core.py +0 -0
  79. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/models/io.py +0 -0
  80. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/models/iteration.py +0 -0
  81. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/models/pipeline.py +0 -0
  82. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/notebooks.py +0 -0
  83. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/office.py +0 -0
  84. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/ops.py +0 -0
  85. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/overleaf.py +0 -0
  86. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/pipeline.py +0 -0
  87. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/releases.py +0 -0
  88. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/server.py +0 -0
  89. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/__init__.py +0 -0
  90. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/core.py +0 -0
  91. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/__init__.py +0 -0
  92. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/article/paper.tex +0 -0
  93. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/core.py +0 -0
  94. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/jfm/jfm.bst +0 -0
  95. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/jfm/jfm.cls +0 -0
  96. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  97. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/jfm/paper.tex +0 -0
  98. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/templates/latex/jfm/upmath.sty +0 -0
  99. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/__init__.py +0 -0
  100. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/__init__.py +0 -0
  101. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/main/__init__.py +0 -0
  102. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/main/test_core.py +0 -0
  103. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/main/test_xr.py +0 -0
  104. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_config.py +0 -0
  105. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_import.py +0 -0
  106. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_latex.py +0 -0
  107. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_list.py +0 -0
  108. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_notebooks.py +0 -0
  109. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_overleaf.py +0 -0
  110. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/cli/test_update.py +0 -0
  111. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/dvc/__init__.py +0 -0
  112. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/dvc/test_core.py +0 -0
  113. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/dvc/test_zip.py +0 -0
  114. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/jupyterlab/__init__.py +0 -0
  115. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/jupyterlab/test_routes.py +0 -0
  116. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/models/__init__.py +0 -0
  117. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/models/test_iteration.py +0 -0
  118. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/models/test_pipeline.py +0 -0
  119. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_calc.py +0 -0
  120. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_check.py +0 -0
  121. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_core.py +0 -0
  122. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_detect.py +0 -0
  123. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_docker.py +0 -0
  124. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_environments.py +0 -0
  125. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_fs.py +0 -0
  126. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_git.py +0 -0
  127. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_invenio.py +0 -0
  128. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_julia.py +0 -0
  129. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_jupyter.py +0 -0
  130. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_magics.py +0 -0
  131. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_matlab.py +0 -0
  132. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_notebooks.py +0 -0
  133. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_pipeline.py +0 -0
  134. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_releases.py +0 -0
  135. {calkit_python-0.37.3 → calkit_python-0.37.4}/calkit/tests/test_templates.py +0 -0
  136. {calkit_python-0.37.3 → calkit_python-0.37.4}/conftest.py +0 -0
  137. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/CNAME +0 -0
  138. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/apps.md +0 -0
  139. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/calculations.md +0 -0
  140. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/calkit-yaml.md +0 -0
  141. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/cli-reference.md +0 -0
  142. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/cloud-integration.md +0 -0
  143. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/datasets.md +0 -0
  144. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/dependencies.md +0 -0
  145. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/environments.md +0 -0
  146. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/examples.md +0 -0
  147. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/governance.md +0 -0
  148. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/help.md +0 -0
  149. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/c-to-the-k-white.svg +0 -0
  150. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/calkit-fragmentation-compendium.png +0 -0
  151. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/calkit-no-bg.png +0 -0
  152. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/connect-zenodo.png +0 -0
  153. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab/all-green.png +0 -0
  154. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  155. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab/new-env.png +0 -0
  156. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab/new-notebook.png +0 -0
  157. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  158. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/jupyterlab-params.png +0 -0
  159. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/plos-osi-code-2024-03.png +0 -0
  160. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/img/vscode-nb-params.png +0 -0
  161. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/index.md +0 -0
  162. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/installation.md +0 -0
  163. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/jupyterlab.md +0 -0
  164. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/local-server.md +0 -0
  165. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/notebooks.md +0 -0
  166. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/overleaf.md +0 -0
  167. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/pipeline/index.md +0 -0
  168. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/pipeline/manual-steps.md +0 -0
  169. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/pipeline/running-and-logging.md +0 -0
  170. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/pipeline/slurm.md +0 -0
  171. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/quickstart.md +0 -0
  172. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/references.md +0 -0
  173. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/releases.md +0 -0
  174. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/reproducibility.md +0 -0
  175. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  176. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/conda-envs.md +0 -0
  177. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/existing-project.md +0 -0
  178. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/first-project.md +0 -0
  179. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/github-actions.md +0 -0
  180. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  181. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  182. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  183. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  184. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  185. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  186. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  187. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  188. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  189. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  190. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  191. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  192. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  193. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  194. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  195. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/create-project.png +0 -0
  196. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  197. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/excel-chart.png +0 -0
  198. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/excel-data.png +0 -0
  199. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  200. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/needs-clone.png +0 -0
  201. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/new-stage.png +0 -0
  202. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  203. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  204. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/status-more-rows.png +0 -0
  205. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  206. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/untracked-data.png +0 -0
  207. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/updated-publication.png +0 -0
  208. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  209. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/office/workflow-page.png +0 -0
  210. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/clone.png +0 -0
  211. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/create-project.png +0 -0
  212. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  213. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  214. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  215. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/new-token.png +0 -0
  216. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/reclone.png +0 -0
  217. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  218. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/quick-actions.png +0 -0
  219. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/run-proc.png +0 -0
  220. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
  221. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
  222. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
  223. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
  224. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
  225. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
  226. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
  227. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/index.md +0 -0
  228. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/jupyterlab.md +0 -0
  229. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/latex-codespaces.md +0 -0
  230. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/matlab.md +0 -0
  231. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/notebook-pipeline.md +0 -0
  232. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/office.md +0 -0
  233. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/openfoam.md +0 -0
  234. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/procedures.md +0 -0
  235. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/tutorials/vscode-slurm-notebook.md +0 -0
  236. {calkit_python-0.37.3 → calkit_python-0.37.4}/docs/version-control.md +0 -0
  237. {calkit_python-0.37.3 → calkit_python-0.37.4}/install.json +0 -0
  238. {calkit_python-0.37.3 → calkit_python-0.37.4}/jest.config.js +0 -0
  239. {calkit_python-0.37.3 → calkit_python-0.37.4}/jupyter-config/server-config/calkit.json +0 -0
  240. {calkit_python-0.37.3 → calkit_python-0.37.4}/mkdocs.yml +0 -0
  241. {calkit_python-0.37.3 → calkit_python-0.37.4}/package.json +0 -0
  242. {calkit_python-0.37.3 → calkit_python-0.37.4}/pyproject.toml +0 -0
  243. {calkit_python-0.37.3 → calkit_python-0.37.4}/schema/plugin.json +0 -0
  244. {calkit_python-0.37.3 → calkit_python-0.37.4}/scripts/generate-docs-references.py +0 -0
  245. {calkit_python-0.37.3 → calkit_python-0.37.4}/scripts/install.ps1 +0 -0
  246. {calkit_python-0.37.3 → calkit_python-0.37.4}/scripts/install.sh +0 -0
  247. {calkit_python-0.37.3 → calkit_python-0.37.4}/scripts/make-calk9.sh +0 -0
  248. {calkit_python-0.37.3 → calkit_python-0.37.4}/scripts/sync-docs.py +0 -0
  249. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/__tests__/useQueries.spec.ts +0 -0
  250. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/calkit-config.ts +0 -0
  251. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/cell-output-marker.ts +0 -0
  252. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/commit-dialog.tsx +0 -0
  253. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/environment-editor.tsx +0 -0
  254. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/notebook-registration.tsx +0 -0
  255. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/notebook-toolbar.tsx +0 -0
  256. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/pipeline-status-bar.tsx +0 -0
  257. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/project-info-editor.tsx +0 -0
  258. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/sidebar-settings.tsx +0 -0
  259. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/sidebar.tsx +0 -0
  260. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/components/stage-editor.tsx +0 -0
  261. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/feature-flags.ts +0 -0
  262. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/file-browser-menu.ts +0 -0
  263. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  264. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/hooks/useQueries.ts +0 -0
  265. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/icons.ts +0 -0
  266. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/index.ts +0 -0
  267. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/io-tracker.ts +0 -0
  268. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/pipeline-state.ts +0 -0
  269. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/queryClient.ts +0 -0
  270. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/request.ts +0 -0
  271. {calkit_python-0.37.3 → calkit_python-0.37.4}/src/shims-mainmenu.d.ts +0 -0
  272. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/base.css +0 -0
  273. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/cell-output-marker.css +0 -0
  274. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/environment-editor.css +0 -0
  275. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/environment-selector.css +0 -0
  276. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/img/calkit-no-bg.png +0 -0
  277. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/index.css +0 -0
  278. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/index.js +0 -0
  279. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/notebook-toolbar.css +0 -0
  280. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/pipeline-status-bar.css +0 -0
  281. {calkit_python-0.37.3 → calkit_python-0.37.4}/style/sidebar.css +0 -0
  282. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  283. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/nb-julia.ipynb +0 -0
  284. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/nb-params.ipynb +0 -0
  285. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/nb-subdir.ipynb +0 -0
  286. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/pipeline.ipynb +0 -0
  287. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/script.py +0 -0
  288. {calkit_python-0.37.3 → calkit_python-0.37.4}/test/test-log.log +0 -0
  289. {calkit_python-0.37.3 → calkit_python-0.37.4}/tsconfig.json +0 -0
  290. {calkit_python-0.37.3 → calkit_python-0.37.4}/tsconfig.test.json +0 -0
  291. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/.gitignore +0 -0
  292. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/README.md +0 -0
  293. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/jupyter_server_test_config.py +0 -0
  294. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/package.json +0 -0
  295. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/playwright.config.js +0 -0
  296. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/tests/calkit.spec.ts +0 -0
  297. {calkit_python-0.37.3 → calkit_python-0.37.4}/ui-tests/yarn.lock +0 -0
  298. {calkit_python-0.37.3 → calkit_python-0.37.4}/uv.lock +0 -0
  299. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/.gitignore +0 -0
  300. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/.vscodeignore +0 -0
  301. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/CHANGELOG.md +0 -0
  302. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/LICENSE +0 -0
  303. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/README.md +0 -0
  304. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/images/calkit-no-bg.png +0 -0
  305. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/package-lock.json +0 -0
  306. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/package.json +0 -0
  307. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/scripts/set-proposed-api.js +0 -0
  308. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/environments.ts +0 -0
  309. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/extension.ts +0 -0
  310. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/notebooks.ts +0 -0
  311. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/test/environments.test.ts +0 -0
  312. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/test/notebooks.test.ts +0 -0
  313. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/src/types.ts +0 -0
  314. {calkit_python-0.37.3 → calkit_python-0.37.4}/vscode-ext/tsconfig.json +0 -0
  315. {calkit_python-0.37.3 → calkit_python-0.37.4}/yarn.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.37.3
3
+ Version: 0.37.4
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -876,15 +876,26 @@ def check_docker_env(
876
876
  typer.echo(f"Found modified dependency: {dep}")
877
877
  rebuild_or_pull = True
878
878
  break
879
+
880
+ def delete_lock_on_failure() -> None:
881
+ if lock_fpath and os.path.exists(lock_fpath):
882
+ os.remove(lock_fpath)
883
+
879
884
  if fpath is not None and rebuild_or_pull:
880
- wdir, fname = os.path.split(fpath)
881
- if not wdir:
882
- wdir = None
883
- cmd = ["docker", "build", "-t", tag, "-f", fname]
885
+ dockerfile_dir, dockerfile_name = os.path.split(fpath)
886
+ if not dockerfile_dir:
887
+ dockerfile_dir = None
888
+ cmd = ["docker", "build", "-t", tag, "-f", dockerfile_name]
884
889
  if platform is not None:
885
890
  cmd += ["--platform", platform]
886
891
  cmd.append(".")
887
- subprocess.check_output(cmd, cwd=wdir)
892
+ try:
893
+ subprocess.check_output(cmd, cwd=dockerfile_dir)
894
+ except subprocess.CalledProcessError:
895
+ delete_lock_on_failure()
896
+ raise_error(
897
+ f"Failed to build Docker image with tag {tag} from {fpath}"
898
+ )
888
899
  elif fpath is None and rebuild_or_pull:
889
900
  # First try to pull by repo digest
890
901
  pulled = False
@@ -901,6 +912,7 @@ def check_docker_env(
901
912
  subprocess.check_output(tag_cmd)
902
913
  pulled = True
903
914
  except subprocess.CalledProcessError:
915
+ delete_lock_on_failure()
904
916
  warn(
905
917
  f"Failed to pull image by digest: {image_with_digest}; "
906
918
  "falling back to pulling by tag"
@@ -912,6 +924,7 @@ def check_docker_env(
912
924
  try:
913
925
  subprocess.check_output(cmd)
914
926
  except subprocess.CalledProcessError:
927
+ delete_lock_on_failure()
915
928
  raise_error(f"Failed to pull image: {tag}")
916
929
  # Write the lock file
917
930
  inspect = get_docker_inspect()
@@ -993,7 +1006,7 @@ def check_conda_env(
993
1006
  try:
994
1007
  calkit.conda.check_env(
995
1008
  env_fpath=env_fpath,
996
- output_fpath=output_fpath,
1009
+ lock_fpath=output_fpath,
997
1010
  alt_lock_fpaths=alt_lock_fpaths,
998
1011
  alt_lock_fpaths_delete=alt_lock_fpaths_delete,
999
1012
  log_func=log_func,
@@ -958,10 +958,9 @@ def push(
958
958
  ):
959
959
  """Push with both Git and DVC."""
960
960
  if not no_dvc:
961
- typer.echo("Pushing to DVC remote")
961
+ remotes = calkit.dvc.get_remotes()
962
962
  if not no_check_auth:
963
963
  # Check that our dvc remotes all have our DVC token set for them
964
- remotes = calkit.dvc.get_remotes()
965
964
  ck_remote_name = calkit.config.get_app_name()
966
965
  for name, url in remotes.items():
967
966
  if (
@@ -972,9 +971,13 @@ def push(
972
971
  f"Checking authentication for DVC remote: {name}"
973
972
  )
974
973
  calkit.dvc.set_remote_auth(remote_name=name)
975
- result = run_dvc_command(["push"] + dvc_args)
976
- if result != 0:
977
- raise_error("DVC push failed")
974
+ if remotes:
975
+ typer.echo("Pushing to DVC remote")
976
+ result = run_dvc_command(["push"] + dvc_args)
977
+ if result != 0:
978
+ raise_error("DVC push failed")
979
+ else:
980
+ warn("No DVC remotes configured; skipping DVC push")
978
981
  if not no_git:
979
982
  typer.echo("Pushing to Git remote")
980
983
  try:
@@ -1204,10 +1204,8 @@ def new_conda_env(
1204
1204
  "(use -f to overwrite)"
1205
1205
  )
1206
1206
  if conda_name is None:
1207
- project_name = ck_info.get("name")
1208
- if project_name is None:
1209
- project_name = os.path.basename(os.getcwd())
1210
- conda_name = calkit.to_kebab_case(project_name) + "-" + name
1207
+ project_name = calkit.detect_project_name(prepend_owner=False)
1208
+ conda_name = calkit.to_kebab_case(project_name) + "." + name
1211
1209
  if packages is not None:
1212
1210
  assert isinstance(packages, list)
1213
1211
  # Write environment to path
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import base64
6
6
  import json
7
7
  import os
8
+ import re
8
9
  import subprocess
9
10
  import sys
10
11
  from pathlib import Path
@@ -122,6 +123,17 @@ def _check_ijulia_available(
122
123
  return res.returncode == 0
123
124
 
124
125
 
126
+ def _sanitize_kernel_name_component(name: str, label: str) -> str:
127
+ """Keep readable names while removing kernelspec-unfriendly characters."""
128
+ sanitized = re.sub(r"[^A-Za-z0-9._-]+", "-", name).strip("._-")
129
+ if not sanitized:
130
+ raise_error(
131
+ f"{label} cannot be empty after sanitizing for kernel name"
132
+ )
133
+ return "" # For typing analysis since raise_error exits
134
+ return sanitized
135
+
136
+
125
137
  @notebooks_app.command("check-kernel")
126
138
  def check_env_kernel(
127
139
  env_name: Annotated[
@@ -191,8 +203,14 @@ def check_env_kernel(
191
203
  project_name = calkit.detect_project_name(prepend_owner=False)
192
204
  if not project_name:
193
205
  raise_error("Project name cannot be empty")
194
- kernel_name = calkit.to_kebab_case(f"{project_name}-{env_name}")
206
+ kernel_name = (
207
+ f"{_sanitize_kernel_name_component(project_name, 'Project name')}"
208
+ f".{_sanitize_kernel_name_component(env_name, 'Environment name')}"
209
+ )
195
210
  display_name = f"{project_name}: {env_name}"
211
+ if verbose and not json_output:
212
+ typer.echo(f"Using kernel name: {kernel_name}")
213
+ typer.echo(f"Using display name: {display_name}")
196
214
  if language == "python":
197
215
  cmd = [
198
216
  "python",
@@ -295,9 +313,10 @@ def check_env_kernel(
295
313
  julia_cmd = (
296
314
  "import IJulia;"
297
315
  "kp=IJulia.installkernel("
298
- f'"{display_name}",'
316
+ f'"{kernel_name}",'
299
317
  f'"--project={env_dir_abs}",'
300
318
  '"--startup-file=no",'
319
+ f'displayname="{display_name}",'
301
320
  'env=Dict("JULIA_LOAD_PATH" => "@:@stdlib")'
302
321
  ");"
303
322
  "println(kp);"
@@ -316,7 +335,7 @@ def check_env_kernel(
316
335
  cmd = calkit.julia.ensure_startup_file_disabled_in_command(cmd)
317
336
  res = subprocess.run(cmd, capture_output=True, text=True)
318
337
  if res.returncode != 0:
319
- raise_error(f"Failed to create kernel:\n{res.stdout}")
338
+ raise_error(f"Failed to create kernel:\n{res.stderr}")
320
339
  kernel_path = res.stdout.strip()
321
340
  kernel_name = os.path.basename(kernel_path)
322
341
  # Update display_name to include version for matching in VS Code
@@ -262,7 +262,7 @@ class EnvCheckResult(BaseModel):
262
262
  def check_env(
263
263
  env_fpath: str = "environment.yml",
264
264
  log_func=None,
265
- output_fpath: str | None = None,
265
+ lock_fpath: str | None = None,
266
266
  alt_lock_fpaths: list[str] = [],
267
267
  alt_lock_fpaths_delete: list[str] = [],
268
268
  relaxed: bool = False,
@@ -284,7 +284,7 @@ def check_env(
284
284
  # Determine which lock file to use for creating the environment
285
285
  lock_to_use_for_creation = None
286
286
  used_legacy_lock = None
287
- if output_fpath and not os.path.isfile(output_fpath):
287
+ if lock_fpath and not os.path.isfile(lock_fpath):
288
288
  # Try alternative lock files first
289
289
  for alt_fpath in alt_lock_fpaths:
290
290
  if os.path.isfile(alt_fpath):
@@ -302,9 +302,31 @@ def check_env(
302
302
  f"Using legacy lock file for creation: {legacy_fpath}"
303
303
  )
304
304
  break
305
- elif output_fpath and os.path.isfile(output_fpath):
306
- lock_to_use_for_creation = output_fpath
307
- log_func(f"Using existing lock file for creation: {output_fpath}")
305
+ elif lock_fpath and os.path.isfile(lock_fpath):
306
+ lock_to_use_for_creation = lock_fpath
307
+ log_func(f"Using existing lock file for creation: {lock_fpath}")
308
+ # Make sure the lock file has the correct env name in it
309
+ if lock_to_use_for_creation:
310
+ with open(lock_to_use_for_creation) as f:
311
+ lock_spec = ryaml.load(f)
312
+ lock_env_name = lock_spec.get("name")
313
+ if lock_env_name is not None:
314
+ with open(env_fpath) as f:
315
+ env_spec = ryaml.load(f)
316
+ env_spec_env_name = env_spec.get("name")
317
+ if (
318
+ env_spec_env_name is not None
319
+ and lock_env_name != env_spec_env_name
320
+ ):
321
+ log_func(
322
+ f"Lock file {lock_to_use_for_creation} has env name "
323
+ f"{lock_env_name}, which does not match env spec "
324
+ f"name {env_spec_env_name}; deleting mismatched lock file "
325
+ "and ignoring it for creation"
326
+ )
327
+ if os.path.isfile(lock_to_use_for_creation):
328
+ os.remove(lock_to_use_for_creation)
329
+ lock_to_use_for_creation = None
308
330
  res = EnvCheckResult()
309
331
  if verbose:
310
332
  log_func("Getting conda info")
@@ -538,15 +560,15 @@ def check_env(
538
560
  )
539
561
  with open(env_check_fpath, "w") as f:
540
562
  ryaml.dump(env_check, f)
541
- if output_fpath is None:
563
+ if lock_fpath is None:
542
564
  fname, ext = os.path.splitext(env_fpath)
543
- output_fpath = fname + "-lock" + ext
565
+ lock_fpath = fname + "-lock" + ext
544
566
  if (
545
567
  not res.env_exists
546
568
  or res.env_needs_rebuild
547
- or not os.path.isfile(output_fpath)
569
+ or not os.path.isfile(lock_fpath)
548
570
  ):
549
- log_func(f"Exporting lock file to {output_fpath}")
571
+ log_func(f"Exporting lock file to {lock_fpath}")
550
572
  env_export = json.loads(
551
573
  subprocess.check_output(
552
574
  [a for a in export_cmd if a != "--no-builds"]
@@ -584,16 +606,16 @@ def check_env(
584
606
  dep_name = re.split("[=<>]+", dep, maxsplit=1)[0]
585
607
  if dep_name in editable_pip_deps:
586
608
  path_rel_to_project_root = editable_pip_deps[dep_name]
587
- lock_dir = os.path.dirname(output_fpath)
609
+ lock_dir = os.path.dirname(lock_fpath)
588
610
  path_rel_to_lock = os.path.relpath(
589
611
  path_rel_to_project_root, start=lock_dir
590
612
  )
591
613
  export_pip_deps[i] = (
592
614
  "-e " + Path(path_rel_to_lock).as_posix()
593
615
  )
594
- out_dir = os.path.dirname(output_fpath)
616
+ out_dir = os.path.dirname(lock_fpath)
595
617
  if out_dir:
596
618
  os.makedirs(out_dir, exist_ok=True)
597
- with open(output_fpath, "w") as f:
619
+ with open(lock_fpath, "w") as f:
598
620
  ryaml.dump(env_export, f)
599
621
  return res
@@ -298,10 +298,45 @@ class CalkitFileSystem(AbstractFileSystem):
298
298
  operation_info: dict[str, Any],
299
299
  operation: str,
300
300
  data: bytes | None = None,
301
+ file_obj: Any = None,
302
+ file_size: int | None = None,
301
303
  headers: dict | None = None,
302
304
  callback=DEFAULT_CALLBACK,
303
305
  ) -> requests.Response:
304
- """Execute a file operation using the provided operation info."""
306
+ """Execute a file operation using the provided operation info.
307
+
308
+ Exactly one of ``data`` or ``file_obj`` should be provided for put
309
+ operations. When ``file_obj`` is given it must be a seekable binary
310
+ file-like object and ``file_size`` must be set; part/chunk bodies are
311
+ streamed from it instead of being held in memory, which keeps memory
312
+ bounded for large uploads.
313
+ """
314
+ if data is not None and file_obj is not None:
315
+ raise ValueError("Provide data or file_obj, not both")
316
+ if file_obj is not None:
317
+ if not callable(getattr(file_obj, "seek", None)):
318
+ raise TypeError(
319
+ "file_obj must be a seekable binary file-like object"
320
+ )
321
+ if file_size is None:
322
+ raise ValueError(
323
+ "file_size must be provided when file_obj is given"
324
+ )
325
+ # Total payload size for progress reporting.
326
+ payload_size = len(data) if data is not None else (file_size or 0)
327
+
328
+ def _prepare_stream(request_headers: dict) -> Any:
329
+ """Return the request body, setting Content-Length if streaming."""
330
+ if data is not None:
331
+ return data
332
+ if file_obj is None:
333
+ return None
334
+ file_obj.seek(0)
335
+ # Explicit Content-Length avoids chunked transfer-encoding, which
336
+ # presigned URL signatures typically reject.
337
+ request_headers.setdefault("Content-Length", str(file_size))
338
+ return file_obj
339
+
305
340
  # Extract access info from the operation info
306
341
  access = operation_info.get("access")
307
342
  if not access:
@@ -322,16 +357,17 @@ class CalkitFileSystem(AbstractFileSystem):
322
357
  if headers:
323
358
  request_headers.update(headers)
324
359
  params = access.get("params")
360
+ body: Any = _prepare_stream(request_headers)
325
361
  resp = self._session.request(
326
362
  method=http_method.upper(),
327
363
  url=url,
328
364
  headers=request_headers,
329
365
  params=params,
330
- data=data,
366
+ data=body,
331
367
  timeout=120,
332
368
  )
333
- if data is not None and operation == "put":
334
- callback.relative_update(len(data))
369
+ if operation == "put" and payload_size:
370
+ callback.relative_update(payload_size)
335
371
  return resp
336
372
  elif kind == "http-request":
337
373
  # Generic HTTP request with custom headers
@@ -345,20 +381,21 @@ class CalkitFileSystem(AbstractFileSystem):
345
381
  if headers:
346
382
  request_headers.update(headers)
347
383
  params = access.get("params")
384
+ body = _prepare_stream(request_headers)
348
385
  resp = self._session.request(
349
386
  method=http_method.upper(),
350
387
  url=url,
351
388
  headers=request_headers,
352
389
  params=params,
353
- data=data,
390
+ data=body,
354
391
  timeout=120,
355
392
  )
356
- if data is not None and operation == "put":
357
- callback.relative_update(len(data))
393
+ if operation == "put" and payload_size:
394
+ callback.relative_update(payload_size)
358
395
  return resp
359
396
  elif kind == "presigned-multipart":
360
397
  # S3 multipart upload using server-provided part URLs
361
- if data is None:
398
+ if data is None and file_obj is None:
362
399
  raise ValueError("Data required for multipart upload")
363
400
  upload_id = access.get("upload_id")
364
401
  part_urls = access.get("part_urls")
@@ -382,7 +419,8 @@ class CalkitFileSystem(AbstractFileSystem):
382
419
  "presigned-multipart"
383
420
  )
384
421
  content_type = access.get("content_type")
385
- total_parts_needed = (len(data) + part_size - 1) // part_size
422
+ total_bytes = len(data) if data is not None else int(file_size)
423
+ total_parts_needed = (total_bytes + part_size - 1) // part_size
386
424
  if total_parts_needed > len(part_urls):
387
425
  raise ValueError(
388
426
  "Insufficient part URLs for multipart upload "
@@ -391,8 +429,12 @@ class CalkitFileSystem(AbstractFileSystem):
391
429
  uploaded_parts: list[tuple[int, str]] = []
392
430
  for part_num in range(1, total_parts_needed + 1):
393
431
  start = (part_num - 1) * part_size
394
- end = min(start + part_size, len(data))
395
- part_data = data[start:end]
432
+ end = min(start + part_size, total_bytes)
433
+ if data is not None:
434
+ part_data = data[start:end]
435
+ else:
436
+ file_obj.seek(start)
437
+ part_data = file_obj.read(end - start)
396
438
  part_url = part_urls[part_num - 1]
397
439
  part_headers = {}
398
440
  if content_type:
@@ -429,7 +471,7 @@ class CalkitFileSystem(AbstractFileSystem):
429
471
  return complete_resp
430
472
  elif kind == "presigned-chunked":
431
473
  # GCS resumable upload - requires multiple requests
432
- if data is None:
474
+ if data is None and file_obj is None:
433
475
  raise ValueError("Data required for chunked upload")
434
476
  init_url = access.get("init_url")
435
477
  if not init_url:
@@ -477,7 +519,7 @@ class CalkitFileSystem(AbstractFileSystem):
477
519
  pass
478
520
 
479
521
  # Step 2: Upload data in chunks
480
- total_size = len(data)
522
+ total_size = len(data) if data is not None else int(file_size)
481
523
  offset = 0
482
524
  chunk_headers_base = dict(
483
525
  access.get("chunk_headers") or access.get("headers") or {}
@@ -510,7 +552,11 @@ class CalkitFileSystem(AbstractFileSystem):
510
552
  try:
511
553
  while offset < total_size:
512
554
  chunk_end = min(offset + chunk_size, total_size)
513
- chunk_data = data[offset:chunk_end]
555
+ if data is not None:
556
+ chunk_data = data[offset:chunk_end]
557
+ else:
558
+ file_obj.seek(offset)
559
+ chunk_data = file_obj.read(chunk_end - offset)
514
560
  # Set Content-Range header for the chunk
515
561
  chunk_headers = {
516
562
  **chunk_headers_base,
@@ -915,12 +961,9 @@ class CalkitFileSystem(AbstractFileSystem):
915
961
  if os.path.isdir(lpath):
916
962
  self.makedirs(rpath, exist_ok=True)
917
963
  return None
918
- with open(lpath, "rb") as f:
919
- size = f.seek(0, 2)
920
- callback.set_size(size)
921
- f.seek(0)
922
- data = f.read()
923
964
  owner, project, file_path = _parse_path(rpath)
965
+ size = os.path.getsize(lpath)
966
+ callback.set_size(size)
924
967
  operation_info = self._get_fs_op_info(
925
968
  owner,
926
969
  project,
@@ -928,12 +971,17 @@ class CalkitFileSystem(AbstractFileSystem):
928
971
  "put",
929
972
  content_length=size,
930
973
  )
931
- resp = self._execute_operation(
932
- operation_info,
933
- "put",
934
- data=data,
935
- callback=callback,
936
- )
974
+ # Stream the file from disk rather than loading it into memory. This
975
+ # keeps RAM bounded when DVC pushes many (or very large) files in
976
+ # parallel via its worker pool.
977
+ with open(lpath, "rb") as f:
978
+ resp = self._execute_operation(
979
+ operation_info,
980
+ "put",
981
+ file_obj=f,
982
+ file_size=size,
983
+ callback=callback,
984
+ )
937
985
  resp.raise_for_status()
938
986
 
939
987
 
@@ -122,3 +122,39 @@ def test_check_julia_env_caches_second_run(tmp_dir):
122
122
  check=True,
123
123
  )
124
124
  assert "skipping Pkg.instantiate" not in result3.stdout
125
+
126
+
127
+ def test_check_docker_env(tmp_dir):
128
+ # First create a Dockerfile with a known base image
129
+ with open("Dockerfile", "w") as f:
130
+ f.write("FROM python:3.9-slim\n")
131
+ # Now check the environment
132
+ subprocess.check_call(
133
+ [
134
+ "calkit",
135
+ "check",
136
+ "docker-env",
137
+ "python-3.9-slim",
138
+ "-i",
139
+ "Dockerfile",
140
+ "-o",
141
+ "Dockerfile-lock.json",
142
+ ]
143
+ )
144
+ # Now modify the image to fail to build and ensure the lock file is deleted
145
+ with open("Dockerfile", "w") as f:
146
+ f.write("FROM non-existent-image:latest\n")
147
+ with pytest.raises(subprocess.CalledProcessError):
148
+ subprocess.check_call(
149
+ [
150
+ "calkit",
151
+ "check",
152
+ "docker-env",
153
+ "python-3.9-slim",
154
+ "-i",
155
+ "Dockerfile",
156
+ "-o",
157
+ "Dockerfile-lock.json",
158
+ ]
159
+ )
160
+ assert not os.path.exists("Dockerfile-lock.json")
@@ -277,7 +277,7 @@ def test_new_conda_env(tmp_dir):
277
277
  )
278
278
  with open("environment.yml") as f:
279
279
  env = calkit.ryaml.load(f)
280
- assert env["name"] == "test-e1"
280
+ assert env["name"] == "test.e1"
281
281
  assert env["dependencies"] == ["python", "requests"]
282
282
 
283
283
 
@@ -69,7 +69,7 @@ def delete_env(name: str):
69
69
 
70
70
  @pytest.fixture
71
71
  def conda_env_name():
72
- name = calkit.to_kebab_case(os.path.basename(os.getcwd())) + "-" + ENV_NAME
72
+ name = calkit.to_kebab_case(os.path.basename(os.getcwd())) + "." + ENV_NAME
73
73
  yield name
74
74
  # Teardown code
75
75
  delete_env(name)
File without changes
File without changes
File without changes