calkit-python 0.39.0__tar.gz → 0.39.2__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 (328) hide show
  1. {calkit_python-0.39.0 → calkit_python-0.39.2}/PKG-INFO +1 -1
  2. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/__init__.py +38 -0
  3. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/main/core.py +63 -20
  4. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/conda.py +182 -10
  5. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/dvc/zip.py +83 -45
  6. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/pipeline.py +174 -58
  7. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_conda.py +59 -0
  8. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_pipeline.py +198 -1
  9. {calkit_python-0.39.0 → calkit_python-0.39.2}/.claude-plugin/marketplace.json +0 -0
  10. {calkit_python-0.39.0 → calkit_python-0.39.2}/.gitignore +0 -0
  11. {calkit_python-0.39.0 → calkit_python-0.39.2}/.pre-commit-config.yaml +0 -0
  12. {calkit_python-0.39.0 → calkit_python-0.39.2}/.prettierignore +0 -0
  13. {calkit_python-0.39.0 → calkit_python-0.39.2}/.python-version +0 -0
  14. {calkit_python-0.39.0 → calkit_python-0.39.2}/.vscode/launch.json +0 -0
  15. {calkit_python-0.39.0 → calkit_python-0.39.2}/.vscode/tasks.json +0 -0
  16. {calkit_python-0.39.0 → calkit_python-0.39.2}/.yarnrc.yml +0 -0
  17. {calkit_python-0.39.0 → calkit_python-0.39.2}/AGENTS.md +0 -0
  18. {calkit_python-0.39.0 → calkit_python-0.39.2}/CITATION.cff +0 -0
  19. {calkit_python-0.39.0 → calkit_python-0.39.2}/CODE_OF_CONDUCT.md +0 -0
  20. {calkit_python-0.39.0 → calkit_python-0.39.2}/CONTRIBUTING.md +0 -0
  21. {calkit_python-0.39.0 → calkit_python-0.39.2}/LICENSE +0 -0
  22. {calkit_python-0.39.0 → calkit_python-0.39.2}/Makefile +0 -0
  23. {calkit_python-0.39.0 → calkit_python-0.39.2}/README.md +0 -0
  24. {calkit_python-0.39.0 → calkit_python-0.39.2}/agent-plugin/.claude-plugin/plugin.json +0 -0
  25. {calkit_python-0.39.0 → calkit_python-0.39.2}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
  26. {calkit_python-0.39.0 → calkit_python-0.39.2}/agent-plugin/skills/conventions/SKILL.md +0 -0
  27. {calkit_python-0.39.0 → calkit_python-0.39.2}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
  28. {calkit_python-0.39.0 → calkit_python-0.39.2}/babel.config.js +0 -0
  29. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/__main__.py +0 -0
  30. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/calc.py +0 -0
  31. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/check.py +0 -0
  32. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/__init__.py +0 -0
  33. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/check.py +0 -0
  34. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/cloud.py +0 -0
  35. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/config.py +0 -0
  36. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/core.py +0 -0
  37. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/describe.py +0 -0
  38. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/dev.py +0 -0
  39. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/import_.py +0 -0
  40. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/latex.py +0 -0
  41. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/list.py +0 -0
  42. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/main/__init__.py +0 -0
  43. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/main/xr.py +0 -0
  44. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/new.py +0 -0
  45. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/notebooks.py +0 -0
  46. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/office.py +0 -0
  47. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/overleaf.py +0 -0
  48. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/slurm.py +0 -0
  49. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cli/update.py +0 -0
  50. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/cloud.py +0 -0
  51. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/config.py +0 -0
  52. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/core.py +0 -0
  53. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/datasets.py +0 -0
  54. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/detect.py +0 -0
  55. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/docker.py +0 -0
  56. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/dvc/__init__.py +0 -0
  57. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/dvc/core.py +0 -0
  58. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/environments.py +0 -0
  59. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/fs.py +0 -0
  60. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/git.py +0 -0
  61. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/github.py +0 -0
  62. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/gui.py +0 -0
  63. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/invenio.py +0 -0
  64. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/julia.py +0 -0
  65. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/jupyter.py +0 -0
  66. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/jupyterlab/__init__.py +0 -0
  67. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/jupyterlab/routes.py +0 -0
  68. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/package.json +0 -0
  69. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  70. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  71. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  72. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  73. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  74. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  75. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  76. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  77. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  78. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/remoteEntry.65469af996e7a96aa983.js +0 -0
  79. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/style.js +0 -0
  80. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/labextension/static/third-party-licenses.json +0 -0
  81. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/licenses.py +0 -0
  82. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/magics.py +0 -0
  83. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/matlab.py +0 -0
  84. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/models/__init__.py +0 -0
  85. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/models/core.py +0 -0
  86. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/models/io.py +0 -0
  87. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/models/iteration.py +0 -0
  88. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/models/pipeline.py +0 -0
  89. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/notebooks.py +0 -0
  90. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/office.py +0 -0
  91. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/ops.py +0 -0
  92. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/overleaf.py +0 -0
  93. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/releases.py +0 -0
  94. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/server.py +0 -0
  95. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/__init__.py +0 -0
  96. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/core.py +0 -0
  97. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/__init__.py +0 -0
  98. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/article/paper.tex +0 -0
  99. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/core.py +0 -0
  100. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/jfm/jfm.bst +0 -0
  101. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/jfm/jfm.cls +0 -0
  102. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  103. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/jfm/paper.tex +0 -0
  104. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/templates/latex/jfm/upmath.sty +0 -0
  105. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/__init__.py +0 -0
  106. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/__init__.py +0 -0
  107. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/main/__init__.py +0 -0
  108. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/main/test_core.py +0 -0
  109. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/main/test_subprojects.py +0 -0
  110. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/main/test_xr.py +0 -0
  111. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_check.py +0 -0
  112. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_cloud.py +0 -0
  113. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_config.py +0 -0
  114. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_import.py +0 -0
  115. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_latex.py +0 -0
  116. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_list.py +0 -0
  117. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_new.py +0 -0
  118. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_notebooks.py +0 -0
  119. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_overleaf.py +0 -0
  120. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/cli/test_update.py +0 -0
  121. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/dvc/__init__.py +0 -0
  122. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/dvc/test_core.py +0 -0
  123. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/dvc/test_zip.py +0 -0
  124. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/jupyterlab/__init__.py +0 -0
  125. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/jupyterlab/test_routes.py +0 -0
  126. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/models/__init__.py +0 -0
  127. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/models/test_iteration.py +0 -0
  128. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/models/test_pipeline.py +0 -0
  129. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_calc.py +0 -0
  130. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_check.py +0 -0
  131. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_cloud.py +0 -0
  132. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_core.py +0 -0
  133. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_detect.py +0 -0
  134. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_docker.py +0 -0
  135. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_environments.py +0 -0
  136. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_fs.py +0 -0
  137. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_git.py +0 -0
  138. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_invenio.py +0 -0
  139. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_julia.py +0 -0
  140. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_jupyter.py +0 -0
  141. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_magics.py +0 -0
  142. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_matlab.py +0 -0
  143. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_notebooks.py +0 -0
  144. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_releases.py +0 -0
  145. {calkit_python-0.39.0 → calkit_python-0.39.2}/calkit/tests/test_templates.py +0 -0
  146. {calkit_python-0.39.0 → calkit_python-0.39.2}/conftest.py +0 -0
  147. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/CNAME +0 -0
  148. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/ai-tools.md +0 -0
  149. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/apps.md +0 -0
  150. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/calculations.md +0 -0
  151. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/calkit-yaml.md +0 -0
  152. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/cli-reference.md +0 -0
  153. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/cloud-integration.md +0 -0
  154. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/datasets.md +0 -0
  155. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/dependencies.md +0 -0
  156. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/environments.md +0 -0
  157. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/examples.md +0 -0
  158. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/governance.md +0 -0
  159. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/help.md +0 -0
  160. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/c-to-the-k-white.svg +0 -0
  161. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/calkit-fragmentation-compendium.png +0 -0
  162. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/calkit-no-bg.png +0 -0
  163. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/connect-zenodo.png +0 -0
  164. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab/all-green.png +0 -0
  165. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  166. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab/new-env.png +0 -0
  167. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab/new-notebook.png +0 -0
  168. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  169. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/jupyterlab-params.png +0 -0
  170. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/plos-osi-code-2024-03.png +0 -0
  171. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/img/vscode-nb-params.png +0 -0
  172. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/index.md +0 -0
  173. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/installation.md +0 -0
  174. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/jupyterlab.md +0 -0
  175. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/local-server.md +0 -0
  176. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/notebooks.md +0 -0
  177. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/overleaf.md +0 -0
  178. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/pipeline/index.md +0 -0
  179. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/pipeline/manual-steps.md +0 -0
  180. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/pipeline/running-and-logging.md +0 -0
  181. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/pipeline/slurm.md +0 -0
  182. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/quickstart.md +0 -0
  183. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/references.md +0 -0
  184. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/releases.md +0 -0
  185. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/reproducibility.md +0 -0
  186. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/subprojects.md +0 -0
  187. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  188. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/conda-envs.md +0 -0
  189. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/existing-project.md +0 -0
  190. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/first-project.md +0 -0
  191. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/github-actions.md +0 -0
  192. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  193. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  194. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  195. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  196. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  197. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  198. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  199. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  200. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  201. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  202. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  203. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  204. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  205. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  206. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  207. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/create-project.png +0 -0
  208. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  209. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/excel-chart.png +0 -0
  210. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/excel-data.png +0 -0
  211. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  212. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/needs-clone.png +0 -0
  213. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/new-stage.png +0 -0
  214. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  215. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  216. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/status-more-rows.png +0 -0
  217. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  218. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/untracked-data.png +0 -0
  219. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/updated-publication.png +0 -0
  220. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  221. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/office/workflow-page.png +0 -0
  222. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/clone.png +0 -0
  223. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/create-project.png +0 -0
  224. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  225. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  226. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  227. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/new-token.png +0 -0
  228. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/reclone.png +0 -0
  229. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  230. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/quick-actions.png +0 -0
  231. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/run-proc.png +0 -0
  232. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
  233. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
  234. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
  235. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
  236. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
  237. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
  238. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
  239. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/index.md +0 -0
  240. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/jupyterlab.md +0 -0
  241. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/latex-codespaces.md +0 -0
  242. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/matlab.md +0 -0
  243. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/notebook-pipeline.md +0 -0
  244. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/office.md +0 -0
  245. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/openfoam.md +0 -0
  246. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/procedures.md +0 -0
  247. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/tutorials/vscode-slurm-notebook.md +0 -0
  248. {calkit_python-0.39.0 → calkit_python-0.39.2}/docs/version-control.md +0 -0
  249. {calkit_python-0.39.0 → calkit_python-0.39.2}/install.json +0 -0
  250. {calkit_python-0.39.0 → calkit_python-0.39.2}/jest.config.js +0 -0
  251. {calkit_python-0.39.0 → calkit_python-0.39.2}/jupyter-config/server-config/calkit.json +0 -0
  252. {calkit_python-0.39.0 → calkit_python-0.39.2}/mkdocs.yml +0 -0
  253. {calkit_python-0.39.0 → calkit_python-0.39.2}/package.json +0 -0
  254. {calkit_python-0.39.0 → calkit_python-0.39.2}/pyproject.toml +0 -0
  255. {calkit_python-0.39.0 → calkit_python-0.39.2}/schema/plugin.json +0 -0
  256. {calkit_python-0.39.0 → calkit_python-0.39.2}/scripts/generate-docs-references.py +0 -0
  257. {calkit_python-0.39.0 → calkit_python-0.39.2}/scripts/install.ps1 +0 -0
  258. {calkit_python-0.39.0 → calkit_python-0.39.2}/scripts/install.sh +0 -0
  259. {calkit_python-0.39.0 → calkit_python-0.39.2}/scripts/make-calk9.sh +0 -0
  260. {calkit_python-0.39.0 → calkit_python-0.39.2}/scripts/sync-docs.py +0 -0
  261. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/__tests__/useQueries.spec.ts +0 -0
  262. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/calkit-config.ts +0 -0
  263. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/cell-output-marker.ts +0 -0
  264. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/commit-dialog.tsx +0 -0
  265. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/environment-editor.tsx +0 -0
  266. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/notebook-registration.tsx +0 -0
  267. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/notebook-toolbar.tsx +0 -0
  268. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/pipeline-status-bar.tsx +0 -0
  269. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/project-info-editor.tsx +0 -0
  270. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/sidebar-settings.tsx +0 -0
  271. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/sidebar.tsx +0 -0
  272. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/components/stage-editor.tsx +0 -0
  273. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/feature-flags.ts +0 -0
  274. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/file-browser-menu.ts +0 -0
  275. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  276. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/hooks/useQueries.ts +0 -0
  277. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/icons.ts +0 -0
  278. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/index.ts +0 -0
  279. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/io-tracker.ts +0 -0
  280. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/pipeline-state.ts +0 -0
  281. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/queryClient.ts +0 -0
  282. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/request.ts +0 -0
  283. {calkit_python-0.39.0 → calkit_python-0.39.2}/src/shims-mainmenu.d.ts +0 -0
  284. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/base.css +0 -0
  285. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/cell-output-marker.css +0 -0
  286. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/environment-editor.css +0 -0
  287. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/environment-selector.css +0 -0
  288. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/img/calkit-no-bg.png +0 -0
  289. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/index.css +0 -0
  290. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/index.js +0 -0
  291. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/notebook-toolbar.css +0 -0
  292. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/pipeline-status-bar.css +0 -0
  293. {calkit_python-0.39.0 → calkit_python-0.39.2}/style/sidebar.css +0 -0
  294. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  295. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/nb-julia.ipynb +0 -0
  296. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/nb-params.ipynb +0 -0
  297. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/nb-subdir.ipynb +0 -0
  298. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/pipeline.ipynb +0 -0
  299. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/script.py +0 -0
  300. {calkit_python-0.39.0 → calkit_python-0.39.2}/test/test-log.log +0 -0
  301. {calkit_python-0.39.0 → calkit_python-0.39.2}/tsconfig.json +0 -0
  302. {calkit_python-0.39.0 → calkit_python-0.39.2}/tsconfig.test.json +0 -0
  303. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/.gitignore +0 -0
  304. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/README.md +0 -0
  305. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/jupyter_server_test_config.py +0 -0
  306. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/package.json +0 -0
  307. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/playwright.config.js +0 -0
  308. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/tests/calkit.spec.ts +0 -0
  309. {calkit_python-0.39.0 → calkit_python-0.39.2}/ui-tests/yarn.lock +0 -0
  310. {calkit_python-0.39.0 → calkit_python-0.39.2}/uv.lock +0 -0
  311. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/.gitignore +0 -0
  312. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/.vscodeignore +0 -0
  313. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/CHANGELOG.md +0 -0
  314. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/LICENSE +0 -0
  315. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/README.md +0 -0
  316. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/images/calkit-no-bg.png +0 -0
  317. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/package-lock.json +0 -0
  318. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/package.json +0 -0
  319. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/scripts/set-proposed-api.js +0 -0
  320. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/environments.ts +0 -0
  321. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/extension.ts +0 -0
  322. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/notebooks.ts +0 -0
  323. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/test/environments.test.ts +0 -0
  324. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
  325. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/test/notebooks.test.ts +0 -0
  326. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/src/types.ts +0 -0
  327. {calkit_python-0.39.0 → calkit_python-0.39.2}/vscode-ext/tsconfig.json +0 -0
  328. {calkit_python-0.39.0 → calkit_python-0.39.2}/yarn.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.39.0
