calkit-python 0.41.14__tar.gz → 0.41.15__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 (351) hide show
  1. {calkit_python-0.41.14 → calkit_python-0.41.15}/AGENTS.md +3 -0
  2. {calkit_python-0.41.14 → calkit_python-0.41.15}/PKG-INFO +1 -1
  3. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/check.py +5 -2
  4. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/main/core.py +14 -1
  5. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/new.py +1 -1
  6. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/notebooks.py +17 -10
  7. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/update.py +1 -1
  8. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/core.py +7 -3
  9. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/julia.py +29 -2
  10. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/notebooks.py +1 -3
  11. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/pipeline.py +32 -8
  12. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_core.py +15 -0
  13. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_julia.py +46 -0
  14. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_pipeline.py +49 -0
  15. calkit_python-0.41.15/vscode-ext/README.md +148 -0
  16. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/package.json +47 -4
  17. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/extension.ts +272 -494
  18. calkit_python-0.41.15/vscode-ext/src/figures/core.ts +93 -0
  19. calkit_python-0.41.15/vscode-ext/src/figures/view.ts +474 -0
  20. calkit_python-0.41.15/vscode-ext/src/pipeline/core.ts +81 -0
  21. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/sidebar.ts +244 -78
  22. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/test/figures.test.ts +41 -1
  23. calkit_python-0.41.15/vscode-ext/src/test/pipeline.test.ts +61 -0
  24. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/types.ts +30 -1
  25. calkit_python-0.41.14/vscode-ext/README.md +0 -66
  26. calkit_python-0.41.14/vscode-ext/src/figures.ts +0 -40
  27. {calkit_python-0.41.14 → calkit_python-0.41.15}/.claude-plugin/marketplace.json +0 -0
  28. {calkit_python-0.41.14 → calkit_python-0.41.15}/.gitattributes +0 -0
  29. {calkit_python-0.41.14 → calkit_python-0.41.15}/.gitignore +0 -0
  30. {calkit_python-0.41.14 → calkit_python-0.41.15}/.pre-commit-config.yaml +0 -0
  31. {calkit_python-0.41.14 → calkit_python-0.41.15}/.prettierignore +0 -0
  32. {calkit_python-0.41.14 → calkit_python-0.41.15}/.python-version +0 -0
  33. {calkit_python-0.41.14 → calkit_python-0.41.15}/.vscode/launch.json +0 -0
  34. {calkit_python-0.41.14 → calkit_python-0.41.15}/.vscode/tasks.json +0 -0
  35. {calkit_python-0.41.14 → calkit_python-0.41.15}/.yarnrc.yml +0 -0
  36. {calkit_python-0.41.14 → calkit_python-0.41.15}/CITATION.cff +0 -0
  37. {calkit_python-0.41.14 → calkit_python-0.41.15}/CODE_OF_CONDUCT.md +0 -0
  38. {calkit_python-0.41.14 → calkit_python-0.41.15}/CONTRIBUTING.md +0 -0
  39. {calkit_python-0.41.14 → calkit_python-0.41.15}/LICENSE +0 -0
  40. {calkit_python-0.41.14 → calkit_python-0.41.15}/Makefile +0 -0
  41. {calkit_python-0.41.14 → calkit_python-0.41.15}/README.md +0 -0
  42. {calkit_python-0.41.14 → calkit_python-0.41.15}/agent-plugin/.claude-plugin/plugin.json +0 -0
  43. {calkit_python-0.41.14 → calkit_python-0.41.15}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
  44. {calkit_python-0.41.14 → calkit_python-0.41.15}/agent-plugin/skills/conventions/SKILL.md +0 -0
  45. {calkit_python-0.41.14 → calkit_python-0.41.15}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
  46. {calkit_python-0.41.14 → calkit_python-0.41.15}/babel.config.js +0 -0
  47. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/__init__.py +0 -0
  48. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/__main__.py +0 -0
  49. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/calc.py +0 -0
  50. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/check.py +0 -0
  51. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/__init__.py +0 -0
  52. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/cloud.py +0 -0
  53. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/config.py +0 -0
  54. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/core.py +0 -0
  55. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/delete.py +0 -0
  56. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/describe.py +0 -0
  57. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/dev.py +0 -0
  58. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/import_.py +0 -0
  59. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/latex.py +0 -0
  60. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/list.py +0 -0
  61. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/main/__init__.py +0 -0
  62. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/main/xr.py +0 -0
  63. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/office.py +0 -0
  64. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/overleaf.py +0 -0
  65. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cli/scheduler.py +0 -0
  66. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/cloud.py +0 -0
  67. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/conda.py +0 -0
  68. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/config.py +0 -0
  69. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/datasets.py +0 -0
  70. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/dependencies.py +0 -0
  71. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/detect.py +0 -0
  72. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/docker.py +0 -0
  73. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/dvc/__init__.py +0 -0
  74. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/dvc/core.py +0 -0
  75. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/dvc/zip.py +0 -0
  76. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/environments.py +0 -0
  77. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/fs.py +0 -0
  78. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/git.py +0 -0
  79. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/github.py +0 -0
  80. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/gui.py +0 -0
  81. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/install.py +0 -0
  82. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/invenio.py +0 -0
  83. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/jupyter.py +0 -0
  84. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/jupyterlab/__init__.py +0 -0
  85. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/jupyterlab/routes.py +0 -0
  86. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/package.json +0 -0
  87. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  88. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  89. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  90. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  91. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  92. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  93. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  94. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  95. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  96. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/remoteEntry.65469af996e7a96aa983.js +0 -0
  97. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/style.js +0 -0
  98. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/labextension/static/third-party-licenses.json +0 -0
  99. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/licenses.py +0 -0
  100. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/magics.py +0 -0
  101. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/matlab.py +0 -0
  102. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/models/__init__.py +0 -0
  103. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/models/core.py +0 -0
  104. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/models/io.py +0 -0
  105. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/models/iteration.py +0 -0
  106. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/models/pipeline.py +0 -0
  107. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/office.py +0 -0
  108. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/ops.py +0 -0
  109. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/overleaf.py +0 -0
  110. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/releases.py +0 -0
  111. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/server.py +0 -0
  112. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/__init__.py +0 -0
  113. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/core.py +0 -0
  114. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/__init__.py +0 -0
  115. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/article/paper.tex +0 -0
  116. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/core.py +0 -0
  117. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/jfm/jfm.bst +0 -0
  118. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/jfm/jfm.cls +0 -0
  119. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  120. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/jfm/paper.tex +0 -0
  121. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/templates/latex/jfm/upmath.sty +0 -0
  122. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/__init__.py +0 -0
  123. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/__init__.py +0 -0
  124. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/main/__init__.py +0 -0
  125. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/main/test_core.py +0 -0
  126. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/main/test_lock.py +0 -0
  127. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/main/test_subprojects.py +0 -0
  128. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/main/test_xr.py +0 -0
  129. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_check.py +0 -0
  130. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_cloud.py +0 -0
  131. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_config.py +0 -0
  132. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_delete.py +0 -0
  133. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_import.py +0 -0
  134. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_latex.py +0 -0
  135. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_list.py +0 -0
  136. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_new.py +0 -0
  137. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_notebooks.py +0 -0
  138. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_overleaf.py +0 -0
  139. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_scheduler.py +0 -0
  140. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/cli/test_update.py +0 -0
  141. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/dvc/__init__.py +0 -0
  142. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/dvc/test_core.py +0 -0
  143. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/dvc/test_zip.py +0 -0
  144. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/jupyterlab/__init__.py +0 -0
  145. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/jupyterlab/test_routes.py +0 -0
  146. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/models/__init__.py +0 -0
  147. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/models/test_core.py +0 -0
  148. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/models/test_iteration.py +0 -0
  149. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/models/test_pipeline.py +0 -0
  150. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_calc.py +0 -0
  151. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_check.py +0 -0
  152. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_cloud.py +0 -0
  153. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_conda.py +0 -0
  154. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_dependencies.py +0 -0
  155. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_detect.py +0 -0
  156. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_docker.py +0 -0
  157. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_environments.py +0 -0
  158. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_fs.py +0 -0
  159. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_git.py +0 -0
  160. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_install.py +0 -0
  161. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_invenio.py +0 -0
  162. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_jupyter.py +0 -0
  163. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_licenses.py +0 -0
  164. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_magics.py +0 -0
  165. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_matlab.py +0 -0
  166. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_notebooks.py +0 -0
  167. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_releases.py +0 -0
  168. {calkit_python-0.41.14 → calkit_python-0.41.15}/calkit/tests/test_templates.py +0 -0
  169. {calkit_python-0.41.14 → calkit_python-0.41.15}/conftest.py +0 -0
  170. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/CNAME +0 -0
  171. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/ai-tools.md +0 -0
  172. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/apps.md +0 -0
  173. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/calculations.md +0 -0
  174. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/calkit-yaml.md +0 -0
  175. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/cli-reference.md +0 -0
  176. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/cloud-integration.md +0 -0
  177. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/datasets.md +0 -0
  178. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/dependencies.md +0 -0
  179. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/environments.md +0 -0
  180. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/examples.md +0 -0
  181. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/governance.md +0 -0
  182. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/help.md +0 -0
  183. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/hpc.md +0 -0
  184. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/c-to-the-k-white.svg +0 -0
  185. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/calkit-fragmentation-compendium.png +0 -0
  186. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/calkit-no-bg.png +0 -0
  187. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/connect-zenodo.png +0 -0
  188. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab/all-green.png +0 -0
  189. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  190. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab/new-env.png +0 -0
  191. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab/new-notebook.png +0 -0
  192. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  193. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/jupyterlab-params.png +0 -0
  194. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/plos-osi-code-2024-03.png +0 -0
  195. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/img/vscode-nb-params.png +0 -0
  196. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/index.md +0 -0
  197. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/installation.md +0 -0
  198. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/jupyterlab.md +0 -0
  199. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/local-server.md +0 -0
  200. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/notebooks.md +0 -0
  201. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/overleaf.md +0 -0
  202. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/pipeline/index.md +0 -0
  203. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/pipeline/manual-steps.md +0 -0
  204. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/pipeline/running-and-logging.md +0 -0
  205. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/quickstart.md +0 -0
  206. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/references.md +0 -0
  207. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/releases.md +0 -0
  208. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/reproducibility.md +0 -0
  209. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/subprojects.md +0 -0
  210. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  211. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/ai-agents.md +0 -0
  212. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/conda-envs.md +0 -0
  213. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/existing-project.md +0 -0
  214. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/first-project.md +0 -0
  215. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/github-actions.md +0 -0
  216. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  217. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  218. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  219. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  220. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  221. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  222. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  223. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  224. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  225. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  226. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  227. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  228. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  229. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  230. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  231. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/create-project.png +0 -0
  232. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  233. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/excel-chart.png +0 -0
  234. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/excel-data.png +0 -0
  235. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  236. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/needs-clone.png +0 -0
  237. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/new-stage.png +0 -0
  238. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  239. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  240. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/status-more-rows.png +0 -0
  241. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  242. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/untracked-data.png +0 -0
  243. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/updated-publication.png +0 -0
  244. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  245. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/office/workflow-page.png +0 -0
  246. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/clone.png +0 -0
  247. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/create-project.png +0 -0
  248. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  249. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  250. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  251. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/new-token.png +0 -0
  252. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/reclone.png +0 -0
  253. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  254. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/quick-actions.png +0 -0
  255. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/run-proc.png +0 -0
  256. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
  257. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
  258. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
  259. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
  260. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
  261. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
  262. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
  263. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/index.md +0 -0
  264. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/jupyterlab.md +0 -0
  265. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/latex-codespaces.md +0 -0
  266. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/matlab.md +0 -0
  267. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/notebook-pipeline.md +0 -0
  268. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/office.md +0 -0
  269. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/openfoam.md +0 -0
  270. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/procedures.md +0 -0
  271. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/tutorials/vscode-slurm-notebook.md +0 -0
  272. {calkit_python-0.41.14 → calkit_python-0.41.15}/docs/version-control.md +0 -0
  273. {calkit_python-0.41.14 → calkit_python-0.41.15}/flake.lock +0 -0
  274. {calkit_python-0.41.14 → calkit_python-0.41.15}/flake.nix +0 -0
  275. {calkit_python-0.41.14 → calkit_python-0.41.15}/install.json +0 -0
  276. {calkit_python-0.41.14 → calkit_python-0.41.15}/jest.config.js +0 -0
  277. {calkit_python-0.41.14 → calkit_python-0.41.15}/jupyter-config/server-config/calkit.json +0 -0
  278. {calkit_python-0.41.14 → calkit_python-0.41.15}/mkdocs.yml +0 -0
  279. {calkit_python-0.41.14 → calkit_python-0.41.15}/package.json +0 -0
  280. {calkit_python-0.41.14 → calkit_python-0.41.15}/pyproject.toml +0 -0
  281. {calkit_python-0.41.14 → calkit_python-0.41.15}/schema/plugin.json +0 -0
  282. {calkit_python-0.41.14 → calkit_python-0.41.15}/scripts/generate-docs-references.py +0 -0
  283. {calkit_python-0.41.14 → calkit_python-0.41.15}/scripts/install.ps1 +0 -0
  284. {calkit_python-0.41.14 → calkit_python-0.41.15}/scripts/install.sh +0 -0
  285. {calkit_python-0.41.14 → calkit_python-0.41.15}/scripts/make-calk9.sh +0 -0
  286. {calkit_python-0.41.14 → calkit_python-0.41.15}/scripts/sync-docs.py +0 -0
  287. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/__tests__/useQueries.spec.ts +0 -0
  288. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/calkit-config.ts +0 -0
  289. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/cell-output-marker.ts +0 -0
  290. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/commit-dialog.tsx +0 -0
  291. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/environment-editor.tsx +0 -0
  292. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/notebook-registration.tsx +0 -0
  293. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/notebook-toolbar.tsx +0 -0
  294. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/pipeline-status-bar.tsx +0 -0
  295. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/project-info-editor.tsx +0 -0
  296. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/sidebar-settings.tsx +0 -0
  297. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/sidebar.tsx +0 -0
  298. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/components/stage-editor.tsx +0 -0
  299. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/feature-flags.ts +0 -0
  300. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/file-browser-menu.ts +0 -0
  301. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  302. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/hooks/useQueries.ts +0 -0
  303. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/icons.ts +0 -0
  304. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/index.ts +0 -0
  305. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/io-tracker.ts +0 -0
  306. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/pipeline-state.ts +0 -0
  307. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/queryClient.ts +0 -0
  308. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/request.ts +0 -0
  309. {calkit_python-0.41.14 → calkit_python-0.41.15}/src/shims-mainmenu.d.ts +0 -0
  310. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/base.css +0 -0
  311. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/cell-output-marker.css +0 -0
  312. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/environment-editor.css +0 -0
  313. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/environment-selector.css +0 -0
  314. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/img/calkit-no-bg.png +0 -0
  315. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/index.css +0 -0
  316. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/index.js +0 -0
  317. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/notebook-toolbar.css +0 -0
  318. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/pipeline-status-bar.css +0 -0
  319. {calkit_python-0.41.14 → calkit_python-0.41.15}/style/sidebar.css +0 -0
  320. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  321. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/nb-julia.ipynb +0 -0
  322. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/nb-params.ipynb +0 -0
  323. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/nb-subdir.ipynb +0 -0
  324. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/pipeline.ipynb +0 -0
  325. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/script.py +0 -0
  326. {calkit_python-0.41.14 → calkit_python-0.41.15}/test/test-log.log +0 -0
  327. {calkit_python-0.41.14 → calkit_python-0.41.15}/tsconfig.json +0 -0
  328. {calkit_python-0.41.14 → calkit_python-0.41.15}/tsconfig.test.json +0 -0
  329. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/.gitignore +0 -0
  330. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/README.md +0 -0
  331. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/jupyter_server_test_config.py +0 -0
  332. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/package.json +0 -0
  333. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/playwright.config.js +0 -0
  334. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/tests/calkit.spec.ts +0 -0
  335. {calkit_python-0.41.14 → calkit_python-0.41.15}/ui-tests/yarn.lock +0 -0
  336. {calkit_python-0.41.14 → calkit_python-0.41.15}/uv.lock +0 -0
  337. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/.gitignore +0 -0
  338. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/.vscodeignore +0 -0
  339. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/CHANGELOG.md +0 -0
  340. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/LICENSE +0 -0
  341. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/images/calkit-icon.svg +0 -0
  342. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/images/calkit-no-bg.png +0 -0
  343. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/package-lock.json +0 -0
  344. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/scripts/set-proposed-api.js +0 -0
  345. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/environments.ts +0 -0
  346. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/notebooks.ts +0 -0
  347. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/test/environments.test.ts +0 -0
  348. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
  349. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/src/test/notebooks.test.ts +0 -0
  350. {calkit_python-0.41.14 → calkit_python-0.41.15}/vscode-ext/tsconfig.json +0 -0
  351. {calkit_python-0.41.14 → calkit_python-0.41.15}/yarn.lock +0 -0
