calkit-python 0.39.0__tar.gz → 0.39.1__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.39.0 → calkit_python-0.39.1}/PKG-INFO +1 -1
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/__init__.py +38 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/main/core.py +22 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/dvc/zip.py +83 -45
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/pipeline.py +107 -58
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_pipeline.py +172 -1
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.claude-plugin/marketplace.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.gitignore +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.pre-commit-config.yaml +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.prettierignore +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.python-version +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.vscode/launch.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.vscode/tasks.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/.yarnrc.yml +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/AGENTS.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/CITATION.cff +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/CODE_OF_CONDUCT.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/CONTRIBUTING.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/LICENSE +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/Makefile +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/README.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/agent-plugin/.claude-plugin/plugin.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/agent-plugin/skills/conventions/SKILL.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/babel.config.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/__main__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/calc.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/check.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/check.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/cloud.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/config.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/describe.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/dev.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/import_.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/latex.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/list.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/main/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/main/xr.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/new.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/office.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/overleaf.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/slurm.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cli/update.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/cloud.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/conda.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/config.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/datasets.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/detect.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/docker.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/dvc/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/dvc/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/environments.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/fs.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/git.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/github.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/gui.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/invenio.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/julia.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/jupyter.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/jupyterlab/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/jupyterlab/routes.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/package.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/remoteEntry.65469af996e7a96aa983.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/style.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/labextension/static/third-party-licenses.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/licenses.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/magics.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/matlab.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/models/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/models/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/models/io.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/models/iteration.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/models/pipeline.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/notebooks.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/office.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/ops.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/overleaf.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/releases.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/server.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/main/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/main/test_core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/main/test_subprojects.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/main/test_xr.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_check.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_cloud.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_config.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_import.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_latex.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_notebooks.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_overleaf.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/cli/test_update.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/dvc/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/dvc/test_core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/dvc/test_zip.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/jupyterlab/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/jupyterlab/test_routes.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/models/__init__.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/models/test_iteration.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/models/test_pipeline.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_cloud.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_detect.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_docker.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_environments.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_fs.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_git.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_invenio.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_julia.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_matlab.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_notebooks.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_releases.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/conftest.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/CNAME +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/ai-tools.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/apps.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/calculations.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/calkit-yaml.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/cli-reference.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/cloud-integration.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/datasets.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/dependencies.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/environments.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/examples.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/governance.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/help.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/calkit-fragmentation-compendium.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/connect-zenodo.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab/all-green.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab/collect-data-stale.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab/new-env.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab/new-notebook.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab/pipeline-badge.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/jupyterlab-params.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/plos-osi-code-2024-03.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/img/vscode-nb-params.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/index.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/installation.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/jupyterlab.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/local-server.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/notebooks.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/overleaf.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/pipeline/index.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/pipeline/running-and-logging.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/pipeline/slurm.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/quickstart.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/references.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/releases.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/reproducibility.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/subprojects.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/existing-project.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/github-actions.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/actions-repo-secrets.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/quick-actions.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/index.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/jupyterlab.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/latex-codespaces.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/office.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/tutorials/vscode-slurm-notebook.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/docs/version-control.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/install.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/jest.config.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/jupyter-config/server-config/calkit.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/mkdocs.yml +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/package.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/pyproject.toml +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/schema/plugin.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/scripts/generate-docs-references.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/scripts/install.ps1 +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/scripts/install.sh +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/scripts/make-calk9.sh +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/scripts/sync-docs.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/__tests__/useQueries.spec.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/calkit-config.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/cell-output-marker.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/commit-dialog.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/environment-editor.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/notebook-registration.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/notebook-toolbar.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/pipeline-status-bar.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/project-info-editor.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/sidebar-settings.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/sidebar.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/components/stage-editor.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/feature-flags.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/file-browser-menu.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/hooks/__tests__/useQueries.test.tsx +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/hooks/useQueries.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/icons.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/index.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/io-tracker.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/pipeline-state.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/queryClient.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/request.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/src/shims-mainmenu.d.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/base.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/cell-output-marker.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/environment-editor.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/environment-selector.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/img/calkit-no-bg.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/index.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/index.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/notebook-toolbar.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/pipeline-status-bar.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/style/sidebar.css +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/dvc-md5-dir/osx-arm64.txt +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/nb-julia.ipynb +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/nb-params.ipynb +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/nb-subdir.ipynb +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/pipeline.ipynb +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/script.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/test/test-log.log +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/tsconfig.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/tsconfig.test.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/.gitignore +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/README.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/jupyter_server_test_config.py +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/package.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/playwright.config.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/tests/calkit.spec.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/ui-tests/yarn.lock +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/uv.lock +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/.gitignore +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/.vscodeignore +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/CHANGELOG.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/LICENSE +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/README.md +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/images/calkit-no-bg.png +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/package-lock.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/package.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/scripts/set-proposed-api.js +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/environments.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/extension.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/notebooks.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/test/environments.test.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/test/notebooks.test.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/src/types.ts +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/vscode-ext/tsconfig.json +0 -0
- {calkit_python-0.39.0 → calkit_python-0.39.1}/yarn.lock +0 -0
|
@@ -1,9 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from importlib.metadata import version as _version
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
2
5
|
|
|
3
6
|
__version__ = _version("calkit-python")
|
|
4
7
|
|
|
5
8
|
from .core import * # noqa: F403, I001
|
|
6
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from calkit import ( # noqa: F401
|
|
12
|
+
calc,
|
|
13
|
+
check,
|
|
14
|
+
cloud,
|
|
15
|
+
conda,
|
|
16
|
+
config,
|
|
17
|
+
datasets,
|
|
18
|
+
detect,
|
|
19
|
+
docker,
|
|
20
|
+
dvc,
|
|
21
|
+
environments,
|
|
22
|
+
fs,
|
|
23
|
+
git,
|
|
24
|
+
github,
|
|
25
|
+
gui,
|
|
26
|
+
invenio,
|
|
27
|
+
julia,
|
|
28
|
+
jupyter,
|
|
29
|
+
licenses,
|
|
30
|
+
magics,
|
|
31
|
+
matlab,
|
|
32
|
+
models,
|
|
33
|
+
notebooks,
|
|
34
|
+
office,
|
|
35
|
+
ops,
|
|
36
|
+
overleaf,
|
|
37
|
+
pipeline,
|
|
38
|
+
releases,
|
|
39
|
+
server,
|
|
40
|
+
templates,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
from .notebooks import declare_notebook # noqa: F401
|
|
44
|
+
|
|
7
45
|
# Lazy-load submodules to speed up imports
|
|
8
46
|
_SUBMODULES = {
|
|
9
47
|
"git",
|
|
@@ -1053,10 +1053,32 @@ def pull(
|
|
|
1053
1053
|
if calkit.dvc.detect_calkit_remote_type(name, url) == "http":
|
|
1054
1054
|
typer.echo(f"Checking authentication for DVC remote: {name}")
|
|
1055
1055
|
calkit.dvc.set_remote_auth(remote_name=name)
|
|
1056
|
+
if (
|
|
1057
|
+
not no_recursive
|
|
1058
|
+
and "--recursive" not in dvc_args
|
|
1059
|
+
and "-R" not in dvc_args
|
|
1060
|
+
):
|
|
1061
|
+
dvc_args.append("--recursive")
|
|
1056
1062
|
result = calkit.dvc.run_dvc_command(["pull"] + dvc_args)
|
|
1057
1063
|
if result != 0:
|
|
1058
1064
|
raise_error("DVC pull failed")
|
|
1059
1065
|
calkit.dvc.zip.sync_all(direction="to-workspace")
|
|
1066
|
+
if not no_recursive:
|
|
1067
|
+
# Pull DVC in isolated subprojects (those with their own .dvc folder)
|
|
1068
|
+
ck_info = calkit.load_calkit_info()
|
|
1069
|
+
for sp in ck_info.get("subprojects", []):
|
|
1070
|
+
if not isinstance(sp, dict) or not sp.get("path"):
|
|
1071
|
+
continue
|
|
1072
|
+
sp_path = sp["path"]
|
|
1073
|
+
if not os.path.isdir(os.path.join(sp_path, ".dvc")):
|
|
1074
|
+
continue
|
|
1075
|
+
typer.echo(f"DVC pulling subproject: {sp_path}")
|
|
1076
|
+
sp_result = calkit.dvc.run_dvc_command(
|
|
1077
|
+
["pull"] + dvc_args, cwd=sp_path
|
|
1078
|
+
)
|
|
1079
|
+
if sp_result != 0:
|
|
1080
|
+
raise_error(f"DVC pull failed for subproject: {sp_path}")
|
|
1081
|
+
calkit.dvc.zip.sync_all(direction="to-workspace", wdir=sp_path)
|
|
1060
1082
|
|
|
1061
1083
|
|
|
1062
1084
|
@app.command(name="push")
|
|
@@ -46,14 +46,19 @@ ZIP_COMPRESS_LEVEL = 1
|
|
|
46
46
|
ZIP_USE_SYSTEM_CLI = False
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
def _local_dir(wdir: str | None = None) -> str:
|
|
50
|
+
return os.path.join(wdir, LOCAL_DIR) if wdir else LOCAL_DIR
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check_local_dir(wdir: str | None = None) -> Path:
|
|
54
|
+
local_dir = _local_dir(wdir)
|
|
55
|
+
if not os.path.isdir(local_dir):
|
|
56
|
+
os.makedirs(local_dir, exist_ok=True)
|
|
57
|
+
gitignore_path = os.path.join(local_dir, ".gitignore")
|
|
53
58
|
if not os.path.isfile(gitignore_path):
|
|
54
59
|
with open(gitignore_path, "w") as f:
|
|
55
60
|
f.write("*\n")
|
|
56
|
-
return Path(
|
|
61
|
+
return Path(local_dir)
|
|
57
62
|
|
|
58
63
|
|
|
59
64
|
def is_zip_candidate(path: str) -> bool:
|
|
@@ -111,10 +116,15 @@ def make_zip_path(workspace_path: str) -> str:
|
|
|
111
116
|
return os.path.join(ZIPS_DIR, "files", workspace_path + ".zip")
|
|
112
117
|
|
|
113
118
|
|
|
114
|
-
def
|
|
119
|
+
def _path_map_path(wdir: str | None = None) -> str:
|
|
120
|
+
return os.path.join(wdir, PATH_MAP_PATH) if wdir else PATH_MAP_PATH
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_zip_path_map(wdir: str | None = None) -> dict[str, str]:
|
|
115
124
|
"""Get a mapping of input paths to zip paths."""
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
path = _path_map_path(wdir)
|
|
126
|
+
if os.path.isfile(path):
|
|
127
|
+
with open(path, "r") as f:
|
|
118
128
|
return json.load(f)
|
|
119
129
|
return {}
|
|
120
130
|
|
|
@@ -207,19 +217,22 @@ def calc_dir_sig(path: str) -> str:
|
|
|
207
217
|
return f"{file_count}-{total_size}-{latest_mtime}"
|
|
208
218
|
|
|
209
219
|
|
|
210
|
-
def get_hash(path: str, alg="md5") -> str | None:
|
|
220
|
+
def get_hash(path: str, alg="md5", wdir: str | None = None) -> str | None:
|
|
211
221
|
"""Get the hash of a path, using/updating the cache if applicable."""
|
|
212
222
|
try:
|
|
213
223
|
st = os.stat(path)
|
|
214
224
|
except OSError:
|
|
215
225
|
return None
|
|
216
|
-
_check_local_dir()
|
|
226
|
+
_check_local_dir(wdir)
|
|
217
227
|
# Normalize path as posix
|
|
218
228
|
path = Path(path).as_posix()
|
|
219
229
|
mtime = st.st_mtime_ns
|
|
220
230
|
size = st.st_size
|
|
221
231
|
dir_sig = calc_dir_sig(path)
|
|
222
|
-
|
|
232
|
+
hash_cache_path = (
|
|
233
|
+
os.path.join(wdir, HASH_CACHE_PATH) if wdir else HASH_CACHE_PATH
|
|
234
|
+
)
|
|
235
|
+
with SqliteDict(hash_cache_path) as cache:
|
|
223
236
|
record = None
|
|
224
237
|
raw = cache.get(path)
|
|
225
238
|
if raw is not None:
|
|
@@ -250,38 +263,44 @@ def get_hash(path: str, alg="md5") -> str | None:
|
|
|
250
263
|
return hash_val
|
|
251
264
|
|
|
252
265
|
|
|
253
|
-
def
|
|
266
|
+
def _sync_records_path(wdir: str | None = None) -> str:
|
|
267
|
+
return os.path.join(wdir, SYNC_RECORDS_PATH) if wdir else SYNC_RECORDS_PATH
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def get_sync_record(
|
|
271
|
+
workspace_path: str, wdir: str | None = None
|
|
272
|
+
) -> SyncRecord | None:
|
|
254
273
|
"""Get a sync record for a given workspace path."""
|
|
255
|
-
_check_local_dir()
|
|
256
|
-
with SqliteDict(
|
|
274
|
+
_check_local_dir(wdir)
|
|
275
|
+
with SqliteDict(_sync_records_path(wdir)) as db:
|
|
257
276
|
raw = db.get(workspace_path)
|
|
258
277
|
if raw is not None:
|
|
259
278
|
return SyncRecord.model_validate(raw)
|
|
260
279
|
return None
|
|
261
280
|
|
|
262
281
|
|
|
263
|
-
def write_sync_record(record: SyncRecord):
|
|
282
|
+
def write_sync_record(record: SyncRecord, wdir: str | None = None):
|
|
264
283
|
"""Write a sync record."""
|
|
265
|
-
_check_local_dir()
|
|
266
|
-
with SqliteDict(
|
|
284
|
+
_check_local_dir(wdir)
|
|
285
|
+
with SqliteDict(_sync_records_path(wdir)) as db:
|
|
267
286
|
db[record.workspace_path] = record.model_dump()
|
|
268
287
|
db.commit()
|
|
269
288
|
|
|
270
289
|
|
|
271
|
-
def delete_sync_record(workspace_path: str):
|
|
290
|
+
def delete_sync_record(workspace_path: str, wdir: str | None = None):
|
|
272
291
|
"""Delete a sync record."""
|
|
273
|
-
_check_local_dir()
|
|
274
|
-
with SqliteDict(
|
|
292
|
+
_check_local_dir(wdir)
|
|
293
|
+
with SqliteDict(_sync_records_path(wdir)) as db:
|
|
275
294
|
if workspace_path in db:
|
|
276
295
|
del db[workspace_path]
|
|
277
296
|
db.commit()
|
|
278
297
|
|
|
279
298
|
|
|
280
|
-
def cleanup_sync_records():
|
|
299
|
+
def cleanup_sync_records(wdir: str | None = None):
|
|
281
300
|
"""Remove sync records for paths no longer in the path map."""
|
|
282
|
-
_check_local_dir()
|
|
283
|
-
pm = get_zip_path_map()
|
|
284
|
-
with SqliteDict(
|
|
301
|
+
_check_local_dir(wdir)
|
|
302
|
+
pm = get_zip_path_map(wdir)
|
|
303
|
+
with SqliteDict(_sync_records_path(wdir)) as db:
|
|
285
304
|
stale = [k for k in db if k not in pm]
|
|
286
305
|
if stale:
|
|
287
306
|
for k in stale:
|
|
@@ -289,8 +308,8 @@ def cleanup_sync_records():
|
|
|
289
308
|
db.commit()
|
|
290
309
|
|
|
291
310
|
|
|
292
|
-
def get_zip_path(workspace_path: str) -> str:
|
|
293
|
-
pm = get_zip_path_map()
|
|
311
|
+
def get_zip_path(workspace_path: str, wdir: str | None = None) -> str:
|
|
312
|
+
pm = get_zip_path_map(wdir)
|
|
294
313
|
workspace_path = Path(workspace_path).as_posix()
|
|
295
314
|
if workspace_path in pm:
|
|
296
315
|
return pm[workspace_path]
|
|
@@ -408,14 +427,15 @@ class SyncStatus(BaseModel):
|
|
|
408
427
|
|
|
409
428
|
|
|
410
429
|
def get_sync_status(
|
|
411
|
-
workspace_path: str,
|
|
430
|
+
workspace_path: str,
|
|
431
|
+
zip_path: str | None = None,
|
|
432
|
+
wdir: str | None = None,
|
|
412
433
|
) -> SyncStatus:
|
|
413
|
-
|
|
414
|
-
workspace_hash = get_hash(workspace_path)
|
|
434
|
+
workspace_hash = get_hash(workspace_path, wdir=wdir)
|
|
415
435
|
if zip_path is None:
|
|
416
|
-
zip_path = get_zip_path(workspace_path)
|
|
417
|
-
zip_hash = get_hash(zip_path)
|
|
418
|
-
last_sync_record = get_sync_record(workspace_path)
|
|
436
|
+
zip_path = get_zip_path(workspace_path, wdir=wdir)
|
|
437
|
+
zip_hash = get_hash(zip_path, wdir=wdir)
|
|
438
|
+
last_sync_record = get_sync_record(workspace_path, wdir=wdir)
|
|
419
439
|
if last_sync_record is not None:
|
|
420
440
|
workspace_changed = workspace_hash != last_sync_record.workspace_hash
|
|
421
441
|
zip_changed = zip_hash != last_sync_record.zip_hash
|
|
@@ -439,9 +459,23 @@ def sync_one(
|
|
|
439
459
|
workspace_path: str,
|
|
440
460
|
zip_path: str | None = None,
|
|
441
461
|
direction: Literal["to-zip", "to-workspace", "both"] = "both",
|
|
462
|
+
wdir: str | None = None,
|
|
442
463
|
) -> SyncRecord | None:
|
|
443
464
|
"""Process a single zip."""
|
|
444
|
-
|
|
465
|
+
if wdir:
|
|
466
|
+
workspace_path = os.path.join(wdir, workspace_path)
|
|
467
|
+
if zip_path is not None:
|
|
468
|
+
zip_path = os.path.join(wdir, zip_path)
|
|
469
|
+
return _sync_one(workspace_path, zip_path, direction, wdir=wdir)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _sync_one(
|
|
473
|
+
workspace_path: str,
|
|
474
|
+
zip_path: str | None = None,
|
|
475
|
+
direction: Literal["to-zip", "to-workspace", "both"] = "both",
|
|
476
|
+
wdir: str | None = None,
|
|
477
|
+
) -> SyncRecord | None:
|
|
478
|
+
status = get_sync_status(workspace_path, zip_path, wdir=wdir)
|
|
445
479
|
workspace_changed = status.workspace_changed
|
|
446
480
|
zip_changed = status.zip_changed
|
|
447
481
|
workspace_hash = status.workspace_hash
|
|
@@ -454,7 +488,7 @@ def sync_one(
|
|
|
454
488
|
# Both deleted — clear the stale sync record so a future recreated side
|
|
455
489
|
# is treated as a fresh first sync rather than a spurious conflict
|
|
456
490
|
if workspace_deleted and zip_deleted:
|
|
457
|
-
delete_sync_record(workspace_path)
|
|
491
|
+
delete_sync_record(workspace_path, wdir=wdir)
|
|
458
492
|
return None
|
|
459
493
|
# Neither side exists and no sync record — nothing to do (e.g., a
|
|
460
494
|
# pipeline output that hasn't been produced yet on a fresh run)
|
|
@@ -478,28 +512,28 @@ def sync_one(
|
|
|
478
512
|
if os.path.exists(zip_path):
|
|
479
513
|
typer.echo(f"Deleting '{zip_path}' (workspace was deleted)")
|
|
480
514
|
os.remove(zip_path)
|
|
481
|
-
delete_sync_record(workspace_path)
|
|
515
|
+
delete_sync_record(workspace_path, wdir=wdir)
|
|
482
516
|
return None
|
|
483
517
|
# Propagate zip deletion to workspace
|
|
484
518
|
if zip_deleted and direction in ["to-workspace", "both"]:
|
|
485
519
|
if os.path.exists(workspace_path):
|
|
486
520
|
typer.echo(f"Deleting '{workspace_path}' (zip was deleted)")
|
|
487
521
|
shutil.rmtree(workspace_path)
|
|
488
|
-
delete_sync_record(workspace_path)
|
|
522
|
+
delete_sync_record(workspace_path, wdir=wdir)
|
|
489
523
|
return None
|
|
490
524
|
# Zip was deleted but workspace exists and direction is to-zip:
|
|
491
525
|
# restore zip
|
|
492
526
|
if zip_deleted and direction == "to-zip":
|
|
493
527
|
typer.echo(f"Rezipping '{workspace_path}' (zip was deleted)")
|
|
494
528
|
zip_(workspace_path=workspace_path, zip_path=zip_path)
|
|
495
|
-
run_dvc_command(["add", zip_path])
|
|
496
|
-
zip_hash = get_hash(zip_path)
|
|
529
|
+
run_dvc_command(["add", zip_path], cwd=wdir)
|
|
530
|
+
zip_hash = get_hash(zip_path, wdir=wdir)
|
|
497
531
|
# Workspace was deleted but zip exists and direction is to-workspace:
|
|
498
532
|
# restore workspace
|
|
499
533
|
if workspace_deleted and direction == "to-workspace":
|
|
500
534
|
typer.echo(f"Unzipping to '{workspace_path}' (workspace was deleted)")
|
|
501
535
|
unzip(workspace_path=workspace_path, zip_path=zip_path)
|
|
502
|
-
workspace_hash = get_hash(workspace_path)
|
|
536
|
+
workspace_hash = get_hash(workspace_path, wdir=wdir)
|
|
503
537
|
# If hashes have changed since last check, we need to synchronize the
|
|
504
538
|
# path with its zip file (unzip if zip is newer, rezip if path is newer)
|
|
505
539
|
# If both have changed, we have a conflict and the user needs to decide
|
|
@@ -514,13 +548,13 @@ def sync_one(
|
|
|
514
548
|
if workspace_changed and (direction in ["to-zip", "both"]):
|
|
515
549
|
typer.echo(f"Zipping '{workspace_path}' (workspace has changed)")
|
|
516
550
|
zip_(workspace_path=workspace_path, zip_path=zip_path)
|
|
517
|
-
run_dvc_command(["add", zip_path])
|
|
518
|
-
zip_hash = get_hash(zip_path)
|
|
551
|
+
run_dvc_command(["add", zip_path], cwd=wdir)
|
|
552
|
+
zip_hash = get_hash(zip_path, wdir=wdir)
|
|
519
553
|
# If we unzip, we need to update the hash
|
|
520
554
|
if zip_changed and (direction in ["to-workspace", "both"]):
|
|
521
555
|
typer.echo(f"Unzipping to '{workspace_path}' (zip has changed)")
|
|
522
556
|
unzip(workspace_path=workspace_path, zip_path=zip_path)
|
|
523
|
-
workspace_hash = get_hash(workspace_path)
|
|
557
|
+
workspace_hash = get_hash(workspace_path, wdir=wdir)
|
|
524
558
|
assert workspace_hash is not None and zip_hash is not None
|
|
525
559
|
record = SyncRecord(
|
|
526
560
|
workspace_path=workspace_path,
|
|
@@ -529,7 +563,7 @@ def sync_one(
|
|
|
529
563
|
workspace_hash=workspace_hash,
|
|
530
564
|
zip_hash=zip_hash,
|
|
531
565
|
)
|
|
532
|
-
write_sync_record(record)
|
|
566
|
+
write_sync_record(record, wdir=wdir)
|
|
533
567
|
return record
|
|
534
568
|
|
|
535
569
|
|
|
@@ -547,11 +581,15 @@ def sync_some(
|
|
|
547
581
|
)
|
|
548
582
|
|
|
549
583
|
|
|
550
|
-
def sync_all(
|
|
584
|
+
def sync_all(
|
|
585
|
+
direction: Literal["to-zip", "to-workspace", "both"] = "both",
|
|
586
|
+
wdir: str | None = None,
|
|
587
|
+
):
|
|
551
588
|
"""Process all project zips."""
|
|
552
|
-
for workspace_path, zip_path in get_zip_path_map().items():
|
|
589
|
+
for workspace_path, zip_path in get_zip_path_map(wdir=wdir).items():
|
|
553
590
|
sync_one(
|
|
554
591
|
workspace_path=workspace_path,
|
|
555
592
|
zip_path=zip_path,
|
|
556
593
|
direction=direction,
|
|
594
|
+
wdir=wdir,
|
|
557
595
|
)
|
|
@@ -311,6 +311,98 @@ def _expand_matrix(input_dict: dict[str, list]) -> list[dict]:
|
|
|
311
311
|
return final_list
|
|
312
312
|
|
|
313
313
|
|
|
314
|
+
def _paths_overlap(a: str, b: str) -> bool:
|
|
315
|
+
"""Return True if path `a` and path `b` overlap in the file tree.
|
|
316
|
+
|
|
317
|
+
Two paths overlap if they are equal or one is an ancestor directory of the
|
|
318
|
+
other, e.g. ``data/`` overlaps with ``data/file.csv``.
|
|
319
|
+
"""
|
|
320
|
+
a = a.rstrip("/")
|
|
321
|
+
b = b.rstrip("/")
|
|
322
|
+
if a == b:
|
|
323
|
+
return True
|
|
324
|
+
return b.startswith(a + "/") or a.startswith(b + "/")
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def collapse_dvc_stages(
|
|
328
|
+
stages: dict,
|
|
329
|
+
cmd: str | None = None,
|
|
330
|
+
wdir: str | None = None,
|
|
331
|
+
desc: str | None = None,
|
|
332
|
+
) -> dict:
|
|
333
|
+
"""Collapse a dict of DVC stages into a single wrapper stage.
|
|
334
|
+
|
|
335
|
+
Collects all deps and outs from ``stages``, expands matrix templates, and
|
|
336
|
+
returns a complete DVC stage dict. ``cmd``, ``wdir``, and ``desc`` are
|
|
337
|
+
optional; when provided they are included in the returned dict.
|
|
338
|
+
|
|
339
|
+
Rules applied:
|
|
340
|
+
- A dep that tree-overlaps any out is dropped (e.g. a folder dep that
|
|
341
|
+
contains output files would cause a DVC conflict).
|
|
342
|
+
- An out that tree-overlaps any remaining dep is also dropped (covers
|
|
343
|
+
exact-match aliasing and folder/child relationships).
|
|
344
|
+
- Outputs are wrapped with ``{cache: false, persist: true}`` so the parent
|
|
345
|
+
doesn't double-cache files already managed by the subproject's DVC.
|
|
346
|
+
"""
|
|
347
|
+
all_outs: set[str] = set()
|
|
348
|
+
all_deps: set[str] = set()
|
|
349
|
+
for stage_cfg in stages.values():
|
|
350
|
+
matrix = stage_cfg.get("matrix")
|
|
351
|
+
replacements = _expand_matrix(matrix) if matrix else [{}]
|
|
352
|
+
for out in stage_cfg.get("outs", []):
|
|
353
|
+
raw = out if isinstance(out, str) else list(out.keys())[0]
|
|
354
|
+
for r in replacements:
|
|
355
|
+
expanded = raw
|
|
356
|
+
for var, val in r.items():
|
|
357
|
+
expanded = expanded.replace(f"${{item.{var}}}", str(val))
|
|
358
|
+
all_outs.add(Path(expanded).as_posix())
|
|
359
|
+
for dep in stage_cfg.get("deps", []):
|
|
360
|
+
if not isinstance(dep, str):
|
|
361
|
+
continue
|
|
362
|
+
for r in replacements:
|
|
363
|
+
expanded = dep
|
|
364
|
+
for var, val in r.items():
|
|
365
|
+
expanded = expanded.replace(f"${{item.{var}}}", str(val))
|
|
366
|
+
all_deps.add(Path(expanded).as_posix())
|
|
367
|
+
external_deps = all_deps - all_outs
|
|
368
|
+
outs_raw = sorted(all_outs)
|
|
369
|
+
# Track whether any dep is dropped because it is a strict folder ancestor
|
|
370
|
+
# of an output (not an exact match — those are already removed by the set
|
|
371
|
+
# subtraction above). When this happens the wrapper cannot fully express
|
|
372
|
+
# the input boundary, so we mark it always_changed.
|
|
373
|
+
# A dep is a strict ancestor of an out when the out path starts with the
|
|
374
|
+
# dep path followed by "/". The reverse (dep inside an output folder) is
|
|
375
|
+
# just a normal internal path drop and does not require always_changed.
|
|
376
|
+
has_ancestor_dep_drop = any(
|
|
377
|
+
o.startswith(d.rstrip("/") + "/")
|
|
378
|
+
for d in external_deps
|
|
379
|
+
for o in outs_raw
|
|
380
|
+
)
|
|
381
|
+
deps = sorted(
|
|
382
|
+
d
|
|
383
|
+
for d in external_deps
|
|
384
|
+
if not any(_paths_overlap(d, o) for o in outs_raw)
|
|
385
|
+
)
|
|
386
|
+
dep_set = set(deps)
|
|
387
|
+
outs = [
|
|
388
|
+
{o: {"cache": False, "persist": True}}
|
|
389
|
+
for o in outs_raw
|
|
390
|
+
if not any(_paths_overlap(o, d) for d in dep_set)
|
|
391
|
+
]
|
|
392
|
+
stage: dict = {}
|
|
393
|
+
if cmd is not None:
|
|
394
|
+
stage["cmd"] = cmd
|
|
395
|
+
if wdir is not None:
|
|
396
|
+
stage["wdir"] = wdir
|
|
397
|
+
if has_ancestor_dep_drop:
|
|
398
|
+
stage["always_changed"] = True
|
|
399
|
+
stage["deps"] = deps
|
|
400
|
+
stage["outs"] = outs
|
|
401
|
+
if desc is not None:
|
|
402
|
+
stage["desc"] = desc
|
|
403
|
+
return stage
|
|
404
|
+
|
|
405
|
+
|
|
314
406
|
def get_status(
|
|
315
407
|
ck_info: dict | None = None,
|
|
316
408
|
targets: list[str] | None = None,
|
|
@@ -617,6 +709,8 @@ def to_dvc(
|
|
|
617
709
|
dvc.yaml files in the tree, so no synthetic parent stages are needed.
|
|
618
710
|
|
|
619
711
|
Returns a dictionary of DVC stages for the `stages` key of a dvc.yaml.
|
|
712
|
+
Returns ``{}`` when the project has no pipeline and no subprojects (e.g.
|
|
713
|
+
a subproject that is a library with no defined pipeline stages).
|
|
620
714
|
"""
|
|
621
715
|
import calkit.dvc.zip
|
|
622
716
|
from calkit.environments import get_env_lock_fpath
|
|
@@ -624,7 +718,7 @@ def to_dvc(
|
|
|
624
718
|
if ck_info is None:
|
|
625
719
|
ck_info = calkit.load_calkit_info(wdir=wdir)
|
|
626
720
|
if "pipeline" not in ck_info and "subprojects" not in ck_info:
|
|
627
|
-
|
|
721
|
+
return {}
|
|
628
722
|
# Compile subproject pipelines recursively.
|
|
629
723
|
# For isolated subprojects (those with their own .dvc/ directory), DVC
|
|
630
724
|
# won't cross the .dvc/ boundary during --all-pipelines discovery, so we
|
|
@@ -661,67 +755,22 @@ def to_dvc(
|
|
|
661
755
|
stacklevel=2,
|
|
662
756
|
)
|
|
663
757
|
continue
|
|
664
|
-
# Collect all outputs and all deps from the subproject's compiled stages.
|
|
665
|
-
# For matrix stages the template strings (${item.foo}) must be expanded
|
|
666
|
-
# so the wrapper stage has concrete paths.
|
|
667
|
-
all_sp_outs: set[str] = set()
|
|
668
|
-
all_sp_deps: set[str] = set()
|
|
669
|
-
for stage_cfg in sp_dvc_stages.values():
|
|
670
|
-
matrix = stage_cfg.get("matrix")
|
|
671
|
-
replacements = _expand_matrix(matrix) if matrix else [{}]
|
|
672
|
-
for out in stage_cfg.get("outs", []):
|
|
673
|
-
raw = out if isinstance(out, str) else list(out.keys())[0]
|
|
674
|
-
for r in replacements:
|
|
675
|
-
expanded = raw
|
|
676
|
-
for var, val in r.items():
|
|
677
|
-
expanded = expanded.replace(
|
|
678
|
-
f"${{item.{var}}}", str(val)
|
|
679
|
-
)
|
|
680
|
-
# Normalize to strip leading "./" so "data.txt" and
|
|
681
|
-
# "./data.txt" are treated as the same path.
|
|
682
|
-
all_sp_outs.add(Path(expanded).as_posix())
|
|
683
|
-
for dep in stage_cfg.get("deps", []):
|
|
684
|
-
if not isinstance(dep, str):
|
|
685
|
-
continue
|
|
686
|
-
for r in replacements:
|
|
687
|
-
expanded = dep
|
|
688
|
-
for var, val in r.items():
|
|
689
|
-
expanded = expanded.replace(
|
|
690
|
-
f"${{item.{var}}}", str(val)
|
|
691
|
-
)
|
|
692
|
-
all_sp_deps.add(Path(expanded).as_posix())
|
|
693
|
-
# External deps are inputs the subproject reads from outside itself.
|
|
694
|
-
external_deps = all_sp_deps - all_sp_outs
|
|
695
|
-
# The wrapper stage has `wdir: sp`, so DVC resolves deps/outs relative
|
|
696
|
-
# to that directory — do NOT prefix with the subproject path.
|
|
697
|
-
wrapper_deps = sorted(
|
|
698
|
-
d for d in external_deps if not d.startswith(".calkit/env-locks/")
|
|
699
|
-
)
|
|
700
|
-
# Wrap outputs with cache: false + persist: true so the parent doesn't
|
|
701
|
-
# double-cache files already managed by the subproject's DVC, and the
|
|
702
|
-
# nested dvc repro run is responsible for file persistence.
|
|
703
|
-
wrapper_outs_raw = sorted(
|
|
704
|
-
o for o in all_sp_outs if not o.startswith(".calkit/env-locks/")
|
|
705
|
-
)
|
|
706
|
-
# Defensive deduplication: if any output path also appears as a dep
|
|
707
|
-
# (which can happen due to path aliasing), keep it as a dep only.
|
|
708
|
-
wrapper_dep_set = set(wrapper_deps)
|
|
709
|
-
wrapper_outs = [
|
|
710
|
-
{o: {"cache": False, "persist": True}}
|
|
711
|
-
for o in wrapper_outs_raw
|
|
712
|
-
if o not in wrapper_dep_set
|
|
713
|
-
]
|
|
714
758
|
sp_stage_name = Path(sp).name
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
"
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
"desc": (
|
|
759
|
+
wrapper = collapse_dvc_stages(
|
|
760
|
+
sp_dvc_stages,
|
|
761
|
+
cmd="calkit dvc repro",
|
|
762
|
+
wdir=sp,
|
|
763
|
+
desc=(
|
|
721
764
|
f"Automatically generated wrapper for subproject '{sp}'. "
|
|
722
765
|
"Changes made here will be overwritten."
|
|
723
766
|
),
|
|
724
|
-
|
|
767
|
+
)
|
|
768
|
+
# calkit.yaml is the source of truth for the subproject pipeline;
|
|
769
|
+
# tracking it ensures the wrapper goes stale when the pipeline
|
|
770
|
+
# definition changes.
|
|
771
|
+
if "calkit.yaml" not in wrapper["deps"]:
|
|
772
|
+
wrapper["deps"] = sorted(wrapper["deps"] + ["calkit.yaml"])
|
|
773
|
+
wrapper_stages[sp_stage_name] = wrapper
|
|
725
774
|
if "pipeline" not in ck_info:
|
|
726
775
|
if write and wrapper_stages:
|
|
727
776
|
dvc_yaml_fpath = (
|