3
+ Version: 0.39.2
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -1,9 +1,47 @@
1
+ from __future__ import annotations
2
+
1
3
  from importlib.metadata import version as _version
4
+ from typing import TYPE_CHECKING
2
5
 
3
6
  __version__ = _version("calkit-python")
4
7
 
5
8
  from .core import * # noqa: F403, I001
6
9
 
10
+ if TYPE_CHECKING:
11
+ from calkit import ( # noqa: F401
12
+ calc,
13
+ check,
14
+ cloud,
15
+ conda,
16
+ config,
17
+ datasets,
18
+ detect,
19
+ docker,
20
+ dvc,
21
+ environments,
22
+ fs,
23
+ git,
24
+ github,
25
+ gui,
26
+ invenio,
27
+ julia,
28
+ jupyter,
29
+ licenses,
30
+ magics,
31
+ matlab,
32
+ models,
33
+ notebooks,
34
+ office,
35
+ ops,
36
+ overleaf,
37
+ pipeline,
38
+ releases,
39
+ server,
40
+ templates,
41
+ )
42
+
43
+ from .notebooks import declare_notebook # noqa: F401
44
+
7
45
  # Lazy-load submodules to speed up imports
8
46
  _SUBMODULES = {
9
47
  "git",
@@ -669,9 +669,11 @@ def add(
669
669
  paths.remove(".")
670
670
  if dvc_repo is not None:
671
671
  dvc_status = dvc_repo.data_status()
672
- for dvc_uncommitted in dvc_status["uncommitted"].get(
672
+ uncommitted = dvc_status["uncommitted"]
673
+ dvc_uncommitted_all = uncommitted.get(
673
674
  "modified", []
674
- ):
675
+ ) + uncommitted.get("deleted", [])
676
+ for dvc_uncommitted in dvc_uncommitted_all:
675
677
  if os.path.exists(dvc_uncommitted):
676
678
  if dry_run:
677
679
  typer.echo(
@@ -679,12 +681,25 @@ def add(
679
681
  )
680
682
  else:
681
683
  typer.echo(f"Adding {dvc_uncommitted} to DVC")
682
- dvc_repo.commit(dvc_uncommitted, force=True)
684
+ dvc_repo.commit(
685
+ dvc_uncommitted,
686
+ force=True,
687
+ allow_missing=True,
688
+ )
683
689
  else:
684
- warn(
685
- f"DVC uncommitted '{dvc_uncommitted}' does not "
686
- "exist; skipping"
687
- )
690
+ if dry_run:
691
+ typer.echo(
692
+ f"Would commit deleted {dvc_uncommitted} to DVC"
693
+ )
694
+ else:
695
+ typer.echo(
696
+ f"Committing deleted {dvc_uncommitted} to DVC"
697
+ )
698
+ dvc_repo.commit(
699
+ dvc_uncommitted,
700
+ force=True,
701
+ allow_missing=True,
702
+ )
688
703
  if not disable_auto_ignore:
689
704
  for untracked_file in untracked_git_files:
690
705
  if (
@@ -1053,10 +1068,32 @@ def pull(
1053
1068
  if calkit.dvc.detect_calkit_remote_type(name, url) == "http":
1054
1069
  typer.echo(f"Checking authentication for DVC remote: {name}")
1055
1070
  calkit.dvc.set_remote_auth(remote_name=name)
1071
+ if (
1072
+ not no_recursive
1073
+ and "--recursive" not in dvc_args
1074
+ and "-R" not in dvc_args
1075
+ ):
1076
+ dvc_args.append("--recursive")
1056
1077
  result = calkit.dvc.run_dvc_command(["pull"] + dvc_args)
1057
1078
  if result != 0:
1058
1079
  raise_error("DVC pull failed")
1059
1080
  calkit.dvc.zip.sync_all(direction="to-workspace")
1081
+ if not no_recursive:
1082
+ # Pull DVC in isolated subprojects (those with their own .dvc folder)
1083
+ ck_info = calkit.load_calkit_info()
1084
+ for sp in ck_info.get("subprojects", []):
1085
+ if not isinstance(sp, dict) or not sp.get("path"):
1086
+ continue
1087
+ sp_path = sp["path"]
1088
+ if not os.path.isdir(os.path.join(sp_path, ".dvc")):
1089
+ continue
1090
+ typer.echo(f"DVC pulling subproject: {sp_path}")
1091
+ sp_result = calkit.dvc.run_dvc_command(
1092
+ ["pull"] + dvc_args, cwd=sp_path
1093
+ )
1094
+ if sp_result != 0:
1095
+ raise_error(f"DVC pull failed for subproject: {sp_path}")
1096
+ calkit.dvc.zip.sync_all(direction="to-workspace", wdir=sp_path)
1060
1097
 
1061
1098
 
1062
1099
  @app.command(name="push")
@@ -1431,13 +1468,8 @@ def run(
1431
1468
  raise_error("Failed to initialize Git repo")
1432
1469
  # Set env vars
1433
1470
  calkit.set_env_vars(ck_info=ck_info)
1434
- # Clean all notebooks in the pipeline
1435
- try:
1436
- calkit.notebooks.clean_all_in_pipeline(ck_info=ck_info)
1437
- except Exception as e:
1438
- raise_error(f"Failed to clean notebooks: {e.__class__.__name__}: {e}")
1439
1471
  if not quiet:
1440
- typer.echo("Getting system information")
1472
+ calkit.echo("💻 Getting system information")
1441
1473
  # Get system information
1442
1474
  system_info = calkit.get_system_info()
1443
1475
  if save_logs:
@@ -1453,7 +1485,7 @@ def run(
1453
1485
  json.dump(system_info, f, indent=2)
1454
1486
  # First check any system-level dependencies exist
1455
1487
  if not quiet:
1456
- typer.echo("Checking system-level dependencies")
1488
+ calkit.echo("🔗 Checking system-level dependencies")
1457
1489
  try:
1458
1490
  calkit.check_system_deps(ck_info=ck_info, system_info=system_info)
1459
1491
  except Exception as e:
@@ -1461,7 +1493,7 @@ def run(
1461
1493
  raise_error(str(e))
1462
1494
  # Check all environments in the pipeline (with caching)
1463
1495
  # If any failed, warn the user that we might have problems running
1464
- typer.echo("Checking environments")
1496
+ calkit.echo("📦 Checking environments")
1465
1497
  env_check_results = calkit.environments.check_all_in_pipeline(
1466
1498
  ck_info=ck_info, targets=targets, force=force
1467
1499
  )
@@ -1471,7 +1503,13 @@ def run(
1471
1503
  failed = not result.get("success", False)
1472
1504
  if failed:
1473
1505
  warn(f"Failed to check environment '{env_name}'")
1474
- # Check environments for each subproject so DVC can detect changes
1506
+ # Clean all notebooks in the pipeline
1507
+ try:
1508
+ calkit.echo("📓 Cleaning notebooks")
1509
+ calkit.notebooks.clean_all_in_pipeline(ck_info=ck_info)
1510
+ except Exception as e:
1511
+ raise_error(f"Failed to clean notebooks: {e.__class__.__name__}: {e}")
1512
+ # Check environments and clean notebooks for each subproject
1475
1513
  subprojects = ck_info.get("subprojects", [])
1476
1514
  if subprojects:
1477
1515
  prev_cwd = os.getcwd()
@@ -1481,11 +1519,13 @@ def run(
1481
1519
  sp = Path(subproject["path"]).as_posix()
1482
1520
  if not os.path.isdir(sp):
1483
1521
  continue
1484
- if not quiet:
1485
- typer.echo(f"Checking environments for subproject: {sp}")
1486
1522
  os.chdir(sp)
1487
1523
  try:
1488
1524
  sp_ck_info = calkit.load_calkit_info()
1525
+ if not quiet:
1526
+ calkit.echo(
1527
+ f"📦 Checking environments for subproject: {sp}"
1528
+ )
1489
1529
  sp_env_results = calkit.environments.check_all_in_pipeline(
1490
1530
  ck_info=sp_ck_info, force=force
1491
1531
  )
@@ -1497,9 +1537,12 @@ def run(
1497
1537
  f"Failed to check environment '{env_name}' "
1498
1538
  f"in subproject '{sp}'"
1499
1539
  )
1540
+ if not quiet:
1541
+ calkit.echo(f"📓 Cleaning notebooks for subproject: {sp}")
1542
+ calkit.notebooks.clean_all_in_pipeline(ck_info=sp_ck_info)
1500
1543
  except Exception as e:
1501
1544
  warn(
1502
- f"Failed to check environments for subproject '{sp}': "
1545
+ f"Failed to prepare subproject '{sp}': "
1503
1546
  f"{e.__class__.__name__}: {e}"
1504
1547
  )
1505
1548
  finally:
@@ -1511,7 +1554,7 @@ def run(
1511
1554
  dvc_stages = None
1512
1555
  if ck_info.get("pipeline", {}) or ck_info.get("subprojects"):
1513
1556
  if not quiet:
1514
- typer.echo("Compiling DVC pipeline")
1557
+ calkit.echo("🔀 Compiling DVC pipeline")
1515
1558
  try:
1516
1559
  dvc_stages = calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
1517
1560
  except Exception as e:
@@ -7,6 +7,7 @@ import os
7
7
  import re
8
8
  import shutil
9
9
  import subprocess
10
+ import sys
10
11
  import warnings
11
12
  from pathlib import Path
12
13
  from typing import cast
@@ -137,6 +138,75 @@ def _editable_package_name_from_dir(dir_path: str) -> str:
137
138
  raise ValueError(f"Could not determine package name from {dir_path}")
138
139
 
139
140
 
141
+ def _run_pip_freeze(env_prefix: str) -> list[str]:
142
+ """Run pip freeze inside a conda env and return the list of packages.
143
+
144
+ Uses the env's pip executable directly (avoids ``conda run``, which can
145
+ touch the env directory and invalidate the stored mtime check).
146
+ This captures git URLs and exact refs that ``conda env export`` drops.
147
+ """
148
+ if sys.platform == "win32":
149
+ pip_exe = os.path.join(env_prefix, "Scripts", "pip.exe")
150
+ else:
151
+ pip_exe = os.path.join(env_prefix, "bin", "pip")
152
+ if not os.path.isfile(pip_exe):
153
+ return []
154
+ output = subprocess.check_output([pip_exe, "freeze"]).decode()
155
+ return [
156
+ line.strip()
157
+ for line in output.splitlines()
158
+ if line.strip() and not line.startswith("#")
159
+ ]
160
+
161
+
162
+ _GIT_RE = re.compile(r"\s*@\s*git\+", re.IGNORECASE)
163
+
164
+
165
+ def _pkg_name_from_dep(dep: str) -> str:
166
+ """Extract the normalized package name from a pip/conda dep string."""
167
+ name = _GIT_RE.split(dep)[0]
168
+ name = re.split(r"[@=<>]", name)[0]
169
+ return name.strip().lower()
170
+
171
+
172
+ def _enrich_pip_deps_from_freeze(
173
+ pip_deps: list[str],
174
+ pip_freeze: list[str],
175
+ ) -> list[str]:
176
+ """Replace pip dep entries with pip freeze versions when available.
177
+
178
+ Preserves git URLs and exact refs that conda env export drops.
179
+ Editable installs in pip_deps are kept unchanged.
180
+ """
181
+ freeze_by_name = {
182
+ _pkg_name_from_dep(line): line
183
+ for line in pip_freeze
184
+ if not line.startswith("-e ") and "@ file://" not in line
185
+ }
186
+ result = []
187
+ for dep in pip_deps:
188
+ if dep.startswith("-e ") or dep.startswith("--editable "):
189
+ result.append(dep)
190
+ continue
191
+ name = _pkg_name_from_dep(dep)
192
+ result.append(freeze_by_name.get(name, dep))
193
+ return result
194
+
195
+
196
+ def _normalize_git_dep_url(dep: str) -> str:
197
+ """Extract and normalize the git URL+ref from a dep string for comparison.
198
+
199
+ Returns the lowercased URL with .git suffix removed.
200
+ """
201
+ parts = _GIT_RE.split(dep, maxsplit=1)
202
+ if len(parts) < 2:
203
+ return ""
204
+ url = parts[1].strip().lower()
205
+ if url.endswith(".git"):
206
+ url = url[:-4]
207
+ return url.rstrip("/")
208
+
209
+
140
210
  def _check_single(
141
211
  req: str, actual: str, env_spec_dir: str, conda: bool = False
142
212
  ) -> bool:
@@ -156,11 +226,28 @@ def _check_single(
156
226
  req = os.path.join(env_spec_dir, req)
157
227
  req = _editable_package_name_from_dir(req)
158
228
  editable = True
159
- # If this is a Git version, we can't check it
160
- # TODO: Clone Git repos to check?
161
- if "@git" in req:
162
- warnings.warn(f"Cannot check Git version for {req}")
163
- req = req.split("@git")[0]
229
+ # Handle git-based requirements both legacy "pkg@git+url" and PEP 508
230
+ # "pkg @ git+url" forms.
231
+ req_is_git = bool(_GIT_RE.search(req))
232
+ actual_is_git = bool(_GIT_RE.search(actual))
233
+ if req_is_git and actual_is_git:
234
+ # Both sides have git URLs: compare normalized URL+ref directly.
235
+ # A short SHA in the spec matches a full SHA in the installed dep when
236
+ # the full SHA starts with the short one.
237
+ req_url = _normalize_git_dep_url(req)
238
+ actual_url = _normalize_git_dep_url(actual)
239
+ if req_url and actual_url:
240
+ req_base, _, req_ref = req_url.rpartition("@")
241
+ actual_base, _, actual_ref = actual_url.rpartition("@")
242
+ if req_base != actual_base:
243
+ return False
244
+ return actual_ref.startswith(req_ref) or req_ref.startswith(
245
+ actual_ref
246
+ )
247
+ # Fallback: compare names only
248
+ return _pkg_name_from_dep(req) == _pkg_name_from_dep(actual)
249
+ if req_is_git:
250
+ req = _GIT_RE.split(req)[0].strip()
164
251
  req_name = re.split("[=<>]", req)[0].strip()
165
252
  req_spec = req.removeprefix(req_name).strip().replace(" ", "")
166
253
  if "[" in req_name:
@@ -176,9 +263,16 @@ def _check_single(
176
263
  numbers_and_dots = re.match(r"^[0-9.]+$", req_spec[2:])
177
264
  if numbers_and_dots and len(req_spec.split(".")) < 3:
178
265
  req_spec += ".*"
179
- actual_name, actual_vers = re.split("[=<>]+", actual, maxsplit=1)
180
- if actual_name != req_name:
266
+ if actual_is_git:
267
+ # Spec has no git URL but installed dep does; name match is sufficient
268
+ actual = _GIT_RE.split(actual)[0].strip()
269
+ actual_parts = re.split("[=<>]+", actual, maxsplit=1)
270
+ actual_name = actual_parts[0]
271
+ actual_vers = actual_parts[1] if len(actual_parts) > 1 else ""
272
+ if actual_name.strip().lower() != req_name.lower():
181
273
  return False
274
+ if req_is_git or actual_is_git:
275
+ return True
182
276
  actual_spec = actual.removeprefix(actual_name)
183
277
  if conda and actual_spec.startswith("="):
184
278
  actual_spec = "=" + actual_spec
@@ -328,6 +422,7 @@ def check_env(
328
422
  os.remove(lock_to_use_for_creation)
329
423
  lock_to_use_for_creation = None
330
424
  res = EnvCheckResult()
425
+ early_pip_freeze: list[str] = []
331
426
  if verbose:
332
427
  log_func("Getting conda info")
333
428
  conda_exe = find_conda_exe()
@@ -360,8 +455,10 @@ def check_env(
360
455
  prefix_orig = prefix
361
456
  if prefix is not None:
362
457
  prefix = os.path.abspath(prefix)
458
+ env_prefix_path = prefix
363
459
  env_check_fpath = os.path.join(prefix, "env-export.yml")
364
460
  else:
461
+ env_prefix_path = os.path.join(envs_dir, env_name)
365
462
  env_check_fpath = os.path.join(
366
463
  os.path.expanduser("~"),
367
464
  ".calkit",
@@ -371,6 +468,8 @@ def check_env(
371
468
  env_check_dir = os.path.dirname(env_check_fpath)
372
469
  os.makedirs(env_check_dir, exist_ok=True)
373
470
  env_spec_dir = os.path.dirname(os.path.abspath(env_fpath))
471
+ spec_pip_deps = _get_pip_dependency_list(env_spec["dependencies"])
472
+ spec_has_git_pip = any(_GIT_RE.search(d) for d in spec_pip_deps)
374
473
  # Create env export command, which will be used later
375
474
  export_cmd = [
376
475
  conda_exe, # Mamba output is slightly different
@@ -464,8 +563,26 @@ def check_env(
464
563
  env_check["mtime"] = os.path.getmtime(
465
564
  os.path.normpath(env_check["prefix"])
466
565
  )
467
- with open(env_check_fpath, "w") as f:
468
- ryaml.dump(env_check, f)
566
+ # If the spec has git pip deps, enrich the in-memory env_check pip
567
+ # section so that git refs are compared correctly during the dep check
568
+ # rather than falling back to name-only matching.
569
+ if spec_has_git_pip:
570
+ log_func("Running pip freeze to enrich git dep comparison")
571
+ try:
572
+ early_pip_freeze = _run_pip_freeze(env_prefix_path)
573
+ except Exception as e:
574
+ log_func(
575
+ f"pip freeze failed; git dep URLs may be missing: {e}"
576
+ )
577
+ if early_pip_freeze:
578
+ check_pip = _get_pip_dependency_list(env_check["dependencies"])
579
+ enriched_check_pip = _enrich_pip_deps_from_freeze(
580
+ check_pip, early_pip_freeze
581
+ )
582
+ for dep_entry in env_check["dependencies"]:
583
+ if isinstance(dep_entry, dict) and "pip" in dep_entry:
584
+ dep_entry["pip"] = enriched_check_pip
585
+ break
469
586
  # Determine if the env matches
470
587
  env_needs_rebuild = False
471
588
  existing_conda_deps, existing_pip_deps = _split_env_dependencies(
@@ -552,12 +669,41 @@ def check_env(
552
669
  )
553
670
  # If the env was rebuilt, export the env check
554
671
  res.env_needs_export = env_needs_export
672
+ # Determine whether we need pip freeze output for enriching the stored
673
+ # env check and lock file with exact git URLs/refs.
674
+ needs_pip_freeze = (
675
+ env_needs_export
676
+ or not res.env_exists
677
+ or res.env_needs_rebuild
678
+ or spec_has_git_pip
679
+ )
680
+ pip_freeze: list[str] = []
681
+ if needs_pip_freeze:
682
+ # Reuse the early freeze captured before dep check when possible so
683
+ # we don't run pip twice; fall back to a fresh run after a rebuild.
684
+ if early_pip_freeze and not res.env_needs_rebuild:
685
+ pip_freeze = early_pip_freeze
686
+ else:
687
+ log_func("Running pip freeze to capture git deps")
688
+ try:
689
+ pip_freeze = _run_pip_freeze(env_prefix_path)
690
+ except Exception as e:
691
+ log_func(
692
+ f"pip freeze failed; git dep URLs may be missing: {e}"
693
+ )
555
694
  if env_needs_export:
556
695
  log_func(f"Exporting existing env to {env_check_fpath}")
557
696
  env_check = json.loads(subprocess.check_output(export_cmd).decode())
558
697
  env_check["mtime"] = os.path.getmtime(
559
698
  os.path.normpath(env_check["prefix"])
560
699
  )
700
+ if pip_freeze:
701
+ check_pip = _get_pip_dependency_list(env_check["dependencies"])
702
+ enriched = _enrich_pip_deps_from_freeze(check_pip, pip_freeze)
703
+ for dep_entry in env_check["dependencies"]:
704
+ if isinstance(dep_entry, dict) and "pip" in dep_entry:
705
+ dep_entry["pip"] = enriched
706
+ break
561
707
  with open(env_check_fpath, "w") as f:
562
708
  ryaml.dump(env_check, f)
563
709
  if lock_fpath is None:
@@ -602,8 +748,13 @@ def check_env(
602
748
  editable_pip_deps[pkg_name] = dir_path
603
749
  export_pip_deps = _get_pip_dependency_list(env_export["dependencies"])
604
750
  if export_pip_deps:
751
+ # Enrich with pip freeze to preserve git URLs, then fix editable paths
752
+ if pip_freeze:
753
+ export_pip_deps = _enrich_pip_deps_from_freeze(
754
+ export_pip_deps, pip_freeze
755
+ )
605
756
  for i, dep in enumerate(export_pip_deps):
606
- dep_name = re.split("[=<>]+", dep, maxsplit=1)[0]
757
+ dep_name = _pkg_name_from_dep(dep)
607
758
  if dep_name in editable_pip_deps:
608
759
  path_rel_to_project_root = editable_pip_deps[dep_name]
609
760
  lock_dir = os.path.dirname(lock_fpath)
@@ -613,9 +764,30 @@ def check_env(
613
764
  export_pip_deps[i] = (
614
765
  "-e " + Path(path_rel_to_lock).as_posix()
615
766
  )
767
+ # Write the modified list back (enrichment returns a new list)
768
+ for dep_entry in env_export["dependencies"]:
769
+ if isinstance(dep_entry, dict) and "pip" in dep_entry:
770
+ dep_entry["pip"] = export_pip_deps
771
+ break
616
772
  out_dir = os.path.dirname(lock_fpath)
617
773
  if out_dir:
618
774
  os.makedirs(out_dir, exist_ok=True)
619
775
  with open(lock_fpath, "w") as f:
620
776
  ryaml.dump(env_export, f)
777
+ elif pip_freeze and spec_has_git_pip and os.path.isfile(lock_fpath):
778
+ # The env matched the spec so no full re-export was done, but the
779
+ # existing lock file may pre-date pip freeze enrichment. Update its
780
+ # pip section in place — no conda env export needed.
781
+ with open(lock_fpath) as f:
782
+ lock_data = ryaml.load(f) or {}
783
+ lock_pip = _get_pip_dependency_list(lock_data.get("dependencies", []))
784
+ enriched = _enrich_pip_deps_from_freeze(lock_pip, pip_freeze)
785
+ if enriched != lock_pip:
786
+ log_func("Enriching existing lock file pip section with git URLs")
787
+ for dep_entry in lock_data.get("dependencies", []):
788
+ if isinstance(dep_entry, dict) and "pip" in dep_entry:
789
+ dep_entry["pip"] = enriched
790
+ break
791
+ with open(lock_fpath, "w") as f:
792
+ ryaml.dump(lock_data, f)
621
793
  return res