@@ -43,3 +43,6 @@ The only exception here is if splitting up a function makes it easier to
43
43
  write meaningful unit tests.
44
44
  If a function is only used inside one other function, nest it inside the
45
45
  caller at the top.
46
+
47
+ No spaces on either side of en or em dashes used for ranges or pauses in prose,
48
+ respectively.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.41.14
3
+ Version: 0.41.15
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -180,7 +180,8 @@ def _check_julia_env(
180
180
  try:
181
181
  subprocess.check_call(
182
182
  cmd,
183
- env=os.environ.copy() | {"JULIA_LOAD_PATH": "@:@stdlib"},
183
+ env=os.environ.copy()
184
+ | {"JULIA_LOAD_PATH": calkit.julia.load_path()},
184
185
  )
185
186
  except subprocess.CalledProcessError:
186
187
  calkit.environments.save_cache(
@@ -206,7 +207,9 @@ def _check_julia_env(
206
207
  cmd = calkit.julia.ensure_startup_file_disabled_in_command(cmd)
207
208
  try:
208
209
  subprocess.check_call(
209
- cmd, env=os.environ.copy() | {"JULIA_LOAD_PATH": "@:@stdlib"}
210
+ cmd,
211
+ env=os.environ.copy()
212
+ | {"JULIA_LOAD_PATH": calkit.julia.load_path()},
210
213
  )
211
214
  except subprocess.CalledProcessError:
212
215
  calkit.environments.save_cache(
@@ -581,6 +581,18 @@ def get_status(
581
581
  if pipeline_status and not pipeline_status.has_pipeline:
582
582
  typer.echo("This project has no pipeline.")
583
583
  return
584
+ # A failed environment check makes get_status bail out before computing
585
+ # staleness, leaving stale_stages empty. Don't let that masquerade as an
586
+ # up-to-date pipeline---say plainly that the status is unknown.
587
+ elif pipeline_status and pipeline_status.failed_environment_checks:
588
+ # The failing environments were already named in the warning emitted
589
+ # right after computing pipeline_status, so keep this explanatory
590
+ # only rather than repeating the list.
591
+ typer.echo(
592
+ "Pipeline status unavailable until the failed environment "
593
+ "checks above are resolved."
594
+ )
595
+ return
584
596
  elif pipeline_status and pipeline_status.is_stale:
585
597
  typer.echo("Stale stages:")
586
598
  for stage_name in pipeline_status.stale_stage_names:
@@ -2586,7 +2598,8 @@ def run_in_env(
2586
2598
  subprocess.check_call(
2587
2599
  julia_cmd,
2588
2600
  cwd=wdir,
2589
- env=os.environ.copy() | {"JULIA_LOAD_PATH": "@:@stdlib"},
2601
+ env=os.environ.copy()
2602
+ | {"JULIA_LOAD_PATH": calkit.julia.load_path()},
2590
2603
  )
2591
2604
  except subprocess.CalledProcessError:
2592
2605
  raise_error("Failed to run in julia environment")
@@ -2108,7 +2108,7 @@ def new_julia_env(
2108
2108
  except subprocess.CalledProcessError:
2109
2109
  raise_error(f"Failed to install Julia version {julia_version}")
2110
2110
  cmd = ["julia", f"+{julia_version}", f"--project={env_dir}", "-e"]
2111
- install_cmd = f'using Pkg; Pkg.activate("{env_dir}");'
2111
+ install_cmd = f'using Pkg; Pkg.activate("{calkit.julia.escape_string(env_dir)}");'
2112
2112
  for package in packages or []:
2113
2113
  install_cmd += f' Pkg.add("{package}");'
2114
2114
  cmd.append(install_cmd)
@@ -310,14 +310,18 @@ def check_env_kernel(
310
310
  "still failed."
311
311
  )
312
312
  # Don't include version in display_name; IJulia appends it automatically
313
+ # Escape interpolated values for the Julia string literals---notably the
314
+ # Windows project path, whose backslashes are otherwise read as invalid
315
+ # unicode escapes.
316
+ esc = calkit.julia.escape_string
313
317
  julia_cmd = (
314
318
  "import IJulia;"
315
319
  "kp=IJulia.installkernel("
316
- f'"{kernel_name}",'
317
- f'"--project={env_dir_abs}",'
320
+ f'"{esc(kernel_name)}",'
321
+ f'"--project={esc(env_dir_abs)}",'
318
322
  '"--startup-file=no",'
319
- f'displayname="{display_name}",'
320
- 'env=Dict("JULIA_LOAD_PATH" => "@:@stdlib")'
323
+ f'displayname="{esc(display_name)}",'
324
+ f'env=Dict("JULIA_LOAD_PATH" => "{esc(calkit.julia.load_path())}")'
321
325
  ");"
322
326
  "println(kp);"
323
327
  )
@@ -474,8 +478,7 @@ def execute_notebook(
474
478
  # Create this environment and write it to file
475
479
  envs[res.name] = res.env
476
480
  ck_info["environments"] = envs
477
- with open("calkit.yaml", "w") as f:
478
- calkit.ryaml.dump(ck_info, f)
481
+ calkit.save_calkit_info(ck_info)
479
482
  # Detect language from environment
480
483
  if language is None:
481
484
  detected_language = language_from_notebook(path)
@@ -501,16 +504,20 @@ def execute_notebook(
501
504
  raise ValueError(
502
505
  "Language must be one of 'python', 'matlab', 'julia', or 'r'"
503
506
  )
504
- # Try to set kernelspec metadata so execution uses the expected kernel
507
+ # Try to set kernelspec metadata so execution uses the expected kernel.
508
+ # Always read/write as UTF-8: notebooks routinely contain non-ASCII (e.g.,
509
+ # Greek letters such as "ν" in code), and on Windows the default open()
510
+ # encoding is cp1252, which mangles UTF-8 content into mojibake ("ν" ->
511
+ # "ν") and breaks execution.
505
512
  try:
506
- with open(path, "r") as f:
513
+ with open(path, "r", encoding="utf-8") as f:
507
514
  nb_json = json.load(f)
508
515
  metadata = nb_json.setdefault("metadata", {})
509
516
  kernelspec = metadata.setdefault("kernelspec", {})
510
517
  kernelspec["name"] = kernel_name
511
518
  kernelspec["display_name"] = display_name
512
- kernelspec.setdefault("language", language.lower())
513
- with open(path, "w") as f:
519
+ kernelspec["language"] = language.lower()
520
+ with open(path, "w", encoding="utf-8") as f:
514
521
  json.dump(nb_json, f, indent=1)
515
522
  except Exception as e:
516
523
  if verbose:
@@ -746,7 +746,7 @@ def update_julia_env(
746
746
  env_dir = os.path.dirname(spec_path) or "."
747
747
  julia_version = env.get("julia")
748
748
  julia_bin = ["julia", f"+{julia_version}"] if julia_version else ["julia"]
749
- cmds = [f'Pkg.activate("{env_dir}")']
749
+ cmds = [f'Pkg.activate("{calkit.julia.escape_string(env_dir)}")']
750
750
  if add:
751
751
  pkg_list = "[" + ", ".join(f'"{p}"' for p in add) + "]"
752
752
  cmds.append(f"Pkg.add({pkg_list})")
@@ -144,7 +144,9 @@ def load_calkit_info(
144
144
  if wdir is not None:
145
145
  fpath = os.path.join(wdir, fpath)
146
146
  if os.path.isfile(fpath):
147
- with open(fpath) as f:
147
+ # Always read as UTF-8; on Windows the default open() encoding is
148
+ # cp1252, which mangles non-ASCII content (e.g., Greek letters).
149
+ with open(fpath, encoding="utf-8") as f:
148
150
  info = ryaml.load(f)
149
151
  if info is None:
150
152
  info = {}
@@ -166,7 +168,7 @@ def load_calkit_info(
166
168
  if wdir is not None:
167
169
  include_fpath = os.path.join(wdir, include_fpath)
168
170
  if os.path.isfile(include_fpath):
169
- with open(include_fpath) as f:
171
+ with open(include_fpath, encoding="utf-8") as f:
170
172
  include_data = ryaml.load(f)
171
173
  info[kind][obj_name] |= include_data
172
174
  return info
@@ -180,7 +182,9 @@ def save_calkit_info(
180
182
  fpath = "calkit.yaml"
181
183
  if wdir is not None:
182
184
  fpath = os.path.join(wdir, fpath)
183
- with open(fpath, "w") as f:
185
+ # Always write as UTF-8; on Windows the default open() encoding is cp1252,
186
+ # which mangles non-ASCII content (e.g., Greek letters) into mojibake.
187
+ with open(fpath, "w", encoding="utf-8") as f:
184
188
  ryaml.dump(info, f)
185
189
 
186
190
 
@@ -27,9 +27,36 @@ def get_julia_exe() -> str:
27
27
  return "julia"
28
28
 
29
29
 
30
+ def load_path() -> str:
31
+ """Return the ``JULIA_LOAD_PATH`` scoping resolution to the active project
32
+ and stdlib.
33
+
34
+ Julia splits ``JULIA_LOAD_PATH`` on the platform path separator---``;`` on
35
+ Windows and ``:`` elsewhere---so a hardcoded ``:`` leaves ``@stdlib`` off
36
+ the load path on Windows and even stdlib packages (e.g. ``Pkg``) fail to
37
+ resolve. ``os.pathsep`` matches Julia's rule on every platform.
38
+ """
39
+ return os.pathsep.join(["@", "@stdlib"])
40
+
41
+
42
+ def escape_string(value: str) -> str:
43
+ """Escape ``value`` for embedding in a Julia double-quoted string literal.
44
+
45
+ Julia treats ``\\``, ``"`` and ``$`` specially inside double quotes. In
46
+ particular a Windows path like ``C:\\Users\\...`` is otherwise read as an
47
+ invalid ``\\U`` unicode escape, so any value interpolated into Julia source
48
+ (e.g., a ``--project`` path passed via ``julia -e``) must be escaped.
49
+ """
50
+ return value.replace("\\", "\\\\").replace('"', '\\"').replace("$", "\\$")
51
+
52
+
30
53
  def _is_julia_command(arg: str) -> bool:
31
- """Whether ``arg`` is a Julia executable (julia or the juliaup launcher)."""
32
- base = os.path.basename(arg)
54
+ """Whether ``arg`` is a Julia executable (julia or the juliaup launcher).
55
+
56
+ The comparison is case-insensitive so Windows paths like ``julia.EXE``
57
+ (e.g. the ``WindowsApps`` execution-alias shim) are recognized.
58
+ """
59
+ base = os.path.basename(arg).lower()
33
60
  if base.endswith(".exe"):
34
61
  base = base[: -len(".exe")]
35
62
  return base in ("julia", "julialauncher")
@@ -242,11 +242,9 @@ def declare_notebook(
242
242
  }
243
243
  )
244
244
  # Write calkit.yaml
245
- fpath = os.path.join(wdir, "calkit.yaml")
246
245
  ck_info["pipeline"] = pipeline_dict
247
246
  ck_info["notebooks"] = notebooks
248
- with open(fpath, "w") as f:
249
- calkit.ryaml.dump(ck_info, f)
247
+ calkit.save_calkit_info(ck_info, wdir=wdir)
250
248
  if must_be_rerun:
251
249
  raise RuntimeError(
252
250
  f"Notebook stage '{stage_name}' was modified while the pipeline "
@@ -1011,6 +1011,35 @@ def get_output_storage_map(
1011
1011
  return result
1012
1012
 
1013
1013
 
1014
+ def _dump_yaml_if_changed(data, fpath: str, verbose: bool = False) -> bool:
1015
+ """Write ``data`` to ``fpath`` as YAML only if it changes the file.
1016
+
1017
+ ``calkit status`` recompiles the pipeline to DVC on every call, and the VS
1018
+ Code extension polls status continuously. Blindly rewriting an identical
1019
+ file causes two problems: (1) bumping ``dvc.yaml``'s mtime wakes the
1020
+ extension's file watcher, which triggers yet another status poll---an
1021
+ endless loop that keeps re-taking DVC's lock; and (2) the serializer formats
1022
+ slightly differently across platforms and ruamel versions, so the file
1023
+ churns in Git for no real change. Comparison is on the parsed structure, so
1024
+ trivial reformatting is not treated as a change. DVC keys stage staleness on
1025
+ ``dvc.lock`` (not on ``dvc.yaml``'s mtime), so skipping a no-op write is
1026
+ safe. Returns whether the file was written.
1027
+ """
1028
+ if os.path.isfile(fpath):
1029
+ try:
1030
+ with open(fpath) as f:
1031
+ if (calkit.ryaml.load(f) or {}) == data:
1032
+ return False
1033
+ except Exception:
1034
+ # Unreadable or invalid existing YAML: fall through and overwrite.
1035
+ pass
1036
+ if verbose:
1037
+ typer.echo(f"Writing to {os.path.basename(fpath)}")
1038
+ with open(fpath, "w") as f:
1039
+ calkit.ryaml.dump(data, f)
1040
+ return True
1041
+
1042
+
1014
1043
  def to_dvc(
1015
1044
  ck_info: dict | None = None,
1016
1045
  wdir: str | None = None,
@@ -1099,8 +1128,7 @@ def to_dvc(
1099
1128
  with open(dvc_yaml_fpath) as f:
1100
1129
  existing = calkit.ryaml.load(f) or {}
1101
1130
  existing.setdefault("stages", {}).update(wrapper_stages)
1102
- with open(dvc_yaml_fpath, "w") as f:
1103
- calkit.ryaml.dump(existing, f)
1131
+ _dump_yaml_if_changed(existing, dvc_yaml_fpath)
1104
1132
  return wrapper_stages
1105
1133
  # Detect stages with the old ``slurm:`` field before model validation
1106
1134
  # because model_validate pops it in place.
@@ -1194,8 +1222,7 @@ def to_dvc(
1194
1222
  for sname in slurm_field_stages:
1195
1223
  stage_data = ck_yaml_data["pipeline"]["stages"][sname]
1196
1224
  stage_data["scheduler"] = stage_data.pop("slurm")
1197
- with open(ck_yaml_path, "w") as _f:
1198
- calkit.ryaml.dump(ck_yaml_data, _f)
1225
+ _dump_yaml_if_changed(ck_yaml_data, ck_yaml_path)
1199
1226
  # Validate and initialize scheduler options (SLURM/PBS) for stages
1200
1227
  # whose outer environment is a job scheduler. Env defaults are applied
1201
1228
  # at job-submission time, not here.
@@ -1372,10 +1399,7 @@ def to_dvc(
1372
1399
  dvc_stages[stage_name] = stage
1373
1400
  dvc_yaml["stages"] = dvc_stages
1374
1401
  dvc_yaml_fpath = os.path.join(wdir, "dvc.yaml") if wdir else "dvc.yaml"
1375
- with open(dvc_yaml_fpath, "w") as f:
1376
- if verbose:
1377
- typer.echo("Writing to dvc.yaml")
1378
- calkit.ryaml.dump(dvc_yaml, f)
1402
+ _dump_yaml_if_changed(dvc_yaml, dvc_yaml_fpath, verbose=verbose)
1379
1403
  # DVC errors out (FileIsGitIgnored) if dvc.lock is Git-ignored, so make
1380
1404
  # sure it isn't---this keeps both status and pipeline runs working
1381
1405
  calkit.dvc.ensure_dvc_lock_not_ignored(wdir)
@@ -43,6 +43,21 @@ def test_detect_project_name(tmp_dir):
43
43
  assert calkit.detect_project_name() == "someone-else/some-project"
44
44
 
45
45
 
46
+ def test_save_calkit_info_preserves_unicode(tmp_dir):
47
+ # Regression: on Windows, opening calkit.yaml without encoding="utf-8"
48
+ # writes as cp1252, which either errors or corrupts non-ASCII content
49
+ # (e.g., the Greek letter "ν") into mojibake ("ν"). ruamel dumps raw
50
+ # unicode (allow_unicode=True), so the encoding of the file handle matters.
51
+ info = {"name": "p", "owner": "o", "title": "viscosity ν"}
52
+ calkit.save_calkit_info(info)
53
+ # The value must survive the round-trip intact.
54
+ assert calkit.load_calkit_info()["title"] == "viscosity ν"
55
+ # Raw bytes must be the UTF-8 encoding of "ν", not cp1252 mojibake.
56
+ raw = open("calkit.yaml", "rb").read()
57
+ assert "ν".encode("utf-8") in raw
58
+ assert "ν".encode("utf-8") not in raw
59
+
60
+
46
61
  def test_load_calkit_info(tmp_dir, monkeypatch):
47
62
  subpath = "some/project"
48
63
  os.makedirs(subpath)
@@ -1,16 +1,40 @@
1
1
  """Tests for ``calkit.julia``"""
2
2
 
3
+ import os
3
4
  from unittest import mock
4
5
 
5
6
  import pytest
6
7
 
7
8
  from calkit.julia import (
9
+ _is_julia_command,
8
10
  check_version_in_command,
9
11
  ensure_startup_file_disabled_in_command,
12
+ escape_string,
10
13
  get_julia_exe,
14
+ load_path,
11
15
  )
12
16
 
13
17
 
18
+ def test_escape_string_windows_path():
19
+ # A Windows path embedded in a Julia string literal must have its
20
+ # backslashes escaped, otherwise "C:\\Users" is read as an invalid \\U
21
+ # unicode escape (ParseError).
22
+ assert escape_string("C:\\Users\\peteb") == "C:\\\\Users\\\\peteb"
23
+ # Double quotes and dollar signs are the other special characters.
24
+ assert escape_string('a"b') == 'a\\"b'
25
+ assert escape_string("a$b") == "a\\$b"
26
+ # Plain values (e.g. the load path) are unchanged.
27
+ assert escape_string("@;@stdlib") == "@;@stdlib"
28
+
29
+
30
+ def test_load_path_uses_platform_separator():
31
+ # Julia splits JULIA_LOAD_PATH on the platform path separator, so the
32
+ # value must use ';' on Windows and ':' elsewhere (== os.pathsep).
33
+ assert load_path() == f"@{os.pathsep}@stdlib"
34
+ assert "@" in load_path()
35
+ assert "@stdlib" in load_path()
36
+
37
+
14
38
  def test_get_julia_exe(monkeypatch, tmp_path):
15
39
  # Prefer julia on the PATH when present
16
40
  def which_julia(name):
@@ -44,6 +68,28 @@ def test_get_julia_exe(monkeypatch, tmp_path):
44
68
  assert get_julia_exe() == "julia"
45
69
 
46
70
 
71
+ def test_is_julia_command_case_insensitive():
72
+ # Windows resolves the execution-alias shim to an uppercase extension
73
+ # (e.g. ``julia.EXE``); the check must still recognize it.
74
+ assert _is_julia_command("C:/x/julia.EXE")
75
+ assert _is_julia_command("C:/x/Julia.Exe")
76
+ assert _is_julia_command("C:/x/julialauncher.EXE")
77
+ assert _is_julia_command("/usr/bin/julia")
78
+ assert _is_julia_command("julialauncher")
79
+ assert not _is_julia_command("python")
80
+ assert not _is_julia_command("C:/x/python.exe")
81
+
82
+
83
+ def test_disable_startup_file_accepts_uppercase_exe():
84
+ # Regression: julia.EXE (Windows execution alias) was rejected as not a
85
+ # Julia command, failing the env check and masking real pipeline status.
86
+ cmd = ensure_startup_file_disabled_in_command(
87
+ ["C:/x/julia.EXE", "--version"]
88
+ )
89
+ assert cmd[0] == "C:/x/julia.EXE"
90
+ assert "--startup-file=no" in cmd
91
+
92
+
47
93
  def test_check_version_in_command():
48
94
  cmd = ["julia", "+1.11", "--project", "whatever"]
49
95
  # Case 1: juliaup is available
@@ -521,6 +521,55 @@ def test_to_dvc_unignores_dvc_lock(tmp_dir):
521
521
  assert not repo.ignored("dvc.lock")
522
522
 
523
523
 
524
+ def test_to_dvc_skips_no_op_writes(tmp_dir):
525
+ # Compiling to DVC happens on every `calkit status` (which the VS Code
526
+ # extension polls continuously). Rewriting an unchanged dvc.yaml churns Git
527
+ # across platforms (trivial reformatting) and wakes the extension's dvc.yaml
528
+ # watcher into another status poll, an endless loop that keeps re-taking
529
+ # DVC's lock. The write must be skipped unless the parsed content changes.
530
+ subprocess.check_call(["calkit", "init"])
531
+ ck_info = {
532
+ "pipeline": {
533
+ "stages": {
534
+ "s1": {
535
+ "kind": "command",
536
+ "environment": "_system",
537
+ "command": "echo a > a.txt",
538
+ "outputs": ["a.txt"],
539
+ }
540
+ }
541
+ },
542
+ }
543
+ with open("calkit.yaml", "w") as f:
544
+ calkit.ryaml.dump(ck_info, f)
545
+ # First compile creates dvc.yaml.
546
+ calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
547
+ assert os.path.isfile("dvc.yaml")
548
+ # Pin an old mtime; a no-op recompile of identical content must leave it.
549
+ pinned = 1000000000.0
550
+ os.utime("dvc.yaml", (pinned, pinned))
551
+ calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
552
+ assert os.path.getmtime("dvc.yaml") == pinned
553
+ # A semantically-equal but reformatted/commented file is also left as-is, so
554
+ # trivial cross-platform reformatting doesn't churn the file in Git.
555
+ with open("dvc.yaml") as f:
556
+ original = f.read()
557
+ with open("dvc.yaml", "w") as f:
558
+ f.write("# hand-written comment\n" + original)
559
+ with open("dvc.yaml") as f:
560
+ reformatted = f.read()
561
+ os.utime("dvc.yaml", (pinned, pinned))
562
+ calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
563
+ with open("dvc.yaml") as f:
564
+ assert f.read() == reformatted
565
+ assert os.path.getmtime("dvc.yaml") == pinned
566
+ # A real change, though, is written.
567
+ ck_info["pipeline"]["stages"]["s1"]["command"] = "echo b > a.txt"
568
+ calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
569
+ with open("dvc.yaml") as f:
570
+ assert f.read() != reformatted
571
+
572
+
524
573
  def test_sbatch_stage_to_dvc(tmp_dir):
525
574
  """Cover the SLURM stage compilation paths.
526
575
 
@@ -0,0 +1,148 @@
1
+ # Calkit for VS Code
2
+
3
+ Turn a research or analytical project into a navigable, reproducible
4
+ system.
5
+
6
+ Most projects where the output artifacts (figures, datasets, results,
7
+ papers, presentations) are what matter accumulate a tangle of scripts,
8
+ notebooks, environments, and data. Calkit gives that tangle a structure: every
9
+ artifact is traceable to the pipeline stage that produced it, every stage to its
10
+ code and environment. This extension is the integration layer on top—it
11
+ surfaces the whole system in one place, lets you traverse between layers
12
+ (artifact → stage → environment → source), and lets you make changes at any
13
+ layer and re-run to keep everything in sync.
14
+
15
+ ## The Calkit sidebar: a holistic view of the project
16
+
17
+ The **Calkit** activity-bar view presents the project as connected sections:
18
+
19
+ - **Questions**: The research questions the project sets out to answer.
20
+ - **Environments**: Every computational environment (uv, Pixi, venv, conda,
21
+ renv, Docker, Julia, MATLAB, SSH, SLURM/PBS, and nested combinations).
22
+ - **Pipeline**: The stages that produce the project's artifacts, each showing
23
+ live status (up to date, **stale**, or running).
24
+ - **Notebooks**: Jupyter notebooks and the environments/stages they belong to.
25
+ - **Figures**, **Datasets**, **Results**, **Publications**, and
26
+ **Presentations**: The output artifacts, each annotated with the stage (or
27
+ import source) it came from.
28
+
29
+ Items are cross-linked, so you can traverse the system in any direction:
30
+ expand a figure to jump to the stage that made it, jump from a stage to its
31
+ environment, open the stage's script/notebook, or open an output file—then run
32
+ the stage again. A badge and per-section warnings flag anything that needs
33
+ attention (stale outputs, artifacts with no defined source, notebooks with no
34
+ environment).
35
+
36
+ Use the toolbar to filter the tree across all sections, refresh status,
37
+ hide/show sections, open `calkit.yaml`, or initialize a new Calkit
38
+ project in a plain folder.
39
+
40
+ ## Provenance & traceability
41
+
42
+ - **Show Source**: From an open figure, PDF, dataset, or other pipeline output,
43
+ jump straight to the producing stage in the sidebar (and into its source).
44
+ - **Figure source links**: In Quarto (`.qmd`) and LaTeX (`.tex`) documents, a
45
+ "Source: \<stage\>" CodeLens appears above each `![](…)` / `\includegraphics{…}`
46
+ that references a pipeline output; right-click also offers **Go to Figure
47
+ Source**. (From a compiled PDF, LaTeX Workshop's reverse-SyncTeX takes you to
48
+ the `\includegraphics` line, where these actions take over.)
49
+ - **Stale-output awareness**: Outputs whose stage needs re-running are flagged
50
+ in the sidebar and in the file explorer.
51
+ - **File history**: View a tracked file's history from the sidebar or explorer.
52
+ - **Scheduler logs**: Stages that run under SLURM/PBS surface their log file in
53
+ the tree so you can open it with a click.
54
+
55
+ ## Pipeline
56
+
57
+ - Run the whole pipeline, an individual stage, or the stage for the file
58
+ you're editing.
59
+ - Define new stages graphically, including turning a notebook, script, or an
60
+ existing artifact into a reproducible stage; edit existing stages.
61
+ - Visualize the pipeline DAG.
62
+ - Open the rendered PDF of a LaTeX/Quarto stage from its source.
63
+
64
+ ## Environments
65
+
66
+ - Create and edit environments graphically (package lists, spec files, base
67
+ images, etc.) for uv, Pixi, venv, conda, renv, Docker, Julia, MATLAB, SSH, and
68
+ SLURM/PBS.
69
+ - Select a notebook's environment and let the extension register/select the
70
+ matching Jupyter kernel—or edit that environment right from the notebook
71
+ toolbar.
72
+ - Use nested environments like `slurm:main` for notebook jobs that need to,
73
+ e.g., to reserve GPUs on a cluster.
74
+ - Start, stop, and restart notebook server sessions for SLURM- and
75
+ Docker-backed workflows from the notebook toolbar.
76
+
77
+ ## Figures & artifacts
78
+
79
+ - Browse figures in a gallery and carousel, including interactive Plotly
80
+ figures (`.json`), with per-figure provenance and a one-click jump to the
81
+ producing stage.
82
+ - Preview Plotly JSON files in a dedicated **Plotly Preview** editor.
83
+ - Open a notebook's executed HTML output.
84
+
85
+ ## Getting started
86
+
87
+ 1. Open a project folder. If it isn't a Calkit project yet, the sidebar offers
88
+ **Initialize Calkit Project**.
89
+ 2. Explore the Calkit sidebar to see environments, the pipeline, and
90
+ artifacts.
91
+ 3. From a notebook, run **Calkit: Select Notebook Environment**, pick or create
92
+ an environment (providing SLURM options like `--gpus`/`--time` if needed), and
93
+ the extension registers/selects the kernel and connects the session.
94
+
95
+ ## Requirements
96
+
97
+ - VS Code with the Jupyter extension installed.
98
+ - The Calkit CLI available on your `PATH`. If it's missing or too old, the
99
+ extension prompts with install/upgrade options.
100
+
101
+ ## How environments are stored in `calkit.yaml`
102
+
103
+ Selecting an environment for a notebook writes it to `calkit.yaml`, updating
104
+ either `notebooks` or `pipeline.stages` depending on whether the notebook is part
105
+ of a pipeline stage.
106
+
107
+ ```yaml
108
+ # Standalone notebook
109
+ notebooks:
110
+ - path: my-notebook.ipynb
111
+ environment: my-env
112
+
113
+ # Notebook that is a pipeline stage
114
+ pipeline:
115
+ stages:
116
+ my-notebook:
117
+ kind: jupyter-notebook
118
+ notebook_path: my-notebook.ipynb
119
+ environment: my-env
120
+ ```
121
+
122
+ ## Settings
123
+
124
+ - `calkit.autoRefreshStatus`: Automatically run `calkit status` to refresh
125
+ pipeline staleness when project files change (disable if frequent status
126
+ checks interfere with Git operations; you can still refresh manually).
127
+ - `calkit.notebook.defaultJupyterPort`: Default port for Calkit-backed Jupyter
128
+ servers.
129
+ - `calkit.sidebar.hiddenSections`: Sidebar sections to hide (managed via the
130
+ sidebar's **Manage Sections** action).
131
+
132
+ ## Commands
133
+
134
+ Most actions are available from the sidebar, notebook toolbar, and editor
135
+ context menus. Highlights, all under the **Calkit** category in the command
136
+ palette:
137
+
138
+ - Project: **Initialize Calkit Project**, **Open calkit.yaml**, **Save**,
139
+ **Refresh**, **Filter** / **Clear Filter**, **Manage Sections**
140
+ - Pipeline: **Run Pipeline**, **Run Stage**, **New Stage**, **Edit Stage**,
141
+ **Show Pipeline DAG**, **Open Rendered PDF**, **Show Source**, **Go to Figure
142
+ Source**
143
+ - Environments: **Create Environment**, **Edit Environment**, **Select Notebook
144
+ Environment**, **Edit Notebook Environment**
145
+ - Notebook sessions: **Start Jupyter SLURM Job**, **Stop Jupyter SLURM Job**,
146
+ **Restart Notebook Server**, **Open Executed HTML**
147
+ - Figures: **Browse Figures**, **Open Plotly Preview**
148
+ - Artifacts: **Define Pipeline Stage**, **Define Import**, **View File History**