calkit-python 0.34.1__tar.gz → 0.34.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.
- {calkit_python-0.34.1 → calkit_python-0.34.2}/PKG-INFO +1 -1
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/main.py +17 -6
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/notebooks.py +26 -10
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/detect.py +3 -1
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/environments.py +343 -20
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/jupyterlab/routes.py +9 -3
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/models/pipeline.py +2 -2
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_main.py +51 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/models/test_pipeline.py +6 -4
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_detect.py +4 -3
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_environments.py +136 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/.gitignore +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/.pre-commit-config.yaml +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/.prettierignore +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/.python-version +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/.yarnrc.yml +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/CITATION.cff +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/CODE_OF_CONDUCT.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/CONTRIBUTING.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/LICENSE +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/Makefile +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/README.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/babel.config.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/__main__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/calc.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/check.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/check.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/cloud.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/config.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/describe.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/import_.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/latex.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/list.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/new.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/office.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/overleaf.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/slurm.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cli/update.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/cloud.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/conda.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/config.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/datasets.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/docker.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/dvc.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/git.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/github.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/gui.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/invenio.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/jupyter.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/jupyterlab/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/package.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/909.99b13e1e3c2a6e1ceb30.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/remoteEntry.63846b8de2465d49bd89.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/style.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/labextension/static/third-party-licenses.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/licenses.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/magics.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/matlab.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/models/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/models/core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/models/io.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/models/iteration.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/notebooks.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/office.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/ops.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/overleaf.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/pipeline.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/releases.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/server.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_check.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_config.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_import.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_latex.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_notebooks.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/cli/test_overleaf.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/jupyterlab/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/jupyterlab/test_routes.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/models/__init__.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/models/test_iteration.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_git.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_invenio.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_matlab.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_notebooks.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_pipeline.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/conftest.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/CNAME +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/apps.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/calculations.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/calkit-yaml.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/cli-reference.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/cloud-integration.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/datasets.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/dependencies.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/environments.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/examples.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/governance.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/help.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/calkit-fragmentation-compendium.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/connect-zenodo.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab/all-green.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab/collect-data-stale.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab/new-env.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab/new-notebook.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab/pipeline-badge.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/jupyterlab-params.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/plos-osi-code-2024-03.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/img/vscode-nb-params.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/index.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/installation.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/jupyterlab.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/local-server.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/notebooks.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/overleaf.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/pipeline/index.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/pipeline/running-and-logging.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/pipeline/slurm.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/quickstart.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/references.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/releases.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/reproducibility.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/existing-project.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/github-actions.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/actions-repo-secrets.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/quick-actions.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/index.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/jupyterlab.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/latex-codespaces.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/office.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/docs/version-control.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/install.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/jest.config.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/jupyter-config/server-config/calkit.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/mkdocs.yml +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/package.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/pyproject.toml +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/schema/plugin.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/scripts/install.ps1 +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/scripts/install.sh +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/scripts/make-calk9.sh +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/scripts/sync-docs.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/__tests__/useQueries.spec.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/calkit-config.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/cell-output-marker.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/commit-dialog.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/environment-editor.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/notebook-registration.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/notebook-toolbar.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/pipeline-status-bar.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/project-info-editor.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/sidebar-settings.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/sidebar.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/components/stage-editor.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/feature-flags.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/file-browser-menu.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/hooks/__tests__/useQueries.test.tsx +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/hooks/useQueries.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/icons.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/index.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/io-tracker.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/pipeline-state.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/queryClient.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/request.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/src/shims-mainmenu.d.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/base.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/cell-output-marker.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/environment-editor.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/environment-selector.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/img/calkit-no-bg.png +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/index.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/index.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/notebook-toolbar.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/pipeline-status-bar.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/style/sidebar.css +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/dvc-md5-dir/osx-arm64.txt +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/nb-julia.ipynb +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/nb-params.ipynb +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/nb-subdir.ipynb +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/pipeline.ipynb +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/script.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/test/test-log.log +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/tsconfig.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/tsconfig.test.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/.gitignore +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/README.md +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/jupyter_server_test_config.py +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/package.json +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/playwright.config.js +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/tests/calkit.spec.ts +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/ui-tests/yarn.lock +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/uv.lock +0 -0
- {calkit_python-0.34.1 → calkit_python-0.34.2}/yarn.lock +0 -0
|
@@ -1835,9 +1835,9 @@ def execute_and_record(
|
|
|
1835
1835
|
list[str],
|
|
1836
1836
|
typer.Argument(
|
|
1837
1837
|
help="Command to execute and record. "
|
|
1838
|
-
"If the first argument is a script or
|
|
1839
|
-
"it will be treated as a stage with that
|
|
1840
|
-
"the target."
|
|
1838
|
+
"If the first argument is a script, notebook or LaTeX file, "
|
|
1839
|
+
"it will be treated as a stage with that file as "
|
|
1840
|
+
"the target. Any command, including arguments, is supported."
|
|
1841
1841
|
),
|
|
1842
1842
|
],
|
|
1843
1843
|
environment: Annotated[
|
|
@@ -1895,6 +1895,17 @@ def execute_and_record(
|
|
|
1895
1895
|
),
|
|
1896
1896
|
),
|
|
1897
1897
|
] = False,
|
|
1898
|
+
force: Annotated[
|
|
1899
|
+
bool,
|
|
1900
|
+
typer.Option(
|
|
1901
|
+
"--force",
|
|
1902
|
+
"-f",
|
|
1903
|
+
help="Force running stage even if it's up-to-date.",
|
|
1904
|
+
),
|
|
1905
|
+
] = False,
|
|
1906
|
+
verbose: Annotated[
|
|
1907
|
+
bool, typer.Option("--verbose", "-v", help="Print verbose output.")
|
|
1908
|
+
] = False,
|
|
1898
1909
|
):
|
|
1899
1910
|
"""Execute a command and if successful, record in the pipeline."""
|
|
1900
1911
|
import io
|
|
@@ -2152,8 +2163,8 @@ def execute_and_record(
|
|
|
2152
2163
|
# If we created an environment from dependencies, write the spec file
|
|
2153
2164
|
if env_result.created_from_dependencies and not dry_run:
|
|
2154
2165
|
typer.echo(
|
|
2155
|
-
"No existing environment detected
|
|
2156
|
-
"Attempting to create one based on detected dependencies
|
|
2166
|
+
"No existing environment detected; "
|
|
2167
|
+
"Attempting to create one based on detected dependencies"
|
|
2157
2168
|
)
|
|
2158
2169
|
if env_result.dependencies:
|
|
2159
2170
|
typer.echo(
|
|
@@ -2302,7 +2313,7 @@ def execute_and_record(
|
|
|
2302
2313
|
f"Adding stage to pipeline and attempting to execute:"
|
|
2303
2314
|
f"\n{indented_yaml}"
|
|
2304
2315
|
)
|
|
2305
|
-
run(targets=[stage_name])
|
|
2316
|
+
run(targets=[stage_name], force=force, verbose=verbose)
|
|
2306
2317
|
except Exception as e:
|
|
2307
2318
|
# If the stage failed, write the old ck_info back to calkit.yaml to
|
|
2308
2319
|
# remove the stage that we added
|
|
@@ -15,7 +15,7 @@ from typing_extensions import Annotated
|
|
|
15
15
|
|
|
16
16
|
import calkit
|
|
17
17
|
import calkit.notebooks
|
|
18
|
-
from calkit.cli.core import raise_error
|
|
18
|
+
from calkit.cli.core import raise_error, warn
|
|
19
19
|
|
|
20
20
|
notebooks_app = typer.Typer(no_args_is_help=True)
|
|
21
21
|
|
|
@@ -115,7 +115,7 @@ def check_env_kernel(
|
|
|
115
115
|
verbose: Annotated[
|
|
116
116
|
bool, typer.Option("--verbose", "-v", help="Print verbose output.")
|
|
117
117
|
] = False,
|
|
118
|
-
):
|
|
118
|
+
) -> tuple[str, str]:
|
|
119
119
|
"""Check that an environment has a registered Jupyter kernel."""
|
|
120
120
|
from calkit.cli.check import check_environment
|
|
121
121
|
from calkit.cli.main import run_in_env
|
|
@@ -159,7 +159,7 @@ def check_env_kernel(
|
|
|
159
159
|
verbose=verbose,
|
|
160
160
|
relaxed_check=True,
|
|
161
161
|
)
|
|
162
|
-
return kernel_name
|
|
162
|
+
return kernel_name, display_name
|
|
163
163
|
elif language == "r":
|
|
164
164
|
cmd = [
|
|
165
165
|
"Rscript",
|
|
@@ -176,7 +176,7 @@ def check_env_kernel(
|
|
|
176
176
|
verbose=verbose,
|
|
177
177
|
relaxed_check=True,
|
|
178
178
|
)
|
|
179
|
-
return kernel_name
|
|
179
|
+
return kernel_name, display_name
|
|
180
180
|
elif language == "julia":
|
|
181
181
|
if not no_check:
|
|
182
182
|
check_environment(env_name=env_name, verbose=verbose)
|
|
@@ -214,9 +214,10 @@ def check_env_kernel(
|
|
|
214
214
|
kernel_path = res.stdout.strip()
|
|
215
215
|
typer.echo(f"Registered IJulia kernel at: {kernel_path}")
|
|
216
216
|
kernel_name = os.path.basename(kernel_path)
|
|
217
|
-
return kernel_name
|
|
217
|
+
return kernel_name, display_name
|
|
218
218
|
else:
|
|
219
219
|
raise_error(f"{language} not supported")
|
|
220
|
+
return "", "" # For typing analysis since raise_error exits
|
|
220
221
|
|
|
221
222
|
|
|
222
223
|
@notebooks_app.command("exec", help="Alias for 'execute'.")
|
|
@@ -345,14 +346,10 @@ def execute_notebook(
|
|
|
345
346
|
env = envs[env_name]
|
|
346
347
|
language = language_from_env(env) or "python"
|
|
347
348
|
typer.echo(f"Using {language} as notebook language")
|
|
348
|
-
if language.lower() not in ["python", "matlab", "julia", "r"]:
|
|
349
|
-
raise ValueError(
|
|
350
|
-
"Language must be one of 'python', 'matlab', 'julia', or 'r'"
|
|
351
|
-
)
|
|
352
349
|
# First, ensure the specified environment has a kernel we can use
|
|
353
350
|
# We need to check the environment type and create the kernel if needed
|
|
354
351
|
if language.lower() in ["python", "julia", "r"]:
|
|
355
|
-
kernel_name = check_env_kernel(
|
|
352
|
+
kernel_name, display_name = check_env_kernel(
|
|
356
353
|
env_name=env_name,
|
|
357
354
|
no_check=no_check,
|
|
358
355
|
verbose=verbose,
|
|
@@ -360,6 +357,25 @@ def execute_notebook(
|
|
|
360
357
|
)
|
|
361
358
|
elif language.lower() == "matlab":
|
|
362
359
|
kernel_name = "jupyter_matlab_kernel"
|
|
360
|
+
display_name = "MATLAB"
|
|
361
|
+
else:
|
|
362
|
+
raise ValueError(
|
|
363
|
+
"Language must be one of 'python', 'matlab', 'julia', or 'r'"
|
|
364
|
+
)
|
|
365
|
+
# Try to set kernelspec metadata so execution uses the expected kernel
|
|
366
|
+
try:
|
|
367
|
+
with open(path, "r") as f:
|
|
368
|
+
nb_json = json.load(f)
|
|
369
|
+
metadata = nb_json.setdefault("metadata", {})
|
|
370
|
+
kernelspec = metadata.setdefault("kernelspec", {})
|
|
371
|
+
kernelspec["name"] = kernel_name
|
|
372
|
+
kernelspec["display_name"] = display_name
|
|
373
|
+
kernelspec.setdefault("language", language.lower())
|
|
374
|
+
with open(path, "w") as f:
|
|
375
|
+
json.dump(nb_json, f, indent=1)
|
|
376
|
+
except Exception as e:
|
|
377
|
+
if verbose:
|
|
378
|
+
warn(f"Warning: failed to set kernelspec metadata: {e}")
|
|
363
379
|
# We can't handle parameters unless language is Python, Julia, or R
|
|
364
380
|
if language.lower() not in ["python", "julia", "r"]:
|
|
365
381
|
if params or params_json is not None or params_base64 is not None:
|
|
@@ -1214,7 +1214,6 @@ def generate_stage_name(cmd: list[str]) -> str:
|
|
|
1214
1214
|
if remaining_args:
|
|
1215
1215
|
args_part = "-".join(remaining_args)
|
|
1216
1216
|
stage_name += "-" + args_part
|
|
1217
|
-
|
|
1218
1217
|
# Convert to kebab-case
|
|
1219
1218
|
stage_name = stage_name.replace("_", "-").lower()
|
|
1220
1219
|
# Replace dots with dashes (except file extensions which are
|
|
@@ -1229,6 +1228,9 @@ def generate_stage_name(cmd: list[str]) -> str:
|
|
|
1229
1228
|
stage_name = re.sub(r"-+", "-", stage_name)
|
|
1230
1229
|
# Remove leading/trailing dashes
|
|
1231
1230
|
stage_name = stage_name.strip("-")
|
|
1231
|
+
# If any args include the .ipynb extension, add a -notebook suffix
|
|
1232
|
+
if any(arg.endswith(".ipynb") for arg in cmd):
|
|
1233
|
+
stage_name += "-notebook"
|
|
1232
1234
|
return stage_name
|
|
1233
1235
|
|
|
1234
1236
|
|
|
@@ -10,6 +10,8 @@ import tempfile
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import cast
|
|
12
12
|
|
|
13
|
+
import toml
|
|
14
|
+
import yaml
|
|
13
15
|
from pydantic import BaseModel
|
|
14
16
|
from sqlitedict import SqliteDict
|
|
15
17
|
|
|
@@ -1055,6 +1057,214 @@ Title: Auto-generated R environment
|
|
|
1055
1057
|
return content
|
|
1056
1058
|
|
|
1057
1059
|
|
|
1060
|
+
def extract_dependencies_from_spec_file(
|
|
1061
|
+
spec_path: str, language: str | None = None
|
|
1062
|
+
) -> list[str]:
|
|
1063
|
+
"""Extract dependencies from an environment spec file.
|
|
1064
|
+
|
|
1065
|
+
Parameters
|
|
1066
|
+
----------
|
|
1067
|
+
spec_path : str
|
|
1068
|
+
Path to the spec file (requirements.txt, Project.toml, etc.).
|
|
1069
|
+
language : str | None
|
|
1070
|
+
Language hint to help identify the format. If None, will be inferred
|
|
1071
|
+
from the file path.
|
|
1072
|
+
|
|
1073
|
+
Returns
|
|
1074
|
+
-------
|
|
1075
|
+
list[str]
|
|
1076
|
+
List of package/dependency names.
|
|
1077
|
+
"""
|
|
1078
|
+
if not os.path.exists(spec_path):
|
|
1079
|
+
return []
|
|
1080
|
+
try:
|
|
1081
|
+
with open(spec_path, "r", encoding="utf-8") as f:
|
|
1082
|
+
content = f.read()
|
|
1083
|
+
except (IOError, UnicodeDecodeError):
|
|
1084
|
+
return []
|
|
1085
|
+
# Determine format from filename if not provided
|
|
1086
|
+
if language is None:
|
|
1087
|
+
if spec_path.endswith("requirements.txt"):
|
|
1088
|
+
language = "python-requirements"
|
|
1089
|
+
elif spec_path.endswith("pyproject.toml"):
|
|
1090
|
+
language = "python-pyproject"
|
|
1091
|
+
elif spec_path.endswith("Project.toml"):
|
|
1092
|
+
language = "julia"
|
|
1093
|
+
elif spec_path.endswith("DESCRIPTION"):
|
|
1094
|
+
language = "r"
|
|
1095
|
+
elif spec_path.endswith("environment.yml"):
|
|
1096
|
+
language = "conda"
|
|
1097
|
+
dependencies: list[str] = []
|
|
1098
|
+
if language in ["python-requirements"]:
|
|
1099
|
+
# Parse requirements.txt
|
|
1100
|
+
for line in content.split("\n"):
|
|
1101
|
+
line = line.strip()
|
|
1102
|
+
if line and not line.startswith("#"):
|
|
1103
|
+
# Extract package name (before any version specifiers)
|
|
1104
|
+
pkg = line.split("[")[0].split("==")[0].split(">=")[0]
|
|
1105
|
+
pkg = pkg.split("<=")[0].split(">")[0].split("<")[0]
|
|
1106
|
+
pkg = pkg.split("~=")[0].strip()
|
|
1107
|
+
if pkg:
|
|
1108
|
+
dependencies.append(pkg)
|
|
1109
|
+
elif language == "python-pyproject":
|
|
1110
|
+
# Parse pyproject.toml to extract dependencies
|
|
1111
|
+
try:
|
|
1112
|
+
data = toml.loads(content)
|
|
1113
|
+
project_deps = data.get("project", {}).get("dependencies", [])
|
|
1114
|
+
for dep in project_deps:
|
|
1115
|
+
# Extract package name (before any version specifiers)
|
|
1116
|
+
pkg = dep.split("[")[0].split("==")[0].split(">=")[0]
|
|
1117
|
+
pkg = pkg.split("<=")[0].split(">")[0].split("<")[0]
|
|
1118
|
+
pkg = pkg.split("~=")[0].strip()
|
|
1119
|
+
if pkg:
|
|
1120
|
+
dependencies.append(pkg)
|
|
1121
|
+
except Exception:
|
|
1122
|
+
pass
|
|
1123
|
+
elif language == "julia":
|
|
1124
|
+
# Parse Julia Project.toml for [deps] section
|
|
1125
|
+
try:
|
|
1126
|
+
data = toml.loads(content)
|
|
1127
|
+
# Package names are the keys in the [deps] section
|
|
1128
|
+
deps_section = data.get("deps", {})
|
|
1129
|
+
if isinstance(deps_section, dict):
|
|
1130
|
+
dependencies = list(deps_section.keys())
|
|
1131
|
+
except Exception:
|
|
1132
|
+
pass
|
|
1133
|
+
elif language == "r":
|
|
1134
|
+
# Parse R DESCRIPTION file for Imports/Depends fields,
|
|
1135
|
+
# correctly handling multi-line (continued) fields where
|
|
1136
|
+
# continuation lines start with whitespace.
|
|
1137
|
+
lines = content.splitlines()
|
|
1138
|
+
i = 0
|
|
1139
|
+
while i < len(lines):
|
|
1140
|
+
raw_line = lines[i]
|
|
1141
|
+
stripped = raw_line.lstrip()
|
|
1142
|
+
if stripped.startswith(("Imports:", "Depends:")):
|
|
1143
|
+
# Extract the text after the field name and colon
|
|
1144
|
+
_, after_colon = stripped.split(":", 1)
|
|
1145
|
+
pkg_chunks: list[str] = [after_colon.strip()]
|
|
1146
|
+
# Collect continuation lines that start with whitespace
|
|
1147
|
+
j = i + 1
|
|
1148
|
+
while j < len(lines) and (
|
|
1149
|
+
lines[j].startswith(" ") or lines[j].startswith("\t")
|
|
1150
|
+
):
|
|
1151
|
+
pkg_chunks.append(lines[j].strip())
|
|
1152
|
+
j += 1
|
|
1153
|
+
# Join all chunks into a single dependency string
|
|
1154
|
+
pkg_str = " ".join(pkg_chunks)
|
|
1155
|
+
# Split on commas to get individual package entries
|
|
1156
|
+
pkgs = [p.strip() for p in pkg_str.split(",") if p.strip()]
|
|
1157
|
+
for pkg in pkgs:
|
|
1158
|
+
# Remove version specifications if present
|
|
1159
|
+
pkg = pkg.split("(", 1)[0].strip()
|
|
1160
|
+
if pkg:
|
|
1161
|
+
dependencies.append(pkg)
|
|
1162
|
+
# Continue parsing from the first non-continuation line
|
|
1163
|
+
i = j
|
|
1164
|
+
continue
|
|
1165
|
+
i += 1
|
|
1166
|
+
elif language == "conda":
|
|
1167
|
+
# Parse conda environment.yml
|
|
1168
|
+
try:
|
|
1169
|
+
data = yaml.safe_load(content)
|
|
1170
|
+
deps = data.get("dependencies", [])
|
|
1171
|
+
for dep in deps:
|
|
1172
|
+
if isinstance(dep, str):
|
|
1173
|
+
# Extract package name (before version spec)
|
|
1174
|
+
pkg = dep.split("==")[0].split(">=")[0].split("<=")[0]
|
|
1175
|
+
pkg = pkg.split("=")[0].strip()
|
|
1176
|
+
if "::" in pkg:
|
|
1177
|
+
pkg = pkg.split("::", 1)[1]
|
|
1178
|
+
if pkg:
|
|
1179
|
+
dependencies.append(pkg)
|
|
1180
|
+
elif isinstance(dep, dict):
|
|
1181
|
+
# Handle pip dependencies nested as {pip: [...]}
|
|
1182
|
+
pip_deps = dep.get("pip", [])
|
|
1183
|
+
for pip_dep in pip_deps:
|
|
1184
|
+
if isinstance(pip_dep, str):
|
|
1185
|
+
pkg = (
|
|
1186
|
+
pip_dep.split("==")[0]
|
|
1187
|
+
.split(">=")[0]
|
|
1188
|
+
.split("<=")[0]
|
|
1189
|
+
.split("[")[0]
|
|
1190
|
+
.strip()
|
|
1191
|
+
)
|
|
1192
|
+
if "::" in pkg:
|
|
1193
|
+
pkg = pkg.split("::", 1)[1]
|
|
1194
|
+
if pkg:
|
|
1195
|
+
dependencies.append(pkg)
|
|
1196
|
+
except Exception:
|
|
1197
|
+
pass
|
|
1198
|
+
# Remove duplicates and sort
|
|
1199
|
+
return sorted(list(set(dependencies)))
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
def env_has_superset_dependencies(
|
|
1203
|
+
env: dict,
|
|
1204
|
+
required_deps: list[str],
|
|
1205
|
+
env_spec_path: str | None = None,
|
|
1206
|
+
strict: bool = False,
|
|
1207
|
+
) -> bool:
|
|
1208
|
+
"""Check if an environment has a superset of required dependencies.
|
|
1209
|
+
|
|
1210
|
+
Parameters
|
|
1211
|
+
----------
|
|
1212
|
+
env : dict
|
|
1213
|
+
Environment dict from calkit.yaml with 'kind' and 'path' keys.
|
|
1214
|
+
required_deps : list[str]
|
|
1215
|
+
List of required dependencies to check for.
|
|
1216
|
+
env_spec_path : str | None
|
|
1217
|
+
Path to the environment spec file. If None, will use env.get("path").
|
|
1218
|
+
strict : bool
|
|
1219
|
+
If True, require the spec file to exist and have extractable
|
|
1220
|
+
dependencies. If False, be optimistic when spec file doesn't exist.
|
|
1221
|
+
|
|
1222
|
+
Returns
|
|
1223
|
+
-------
|
|
1224
|
+
bool
|
|
1225
|
+
True if the environment contains all required dependencies,
|
|
1226
|
+
False otherwise.
|
|
1227
|
+
"""
|
|
1228
|
+
if not required_deps:
|
|
1229
|
+
# No dependencies to check, so any environment works
|
|
1230
|
+
return True
|
|
1231
|
+
if env_spec_path is None:
|
|
1232
|
+
env_spec_path = env.get("path")
|
|
1233
|
+
if not env_spec_path:
|
|
1234
|
+
# No path to check
|
|
1235
|
+
if strict:
|
|
1236
|
+
return False
|
|
1237
|
+
return True
|
|
1238
|
+
if not os.path.exists(env_spec_path):
|
|
1239
|
+
# Spec file doesn't exist
|
|
1240
|
+
if strict:
|
|
1241
|
+
# Strict mode: can't verify, so reject
|
|
1242
|
+
return False
|
|
1243
|
+
# Optimistic mode: assume it might work
|
|
1244
|
+
return True
|
|
1245
|
+
# Extract dependencies from the environment's spec file
|
|
1246
|
+
env_deps = extract_dependencies_from_spec_file(env_spec_path)
|
|
1247
|
+
if not env_deps:
|
|
1248
|
+
# Couldn't extract dependencies (or file is empty)
|
|
1249
|
+
if strict:
|
|
1250
|
+
# Strict mode: can't verify, so reject
|
|
1251
|
+
return False
|
|
1252
|
+
# Optimistic mode: assume it might work
|
|
1253
|
+
return True
|
|
1254
|
+
# Check if env_deps is a superset of required_deps
|
|
1255
|
+
# (case-insensitive comparison for package names)
|
|
1256
|
+
env_deps_lower = {dep.lower() for dep in env_deps}
|
|
1257
|
+
required_deps_lower = {dep.lower() for dep in required_deps}
|
|
1258
|
+
transitive = {
|
|
1259
|
+
"jupyter": {"ipykernel"},
|
|
1260
|
+
"pandas": {"numpy"},
|
|
1261
|
+
}
|
|
1262
|
+
for base, provides in transitive.items():
|
|
1263
|
+
if base in env_deps_lower:
|
|
1264
|
+
env_deps_lower.update(provides)
|
|
1265
|
+
return required_deps_lower.issubset(env_deps_lower)
|
|
1266
|
+
|
|
1267
|
+
|
|
1058
1268
|
def detect_env_for_stage(
|
|
1059
1269
|
stage: dict,
|
|
1060
1270
|
environment: str | None = None,
|
|
@@ -1144,8 +1354,74 @@ def detect_env_for_stage(
|
|
|
1144
1354
|
is_first_env_for_language = not any(
|
|
1145
1355
|
env.get("kind") in preferred_kinds for env in envs.values()
|
|
1146
1356
|
)
|
|
1147
|
-
#
|
|
1148
|
-
|
|
1357
|
+
# Stages with analyzable content where we should check dependencies before
|
|
1358
|
+
# reusing existing environments
|
|
1359
|
+
analyzable_stages = {
|
|
1360
|
+
"jupyter-notebook",
|
|
1361
|
+
"python-script",
|
|
1362
|
+
"r-script",
|
|
1363
|
+
"julia-script",
|
|
1364
|
+
"shell-script",
|
|
1365
|
+
}
|
|
1366
|
+
# Initialize detected_dependencies so it's available throughout function
|
|
1367
|
+
detected_dependencies: list[str] = []
|
|
1368
|
+
# For analyzable stages, detect dependencies and check if existing
|
|
1369
|
+
# environments satisfy them
|
|
1370
|
+
if stage_language and stage_kind in analyzable_stages:
|
|
1371
|
+
if stage_kind == "python-script":
|
|
1372
|
+
detected_dependencies = detect_python_dependencies(
|
|
1373
|
+
script_path=stage["script_path"]
|
|
1374
|
+
)
|
|
1375
|
+
elif stage_kind == "r-script":
|
|
1376
|
+
detected_dependencies = detect_r_dependencies(
|
|
1377
|
+
script_path=stage["script_path"]
|
|
1378
|
+
)
|
|
1379
|
+
elif stage_kind == "julia-script":
|
|
1380
|
+
detected_dependencies = detect_julia_dependencies(
|
|
1381
|
+
script_path=stage["script_path"]
|
|
1382
|
+
)
|
|
1383
|
+
elif stage_kind == "jupyter-notebook":
|
|
1384
|
+
notebook_lang = language_from_notebook(stage["notebook_path"])
|
|
1385
|
+
detected_dependencies = detect_dependencies_from_notebook(
|
|
1386
|
+
stage["notebook_path"], language=notebook_lang
|
|
1387
|
+
)
|
|
1388
|
+
elif stage_kind == "matlab-script":
|
|
1389
|
+
# MATLAB detection if needed
|
|
1390
|
+
detected_dependencies = []
|
|
1391
|
+
elif stage_kind == "shell-script":
|
|
1392
|
+
# Shell script detection if needed
|
|
1393
|
+
detected_dependencies = []
|
|
1394
|
+
|
|
1395
|
+
# Check if any existing environment has all these dependencies
|
|
1396
|
+
matching_envs = [
|
|
1397
|
+
(name, env)
|
|
1398
|
+
for name, env in envs.items()
|
|
1399
|
+
if env.get("kind") in preferred_kinds
|
|
1400
|
+
]
|
|
1401
|
+
if matching_envs and detected_dependencies:
|
|
1402
|
+
# Check if any matching environment has all required dependencies
|
|
1403
|
+
# Use strict mode: only reuse if we can verify the deps are satisfied
|
|
1404
|
+
for env_name, env in sorted(
|
|
1405
|
+
matching_envs, key=lambda item: item[0]
|
|
1406
|
+
):
|
|
1407
|
+
if env_has_superset_dependencies(
|
|
1408
|
+
env, detected_dependencies, strict=True
|
|
1409
|
+
):
|
|
1410
|
+
env_name = cast(str, env_name)
|
|
1411
|
+
return EnvForStageResult(
|
|
1412
|
+
name=env_name,
|
|
1413
|
+
env=env,
|
|
1414
|
+
exists=True,
|
|
1415
|
+
spec_path=env.get("path"),
|
|
1416
|
+
dependencies=detected_dependencies,
|
|
1417
|
+
created_from_dependencies=False,
|
|
1418
|
+
)
|
|
1419
|
+
# No existing environment has verified dependencies, fall through to create
|
|
1420
|
+
# If no matching environment found or no dependencies detected,
|
|
1421
|
+
# fall through to create one from dependencies
|
|
1422
|
+
# 2) If there is already an environment for the stage language (for
|
|
1423
|
+
# non-analyzable stages or analyzable stages with no match), use that
|
|
1424
|
+
if stage_language and stage_kind not in analyzable_stages:
|
|
1149
1425
|
matching_envs = [
|
|
1150
1426
|
(name, env)
|
|
1151
1427
|
for name, env in envs.items()
|
|
@@ -1172,6 +1448,7 @@ def detect_env_for_stage(
|
|
|
1172
1448
|
created_from_dependencies=False,
|
|
1173
1449
|
)
|
|
1174
1450
|
# 3) If a typical env spec exists for the stage language, use that
|
|
1451
|
+
# (fallback for analyzable stages if no existing environment matched)
|
|
1175
1452
|
if stage_language:
|
|
1176
1453
|
if stage_language == "latex":
|
|
1177
1454
|
res = env_from_name_or_path(
|
|
@@ -1219,6 +1496,46 @@ def detect_env_for_stage(
|
|
|
1219
1496
|
ck_info=ck_info,
|
|
1220
1497
|
language=stage_language,
|
|
1221
1498
|
)
|
|
1499
|
+
# For analyzable stages with detected dependencies, verify the
|
|
1500
|
+
# spec file has all required packages before reusing
|
|
1501
|
+
if stage_kind in analyzable_stages:
|
|
1502
|
+
# Detect dependencies for this stage if not already done
|
|
1503
|
+
if not detected_dependencies:
|
|
1504
|
+
if stage_kind == "python-script":
|
|
1505
|
+
detected_dependencies = detect_python_dependencies(
|
|
1506
|
+
script_path=stage["script_path"]
|
|
1507
|
+
)
|
|
1508
|
+
elif stage_kind == "r-script":
|
|
1509
|
+
detected_dependencies = detect_r_dependencies(
|
|
1510
|
+
script_path=stage["script_path"]
|
|
1511
|
+
)
|
|
1512
|
+
elif stage_kind == "julia-script":
|
|
1513
|
+
detected_dependencies = detect_julia_dependencies(
|
|
1514
|
+
script_path=stage["script_path"]
|
|
1515
|
+
)
|
|
1516
|
+
elif stage_kind == "jupyter-notebook":
|
|
1517
|
+
notebook_lang = language_from_notebook(
|
|
1518
|
+
stage["notebook_path"]
|
|
1519
|
+
)
|
|
1520
|
+
detected_dependencies = (
|
|
1521
|
+
detect_dependencies_from_notebook(
|
|
1522
|
+
stage["notebook_path"],
|
|
1523
|
+
language=notebook_lang,
|
|
1524
|
+
)
|
|
1525
|
+
)
|
|
1526
|
+
# Only reuse if it has all the dependencies (strict mode)
|
|
1527
|
+
if (
|
|
1528
|
+
detected_dependencies
|
|
1529
|
+
and not env_has_superset_dependencies(
|
|
1530
|
+
res.env,
|
|
1531
|
+
detected_dependencies,
|
|
1532
|
+
spec_path,
|
|
1533
|
+
strict=True,
|
|
1534
|
+
)
|
|
1535
|
+
):
|
|
1536
|
+
# This spec file doesn't have all deps, try next
|
|
1537
|
+
# candidate
|
|
1538
|
+
continue
|
|
1222
1539
|
return EnvForStageResult(
|
|
1223
1540
|
name=res.name,
|
|
1224
1541
|
env=res.env,
|
|
@@ -1237,6 +1554,7 @@ def detect_env_for_stage(
|
|
|
1237
1554
|
dependencies = detect_python_dependencies(
|
|
1238
1555
|
script_path=stage["script_path"]
|
|
1239
1556
|
)
|
|
1557
|
+
project_name = calkit.detect_project_name(prepend_owner=False)
|
|
1240
1558
|
# Generate unique environment name
|
|
1241
1559
|
if is_first_env_for_language:
|
|
1242
1560
|
temp_path = "pyproject.toml"
|
|
@@ -1248,15 +1566,16 @@ def detect_env_for_stage(
|
|
|
1248
1566
|
"path": spec_path,
|
|
1249
1567
|
}
|
|
1250
1568
|
else:
|
|
1251
|
-
temp_path = ".calkit/envs/py/
|
|
1252
|
-
env_name = make_env_name(temp_path, all_env_names, kind="uv
|
|
1253
|
-
spec_path = f".calkit/envs/{env_name}/
|
|
1254
|
-
spec_content =
|
|
1569
|
+
temp_path = ".calkit/envs/py/pyproject.toml"
|
|
1570
|
+
env_name = make_env_name(temp_path, all_env_names, kind="uv")
|
|
1571
|
+
spec_path = f".calkit/envs/{env_name}/pyproject.toml"
|
|
1572
|
+
spec_content = create_uv_pyproject_content(
|
|
1573
|
+
dependencies,
|
|
1574
|
+
project_name=f"{project_name}-{env_name}",
|
|
1575
|
+
)
|
|
1255
1576
|
env_dict = {
|
|
1256
|
-
"kind": "uv
|
|
1577
|
+
"kind": "uv",
|
|
1257
1578
|
"path": spec_path,
|
|
1258
|
-
"python": DEFAULT_PYTHON_VERSION,
|
|
1259
|
-
"prefix": os.path.join(os.path.dirname(spec_path), ".venv"),
|
|
1260
1579
|
}
|
|
1261
1580
|
elif stage["kind"] == "r-script":
|
|
1262
1581
|
dependencies = detect_r_dependencies(script_path=stage["script_path"])
|
|
@@ -1304,6 +1623,7 @@ def detect_env_for_stage(
|
|
|
1304
1623
|
stage["notebook_path"], language=notebook_lang
|
|
1305
1624
|
)
|
|
1306
1625
|
if notebook_lang == "python" or notebook_lang is None:
|
|
1626
|
+
project_name = calkit.detect_project_name(prepend_owner=False)
|
|
1307
1627
|
# Add ipykernel for Jupyter notebook support
|
|
1308
1628
|
if "ipykernel" not in dependencies:
|
|
1309
1629
|
dependencies.append("ipykernel")
|
|
@@ -1318,19 +1638,16 @@ def detect_env_for_stage(
|
|
|
1318
1638
|
"path": spec_path,
|
|
1319
1639
|
}
|
|
1320
1640
|
else:
|
|
1321
|
-
temp_path = ".calkit/envs/py/
|
|
1322
|
-
env_name = make_env_name(
|
|
1323
|
-
|
|
1641
|
+
temp_path = ".calkit/envs/py/pyproject.toml"
|
|
1642
|
+
env_name = make_env_name(temp_path, all_env_names, kind="uv")
|
|
1643
|
+
spec_path = f".calkit/envs/{env_name}/pyproject.toml"
|
|
1644
|
+
spec_content = create_uv_pyproject_content(
|
|
1645
|
+
dependencies,
|
|
1646
|
+
project_name=f"{project_name}-{env_name}",
|
|
1324
1647
|
)
|
|
1325
|
-
spec_path = f".calkit/envs/{env_name}/requirements.txt"
|
|
1326
|
-
spec_content = create_python_requirements_content(dependencies)
|
|
1327
1648
|
env_dict = {
|
|
1328
|
-
"kind": "uv
|
|
1649
|
+
"kind": "uv",
|
|
1329
1650
|
"path": spec_path,
|
|
1330
|
-
"python": DEFAULT_PYTHON_VERSION,
|
|
1331
|
-
"prefix": os.path.join(
|
|
1332
|
-
os.path.dirname(spec_path), ".venv"
|
|
1333
|
-
),
|
|
1334
1651
|
}
|
|
1335
1652
|
elif notebook_lang == "r":
|
|
1336
1653
|
# Add IRkernel for Jupyter notebook support
|
|
@@ -1354,6 +1671,7 @@ def detect_env_for_stage(
|
|
|
1354
1671
|
# Add IJulia for Jupyter notebook support
|
|
1355
1672
|
if "IJulia" not in dependencies:
|
|
1356
1673
|
dependencies.append("IJulia")
|
|
1674
|
+
project_name = calkit.detect_project_name(prepend_owner=False)
|
|
1357
1675
|
# Generate unique environment name
|
|
1358
1676
|
if is_first_env_for_language:
|
|
1359
1677
|
temp_path = "Project.toml"
|
|
@@ -1361,16 +1679,21 @@ def detect_env_for_stage(
|
|
|
1361
1679
|
temp_path, all_env_names, kind="julia"
|
|
1362
1680
|
)
|
|
1363
1681
|
spec_path = "Project.toml"
|
|
1682
|
+
julia_env_name = project_name
|
|
1364
1683
|
else:
|
|
1365
1684
|
temp_path = ".calkit/envs/julia/Project.toml"
|
|
1366
1685
|
env_name = make_env_name(
|
|
1367
1686
|
temp_path, all_env_names, kind="julia"
|
|
1368
1687
|
)
|
|
1369
1688
|
spec_path = f".calkit/envs/{env_name}/Project.toml"
|
|
1370
|
-
|
|
1689
|
+
julia_env_name = f"{project_name}-{env_name}"
|
|
1690
|
+
spec_content = create_julia_project_file_content(
|
|
1691
|
+
dependencies, project_name=julia_env_name
|
|
1692
|
+
)
|
|
1371
1693
|
env_dict = {
|
|
1372
1694
|
"kind": "julia",
|
|
1373
1695
|
"path": spec_path,
|
|
1696
|
+
"julia": _get_julia_version(),
|
|
1374
1697
|
}
|
|
1375
1698
|
if not spec_path or not env_name:
|
|
1376
1699
|
raise ValueError(
|
|
@@ -389,11 +389,13 @@ class NotebookKernelRouteHandler(APIHandler):
|
|
|
389
389
|
400, "Both 'path' and 'environment' are required"
|
|
390
390
|
)
|
|
391
391
|
try:
|
|
392
|
-
kernel_name = check_env_kernel(env_name=env_name)
|
|
392
|
+
kernel_name, display_name = check_env_kernel(env_name=env_name)
|
|
393
393
|
except Exception as e:
|
|
394
394
|
self.log.error(f"Failed to check env kernel for {env_name}: {e}")
|
|
395
395
|
return self.error(500, f"Failed to check environment kernel: {e}")
|
|
396
|
-
self.finish(
|
|
396
|
+
self.finish(
|
|
397
|
+
json.dumps({"name": kernel_name, "display_name": display_name})
|
|
398
|
+
)
|
|
397
399
|
|
|
398
400
|
|
|
399
401
|
class NotebookStageRouteHandler(APIHandler):
|
|
@@ -412,7 +414,11 @@ class NotebookStageRouteHandler(APIHandler):
|
|
|
412
414
|
executed_ipynb_storage = body.get("executed_ipynb_storage", "git")
|
|
413
415
|
if not notebook_path or not stage_name or not env_name:
|
|
414
416
|
return self.error(
|
|
415
|
-
400,
|
|
417
|
+
400,
|
|
418
|
+
(
|
|
419
|
+
"Request body must include 'path', 'stage_name', and "
|
|
420
|
+
"'environment'"
|
|
421
|
+
),
|
|
416
422
|
)
|
|
417
423
|
ck_info = calkit.load_calkit_info()
|
|
418
424
|
stages = ck_info.get("pipeline", {}).get("stages", {})
|
|
@@ -431,7 +431,7 @@ class MatlabScriptStage(Stage):
|
|
|
431
431
|
def dvc_cmd(self) -> str:
|
|
432
432
|
cmd = self.xenv_cmd
|
|
433
433
|
if self.environment == "_system":
|
|
434
|
-
cmd += "matlab -batch"
|
|
434
|
+
cmd += "matlab -noFigureWindows -batch"
|
|
435
435
|
matlab_cmd = ""
|
|
436
436
|
if self.matlab_path is not None:
|
|
437
437
|
matlab_cmd += f"addpath(genpath('{self.matlab_path}')); "
|
|
@@ -450,7 +450,7 @@ class MatlabCommandStage(Stage):
|
|
|
450
450
|
matlab_cmd = self.command.replace('"', '\\"')
|
|
451
451
|
cmd = self.xenv_cmd
|
|
452
452
|
if self.environment == "_system":
|
|
453
|
-
cmd += "matlab -batch"
|
|
453
|
+
cmd += "matlab -noFigureWindows -batch"
|
|
454
454
|
cmd += f' "{matlab_cmd}"'
|
|
455
455
|
return cmd
|
|
456
456
|
|