calkit-python 0.41.20__tar.gz → 0.41.21__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.41.21/.gitmodules +3 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/PKG-INFO +8 -7
- {calkit_python-0.41.20 → calkit_python-0.41.21}/README.md +7 -6
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/list.py +35 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/main/core.py +13 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/new.py +4 -8
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/overleaf.py +12 -2
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/detect.py +102 -25
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/git.py +29 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/models/core.py +11 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/main/test_core.py +67 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_list.py +41 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_new.py +14 -19
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_overleaf.py +47 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/models/test_core.py +22 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_detect.py +59 -2
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/calkit-yaml.md +1 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/cli-reference.md +3 -3
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/index.md +7 -6
- calkit_python-0.41.21/docs/questions.md +31 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/latex-codespaces.md +27 -4
- {calkit_python-0.41.20 → calkit_python-0.41.21}/mkdocs.yml +1 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/pyproject.toml +0 -1
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.claude-plugin/marketplace.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.gitattributes +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.gitignore +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.pre-commit-config.yaml +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.prettierignore +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.prettierrc.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.python-version +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.vscode/launch.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/.vscode/tasks.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/AGENTS.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/CITATION.cff +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/CODE_OF_CONDUCT.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/CONTRIBUTING.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/LICENSE +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/Makefile +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/agent-plugin/.claude-plugin/plugin.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/agent-plugin/skills/conventions/SKILL.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/__main__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/calc.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/check.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/check.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/cloud.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/config.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/delete.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/describe.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/dev.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/import_.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/latex.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/main/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/main/xr.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/office.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/scheduler.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cli/update.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/cloud.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/conda.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/config.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/datasets.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/dependencies.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/docker.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/dvc/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/dvc/core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/dvc/zip.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/environments.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/fs.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/github.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/gui.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/install.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/invenio.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/julia.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/jupyter.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/jupyterlab/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/jupyterlab/routes.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/package.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/remoteEntry.ac9035764d5b2adbb542.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/style.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/labextension/static/third-party-licenses.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/licenses.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/magics.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/matlab.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/models/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/models/io.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/models/iteration.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/models/pipeline.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/notebooks.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/office.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/ops.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/overleaf.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/pipeline.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/releases.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/server.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/main/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/main/test_lock.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/main/test_subprojects.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/main/test_xr.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_check.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_cloud.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_config.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_delete.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_import.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_latex.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_notebooks.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_scheduler.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/cli/test_update.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/dvc/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/dvc/test_core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/dvc/test_zip.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/jupyterlab/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/jupyterlab/conftest.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/jupyterlab/test_routes.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/models/__init__.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/models/test_iteration.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/models/test_pipeline.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_cloud.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_dependencies.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_docker.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_environments.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_fs.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_git.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_install.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_invenio.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_julia.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_licenses.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_matlab.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_notebooks.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_pipeline.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_releases.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/CNAME +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/acknowledgements.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/ai-tools.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/apps.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/calculations.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/cloud-integration.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/datasets.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/dependencies.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/environments.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/examples.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/governance.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/help.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/hpc.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/calkit-fragmentation-compendium.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/caltech.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/connect-zenodo.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab/all-green.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab/collect-data-stale.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab/new-env.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab/new-notebook.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab/pipeline-badge.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/jupyterlab-params.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/pipeline.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/plos-osi-code-2024-03.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/schmidt-sciences.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/img/vscode-nb-params.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/installation.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/jupyterlab.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/local-server.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/notebooks.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/overleaf.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/pipeline/index.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/pipeline/running-and-logging.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/quickstart.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/references.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/releases.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/reproducibility.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/subprojects.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/ai-agents.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/existing-project.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/github-actions.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/actions-repo-secrets.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/quick-actions.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/index.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/jupyterlab.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/office.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/tutorials/vscode-slurm-notebook.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/docs/version-control.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/flake.lock +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/flake.nix +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/.yarnrc.yml +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/babel.config.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/install.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/jest.config.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/jupyter-config/server-config/calkit.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/package.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/schema/plugin.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/__tests__/useQueries.spec.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/calkit-config.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/cell-output-marker.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/commit-dialog.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/environment-editor.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/notebook-registration.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/notebook-toolbar.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/pipeline-status-bar.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/project-info-editor.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/sidebar-settings.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/sidebar.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/components/stage-editor.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/feature-flags.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/file-browser-menu.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/hooks/__tests__/useQueries.test.tsx +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/hooks/useQueries.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/icons.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/index.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/io-tracker.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/pipeline-state.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/queryClient.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/request.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/src/shims-mainmenu.d.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/base.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/cell-output-marker.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/environment-editor.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/environment-selector.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/img/calkit-no-bg.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/index.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/index.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/notebook-toolbar.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/pipeline-status-bar.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/style/sidebar.css +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/tsconfig.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/tsconfig.test.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/.gitignore +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/README.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/jupyter_server_test_config.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/package.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/playwright.config.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/tests/calkit.spec.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/ui-tests/yarn.lock +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/jupyterlab-ext/yarn.lock +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/scripts/generate-docs-references.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/scripts/install.ps1 +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/scripts/install.sh +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/scripts/make-calk9.sh +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/scripts/sync-docs.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/dvc-md5-dir/osx-arm64.txt +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/nb-julia.ipynb +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/nb-params.ipynb +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/nb-subdir.ipynb +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/pipeline.ipynb +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/script.py +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/test/test-log.log +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/uv.lock +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/.gitignore +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/.vscodeignore +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/CHANGELOG.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/LICENSE +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/README.md +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/images/calkit-icon.svg +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/images/calkit-no-bg.png +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/package-lock.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/package.json +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/scripts/set-proposed-api.js +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/environments.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/extension.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/figures/core.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/figures/view.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/notebooks.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/pipeline/core.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/sidebar.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/test/environments.test.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/test/figures.test.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/test/notebooks.test.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/test/pipeline.test.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/src/types.ts +0 -0
- {calkit_python-0.41.20 → calkit_python-0.41.21}/vscode-ext/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: calkit-python
|
|
3
|
-
Version: 0.41.
|
|
3
|
+
Version: 0.41.21
|
|
4
4
|
Summary: Reproducibility simplified.
|
|
5
5
|
Project-URL: Homepage, https://calkit.org
|
|
6
6
|
Project-URL: Issues, https://github.com/calkit/calkit/issues
|
|
@@ -78,15 +78,16 @@ Description-Content-Type: text/markdown
|
|
|
78
78
|
|
|
79
79
|
<!-- INCLUDE: docs/index.md -->
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
Typical research workflows are horizontally-siloed, i.e.,
|
|
82
|
+
various stages--data collection, analysis, writing--are performed in
|
|
83
|
+
disconnected systems,
|
|
84
|
+
turning research into a slow, error-prone, and tedious
|
|
85
|
+
[waterfall](https://en.wikipedia.org/wiki/Waterfall_model) process.
|
|
85
86
|
|
|
86
87
|
Calkit helps you integrate code, data, figures, results, publications,
|
|
87
88
|
and more into a cohesive, traceable, and portable _knowledge creation system_,
|
|
88
|
-
so every output can be traced back to its source
|
|
89
|
-
single command.
|
|
89
|
+
so every output can be traced back to its source (provenance)
|
|
90
|
+
and reproduced with a single command.
|
|
90
91
|
|
|
91
92
|
With industry standard tools combined into a unified and simplified experience
|
|
92
93
|
tailored for research,
|
|
@@ -19,15 +19,16 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- INCLUDE: docs/index.md -->
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
Typical research workflows are horizontally-siloed, i.e.,
|
|
23
|
+
various stages--data collection, analysis, writing--are performed in
|
|
24
|
+
disconnected systems,
|
|
25
|
+
turning research into a slow, error-prone, and tedious
|
|
26
|
+
[waterfall](https://en.wikipedia.org/wiki/Waterfall_model) process.
|
|
26
27
|
|
|
27
28
|
Calkit helps you integrate code, data, figures, results, publications,
|
|
28
29
|
and more into a cohesive, traceable, and portable _knowledge creation system_,
|
|
29
|
-
so every output can be traced back to its source
|
|
30
|
-
single command.
|
|
30
|
+
so every output can be traced back to its source (provenance)
|
|
31
|
+
and reproduced with a single command.
|
|
31
32
|
|
|
32
33
|
With industry standard tools combined into a unified and simplified experience
|
|
33
34
|
tailored for research,
|
|
@@ -174,6 +174,40 @@ def list_presentations(
|
|
|
174
174
|
_list_artifacts("presentations", json_output, declared_only)
|
|
175
175
|
|
|
176
176
|
|
|
177
|
+
def _echo_question(n: int, question: str | dict) -> None:
|
|
178
|
+
"""Print a single question in the human-readable YAML-ish listing format.
|
|
179
|
+
|
|
180
|
+
String questions are shown as a numbered line. Rich (dict) questions are
|
|
181
|
+
shown with their text on the first line followed by hypothesis, answer,
|
|
182
|
+
evidence, and any other fields in YAML-like indented form.
|
|
183
|
+
"""
|
|
184
|
+
if isinstance(question, str):
|
|
185
|
+
typer.echo(f"{n}. {question}")
|
|
186
|
+
return
|
|
187
|
+
# Copy so popping 'question' doesn't mutate the caller's dict.
|
|
188
|
+
question = dict(question)
|
|
189
|
+
text = question.pop("question", "")
|
|
190
|
+
typer.echo(f"{n}. question: {text}")
|
|
191
|
+
for k, v in question.items():
|
|
192
|
+
if isinstance(v, dict):
|
|
193
|
+
typer.echo(f" {k}:")
|
|
194
|
+
for k1, v1 in v.items():
|
|
195
|
+
typer.echo(f" {k1}: {v1}")
|
|
196
|
+
elif isinstance(v, list):
|
|
197
|
+
typer.echo(f" {k}:")
|
|
198
|
+
for item in v:
|
|
199
|
+
if isinstance(item, dict):
|
|
200
|
+
for n1, (k1, v1) in enumerate(item.items()):
|
|
201
|
+
if n1 == 0:
|
|
202
|
+
typer.echo(f" - {k1}: {v1}")
|
|
203
|
+
else:
|
|
204
|
+
typer.echo(f" {k1}: {v1}")
|
|
205
|
+
else:
|
|
206
|
+
typer.echo(f" - {item}")
|
|
207
|
+
else:
|
|
208
|
+
typer.echo(f" {k}: {v}")
|
|
209
|
+
|
|
210
|
+
|
|
177
211
|
@list_app.command(name="questions")
|
|
178
212
|
def list_questions(
|
|
179
213
|
json_output: Annotated[
|
|
@@ -186,7 +220,7 @@ def list_questions(
|
|
|
186
220
|
typer.echo(json.dumps(questions))
|
|
187
221
|
return
|
|
188
222
|
for n, question in enumerate(questions, start=1):
|
|
189
|
-
|
|
223
|
+
_echo_question(n, question)
|
|
190
224
|
|
|
191
225
|
|
|
192
226
|
@list_app.command(name="publications|pubs")
|
|
@@ -786,6 +786,8 @@ def add(
|
|
|
786
786
|
elif to == "git":
|
|
787
787
|
subprocess.call(["git", "add"] + paths)
|
|
788
788
|
elif to == "dvc":
|
|
789
|
+
for path in paths:
|
|
790
|
+
calkit.git.ensure_dvc_pointer_is_not_ignored(repo, path)
|
|
789
791
|
calkit.dvc.run_dvc_command(["add"] + paths)
|
|
790
792
|
elif to == "dvc-zip":
|
|
791
793
|
for path in paths:
|
|
@@ -899,6 +901,7 @@ def add(
|
|
|
899
901
|
f"Adding {path} to DVC since it's already tracked "
|
|
900
902
|
"with DVC"
|
|
901
903
|
)
|
|
904
|
+
calkit.git.ensure_dvc_pointer_is_not_ignored(repo, path)
|
|
902
905
|
calkit.dvc.run_dvc_command(["add", path])
|
|
903
906
|
elif posix_path in pipeline_output_storage:
|
|
904
907
|
# Respect storage explicitly set in the pipeline definition
|
|
@@ -943,6 +946,7 @@ def add(
|
|
|
943
946
|
typer.echo(f"Would add {path} to DVC (per extension)")
|
|
944
947
|
else:
|
|
945
948
|
typer.echo(f"Adding {path} to DVC per its extension")
|
|
949
|
+
calkit.git.ensure_dvc_pointer_is_not_ignored(repo, path)
|
|
946
950
|
calkit.dvc.run_dvc_command(["add", path])
|
|
947
951
|
elif calkit.dvc.zip.is_zip_candidate(path):
|
|
948
952
|
if dry_run:
|
|
@@ -963,6 +967,7 @@ def add(
|
|
|
963
967
|
typer.echo(
|
|
964
968
|
f"Adding {path} to DVC since it's greater than 1 MB"
|
|
965
969
|
)
|
|
970
|
+
calkit.git.ensure_dvc_pointer_is_not_ignored(repo, path)
|
|
966
971
|
calkit.dvc.run_dvc_command(["add", path])
|
|
967
972
|
else:
|
|
968
973
|
if dry_run:
|
|
@@ -3218,6 +3223,14 @@ def call_dvc(
|
|
|
3218
3223
|
and DVC is not installed.
|
|
3219
3224
|
"""
|
|
3220
3225
|
result = calkit.dvc.run_dvc_command(sys.argv[2:])
|
|
3226
|
+
if result != 0 and len(sys.argv) > 2 and sys.argv[2] == "add":
|
|
3227
|
+
typer.secho(
|
|
3228
|
+
"Hint: If DVC failed because a .dvc pointer file is git-ignored, "
|
|
3229
|
+
"use `calkit add --to=dvc <path>` instead so Calkit can "
|
|
3230
|
+
"automatically manage the gitignore exception.",
|
|
3231
|
+
fg=typer.colors.YELLOW,
|
|
3232
|
+
err=True,
|
|
3233
|
+
)
|
|
3221
3234
|
sys.exit(result)
|
|
3222
3235
|
|
|
3223
3236
|
|
|
@@ -3174,7 +3174,7 @@ def new_release(
|
|
|
3174
3174
|
detected_kind, artifact = calkit.releases.find_artifact(path, ck_info)
|
|
3175
3175
|
if detected_kind is None:
|
|
3176
3176
|
detected_kind = calkit.detect.detect_artifact_kind(
|
|
3177
|
-
pathlib.Path(path).as_posix()
|
|
3177
|
+
pathlib.Path(path).as_posix(),
|
|
3178
3178
|
)
|
|
3179
3179
|
if release_kind is None:
|
|
3180
3180
|
if detected_kind is None:
|
|
@@ -3258,16 +3258,12 @@ def new_release(
|
|
|
3258
3258
|
# stored as-is (renamed); folders and whole-project releases are
|
|
3259
3259
|
# zipped.
|
|
3260
3260
|
project_name = calkit.detect_project_name(prepend_owner=False)
|
|
3261
|
-
if path == ".":
|
|
3261
|
+
if path == "." or os.path.isdir(path):
|
|
3262
3262
|
stored_filename = f"{project_name}-{name}.zip"
|
|
3263
3263
|
is_zip = True
|
|
3264
|
-
elif os.path.isdir(path):
|
|
3265
|
-
folder_name = os.path.basename(os.path.normpath(path))
|
|
3266
|
-
stored_filename = f"{project_name}-{folder_name}-{name}.zip"
|
|
3267
|
-
is_zip = True
|
|
3268
3264
|
elif os.path.isfile(path):
|
|
3269
|
-
|
|
3270
|
-
stored_filename = f"{project_name}-{
|
|
3265
|
+
_, ext = os.path.splitext(os.path.basename(path))
|
|
3266
|
+
stored_filename = f"{project_name}-{name}{ext}"
|
|
3271
3267
|
is_zip = False
|
|
3272
3268
|
else:
|
|
3273
3269
|
raise_error(f"Release path '{path}' does not exist")
|
|
@@ -437,7 +437,12 @@ def sync(
|
|
|
437
437
|
raise_error("No Overleaf sync info found")
|
|
438
438
|
overleaf_sync_dirs = list(overleaf_info.keys())
|
|
439
439
|
if paths is not None:
|
|
440
|
-
paths = [
|
|
440
|
+
paths = [
|
|
441
|
+
(os.path.dirname(p) if os.path.isfile(p) else p)
|
|
442
|
+
.strip()
|
|
443
|
+
.rstrip("/\\")
|
|
444
|
+
for p in paths
|
|
445
|
+
]
|
|
441
446
|
for path in paths:
|
|
442
447
|
if path not in overleaf_sync_dirs:
|
|
443
448
|
raise_error(f"Path '{path}' is not synced with Overleaf")
|
|
@@ -611,7 +616,12 @@ def get_status(
|
|
|
611
616
|
raise_error("No Overleaf sync info found")
|
|
612
617
|
overleaf_sync_dirs = list(overleaf_info.keys())
|
|
613
618
|
if paths is not None:
|
|
614
|
-
paths = [
|
|
619
|
+
paths = [
|
|
620
|
+
(os.path.dirname(p) if os.path.isfile(p) else p)
|
|
621
|
+
.strip()
|
|
622
|
+
.rstrip("/\\")
|
|
623
|
+
for p in paths
|
|
624
|
+
]
|
|
615
625
|
for path in paths:
|
|
616
626
|
if path not in overleaf_sync_dirs:
|
|
617
627
|
raise_error(f"Path '{path}' is not synced with Overleaf")
|
|
@@ -13,6 +13,8 @@ import sys
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Literal
|
|
15
15
|
|
|
16
|
+
import pathspec
|
|
17
|
+
|
|
16
18
|
NotebookLanguage = Literal["python", "julia", "r"]
|
|
17
19
|
|
|
18
20
|
|
|
@@ -1549,6 +1551,20 @@ def _is_stdlib_module(module_name: str) -> bool:
|
|
|
1549
1551
|
return base_module in common_stdlib
|
|
1550
1552
|
|
|
1551
1553
|
|
|
1554
|
+
_PYTHON_IMPORT_TO_DISTRIBUTION = {
|
|
1555
|
+
"sklearn": "scikit-learn",
|
|
1556
|
+
"skimage": "scikit-image",
|
|
1557
|
+
"cv2": "opencv-python",
|
|
1558
|
+
"PIL": "Pillow",
|
|
1559
|
+
"bs4": "beautifulsoup4",
|
|
1560
|
+
"yaml": "PyYAML",
|
|
1561
|
+
"dateutil": "python-dateutil",
|
|
1562
|
+
"dotenv": "python-dotenv",
|
|
1563
|
+
"serial": "pyserial",
|
|
1564
|
+
"git": "GitPython",
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
|
|
1552
1568
|
def detect_python_dependencies(
|
|
1553
1569
|
script_path: str | None = None,
|
|
1554
1570
|
code: str | None = None,
|
|
@@ -1614,13 +1630,21 @@ def detect_python_dependencies(
|
|
|
1614
1630
|
if not _is_stdlib_module(module_name):
|
|
1615
1631
|
# Get the top-level package name
|
|
1616
1632
|
top_level = module_name.split(".")[0]
|
|
1617
|
-
dependencies.add(
|
|
1633
|
+
dependencies.add(
|
|
1634
|
+
_PYTHON_IMPORT_TO_DISTRIBUTION.get(
|
|
1635
|
+
top_level, top_level
|
|
1636
|
+
)
|
|
1637
|
+
)
|
|
1618
1638
|
elif isinstance(node, ast.ImportFrom):
|
|
1619
1639
|
if node.module and node.level == 0:
|
|
1620
1640
|
module_name = node.module
|
|
1621
1641
|
if not _is_stdlib_module(module_name):
|
|
1622
1642
|
top_level = module_name.split(".")[0]
|
|
1623
|
-
dependencies.add(
|
|
1643
|
+
dependencies.add(
|
|
1644
|
+
_PYTHON_IMPORT_TO_DISTRIBUTION.get(
|
|
1645
|
+
top_level, top_level
|
|
1646
|
+
)
|
|
1647
|
+
)
|
|
1624
1648
|
return sorted(list(dependencies))
|
|
1625
1649
|
|
|
1626
1650
|
|
|
@@ -1927,6 +1951,21 @@ PRESENTATION_NAMES = {
|
|
|
1927
1951
|
"talk.pdf",
|
|
1928
1952
|
}
|
|
1929
1953
|
|
|
1954
|
+
DETECTION_IGNORE_FPATH = os.path.join(".calkit", "ignore")
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
def load_detection_ignore(wdir: str | None = None) -> pathspec.PathSpec | None:
|
|
1958
|
+
"""Load ``.calkit/ignore`` artifact detection exclusions."""
|
|
1959
|
+
import pathspec
|
|
1960
|
+
|
|
1961
|
+
fpath = DETECTION_IGNORE_FPATH
|
|
1962
|
+
if wdir is not None:
|
|
1963
|
+
fpath = os.path.join(wdir, fpath)
|
|
1964
|
+
if not os.path.isfile(fpath):
|
|
1965
|
+
return None
|
|
1966
|
+
with open(fpath, encoding="utf-8") as f:
|
|
1967
|
+
return pathspec.PathSpec.from_lines("gitwildmatch", f)
|
|
1968
|
+
|
|
1930
1969
|
|
|
1931
1970
|
def _ancestor_dir_names(rel_path: str) -> set[str]:
|
|
1932
1971
|
"""Lower-cased names of the ancestor directories of a "/"-separated path."""
|
|
@@ -1938,8 +1977,12 @@ def _path_ext(rel_path: str) -> str:
|
|
|
1938
1977
|
return ("." + name.rsplit(".", 1)[-1].lower()) if "." in name else ""
|
|
1939
1978
|
|
|
1940
1979
|
|
|
1941
|
-
def is_figure_path(
|
|
1980
|
+
def is_figure_path(
|
|
1981
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
1982
|
+
) -> bool:
|
|
1942
1983
|
"""Whether a repo-relative path looks like an auto-detectable figure."""
|
|
1984
|
+
if ignore is not None and ignore.match_file(rel_path):
|
|
1985
|
+
return False
|
|
1943
1986
|
ancestors = _ancestor_dir_names(rel_path)
|
|
1944
1987
|
ext = _path_ext(rel_path)
|
|
1945
1988
|
if ext in FIGURE_EXTENSIONS and ancestors & FIGURE_DIRS:
|
|
@@ -1951,37 +1994,49 @@ def is_figure_path(rel_path: str) -> bool:
|
|
|
1951
1994
|
return False
|
|
1952
1995
|
|
|
1953
1996
|
|
|
1954
|
-
def is_dataset_path(
|
|
1997
|
+
def is_dataset_path(
|
|
1998
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
1999
|
+
) -> bool:
|
|
1955
2000
|
"""Whether a repo-relative path looks like an auto-detectable dataset."""
|
|
2001
|
+
if ignore is not None and ignore.match_file(rel_path):
|
|
2002
|
+
return False
|
|
1956
2003
|
return (
|
|
1957
2004
|
_path_ext(rel_path) in DATASET_EXTENSIONS
|
|
1958
2005
|
and bool(_ancestor_dir_names(rel_path) & DATA_DIRS)
|
|
1959
|
-
and not is_figure_path(rel_path)
|
|
2006
|
+
and not is_figure_path(rel_path, ignore=ignore)
|
|
1960
2007
|
)
|
|
1961
2008
|
|
|
1962
2009
|
|
|
1963
|
-
def is_result_path(
|
|
2010
|
+
def is_result_path(
|
|
2011
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
2012
|
+
) -> bool:
|
|
1964
2013
|
"""Whether a repo-relative path looks like an auto-detectable result.
|
|
1965
2014
|
|
|
1966
2015
|
A result is a data-like file (JSON, CSV, etc.) under a ``results``-style
|
|
1967
2016
|
directory. Anything already detected as a figure or dataset is excluded.
|
|
1968
2017
|
"""
|
|
2018
|
+
if ignore is not None and ignore.match_file(rel_path):
|
|
2019
|
+
return False
|
|
1969
2020
|
return (
|
|
1970
2021
|
_path_ext(rel_path) in RESULT_EXTENSIONS
|
|
1971
2022
|
and bool(_ancestor_dir_names(rel_path) & RESULT_DIRS)
|
|
1972
|
-
and not is_figure_path(rel_path)
|
|
1973
|
-
and not is_dataset_path(rel_path)
|
|
2023
|
+
and not is_figure_path(rel_path, ignore=ignore)
|
|
2024
|
+
and not is_dataset_path(rel_path, ignore=ignore)
|
|
1974
2025
|
)
|
|
1975
2026
|
|
|
1976
2027
|
|
|
1977
|
-
def is_presentation_path(
|
|
2028
|
+
def is_presentation_path(
|
|
2029
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
2030
|
+
) -> bool:
|
|
1978
2031
|
"""Whether a repo-relative path looks like an auto-detectable presentation.
|
|
1979
2032
|
|
|
1980
2033
|
Either a slide-deck file (PDF/PPTX/KEY) under a ``slides``/``presentations``
|
|
1981
2034
|
directory, or a file with a presentation-like name (e.g. ``slides.pdf``,
|
|
1982
2035
|
``presentation.pdf``) anywhere. Figures are excluded.
|
|
1983
2036
|
"""
|
|
1984
|
-
if
|
|
2037
|
+
if ignore is not None and ignore.match_file(rel_path):
|
|
2038
|
+
return False
|
|
2039
|
+
if is_figure_path(rel_path, ignore=ignore):
|
|
1985
2040
|
return False
|
|
1986
2041
|
name = rel_path.rsplit("/", 1)[-1].lower()
|
|
1987
2042
|
if name in PRESENTATION_NAMES:
|
|
@@ -2018,7 +2073,9 @@ PUBLICATION_NAMES = {
|
|
|
2018
2073
|
}
|
|
2019
2074
|
|
|
2020
2075
|
|
|
2021
|
-
def is_publication_path(
|
|
2076
|
+
def is_publication_path(
|
|
2077
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
2078
|
+
) -> bool:
|
|
2022
2079
|
"""Whether a repo-relative path looks like an auto-detectable publication.
|
|
2023
2080
|
|
|
2024
2081
|
Either a document (PDF/TeX/DOCX) under a ``paper``/``publication``/
|
|
@@ -2026,7 +2083,11 @@ def is_publication_path(rel_path: str) -> bool:
|
|
|
2026
2083
|
``manuscript.pdf``, ``main.tex``) anywhere. Figures and presentations are
|
|
2027
2084
|
excluded.
|
|
2028
2085
|
"""
|
|
2029
|
-
if
|
|
2086
|
+
if ignore is not None and ignore.match_file(rel_path):
|
|
2087
|
+
return False
|
|
2088
|
+
if is_figure_path(rel_path, ignore=ignore) or is_presentation_path(
|
|
2089
|
+
rel_path, ignore=ignore
|
|
2090
|
+
):
|
|
2030
2091
|
return False
|
|
2031
2092
|
name = rel_path.rsplit("/", 1)[-1].lower()
|
|
2032
2093
|
if name in PUBLICATION_NAMES:
|
|
@@ -2036,19 +2097,21 @@ def is_publication_path(rel_path: str) -> bool:
|
|
|
2036
2097
|
)
|
|
2037
2098
|
|
|
2038
2099
|
|
|
2039
|
-
def detect_artifact_kind(
|
|
2100
|
+
def detect_artifact_kind(
|
|
2101
|
+
rel_path: str, *, ignore: pathspec.PathSpec | None = None
|
|
2102
|
+
) -> str | None:
|
|
2040
2103
|
"""Infer an artifact's release kind from its repo-relative path.
|
|
2041
2104
|
|
|
2042
2105
|
Returns one of ``"figure"``, ``"presentation"``, ``"publication"``, or
|
|
2043
2106
|
``"dataset"``, or ``None`` if the path doesn't look like any of these.
|
|
2044
2107
|
"""
|
|
2045
|
-
if is_figure_path(rel_path):
|
|
2108
|
+
if is_figure_path(rel_path, ignore=ignore):
|
|
2046
2109
|
return "figure"
|
|
2047
|
-
if is_presentation_path(rel_path):
|
|
2110
|
+
if is_presentation_path(rel_path, ignore=ignore):
|
|
2048
2111
|
return "presentation"
|
|
2049
|
-
if is_publication_path(rel_path):
|
|
2112
|
+
if is_publication_path(rel_path, ignore=ignore):
|
|
2050
2113
|
return "publication"
|
|
2051
|
-
if is_dataset_path(rel_path):
|
|
2114
|
+
if is_dataset_path(rel_path, ignore=ignore):
|
|
2052
2115
|
return "dataset"
|
|
2053
2116
|
return None
|
|
2054
2117
|
|
|
@@ -2090,6 +2153,7 @@ def _collapse_dataset_folders(paths: list[str]) -> list[str]:
|
|
|
2090
2153
|
def detect_figures(
|
|
2091
2154
|
candidate_paths: list[str],
|
|
2092
2155
|
reserved_paths: list[str] | tuple[str, ...] = (),
|
|
2156
|
+
ignore: pathspec.PathSpec | None = None,
|
|
2093
2157
|
) -> list[str]:
|
|
2094
2158
|
"""Auto-detected figure paths among ``candidate_paths``.
|
|
2095
2159
|
|
|
@@ -2103,7 +2167,7 @@ def detect_figures(
|
|
|
2103
2167
|
for p in candidate_paths
|
|
2104
2168
|
if not _is_hidden_path(p)
|
|
2105
2169
|
and not _is_under_any_dir(p, reserved)
|
|
2106
|
-
and is_figure_path(p)
|
|
2170
|
+
and is_figure_path(p, ignore=ignore)
|
|
2107
2171
|
}
|
|
2108
2172
|
)
|
|
2109
2173
|
|
|
@@ -2112,6 +2176,7 @@ def detect_datasets(
|
|
|
2112
2176
|
candidate_paths: list[str],
|
|
2113
2177
|
reserved_paths: list[str] | tuple[str, ...] = (),
|
|
2114
2178
|
figure_paths: list[str] | tuple[str, ...] = (),
|
|
2179
|
+
ignore: pathspec.PathSpec | None = None,
|
|
2115
2180
|
) -> list[str]:
|
|
2116
2181
|
"""Auto-detected dataset paths among ``candidate_paths``.
|
|
2117
2182
|
|
|
@@ -2126,7 +2191,7 @@ def detect_datasets(
|
|
|
2126
2191
|
if not _is_hidden_path(p)
|
|
2127
2192
|
and not _is_under_any_dir(p, reserved)
|
|
2128
2193
|
and p not in figset
|
|
2129
|
-
and is_dataset_path(p)
|
|
2194
|
+
and is_dataset_path(p, ignore=ignore)
|
|
2130
2195
|
}
|
|
2131
2196
|
return _collapse_dataset_folders(sorted(files))
|
|
2132
2197
|
|
|
@@ -2134,6 +2199,7 @@ def detect_datasets(
|
|
|
2134
2199
|
def detect_results(
|
|
2135
2200
|
candidate_paths: list[str],
|
|
2136
2201
|
reserved_paths: list[str] | tuple[str, ...] = (),
|
|
2202
|
+
ignore: pathspec.PathSpec | None = None,
|
|
2137
2203
|
) -> list[str]:
|
|
2138
2204
|
"""Auto-detected result paths among ``candidate_paths``.
|
|
2139
2205
|
|
|
@@ -2147,7 +2213,7 @@ def detect_results(
|
|
|
2147
2213
|
for p in candidate_paths
|
|
2148
2214
|
if not _is_hidden_path(p)
|
|
2149
2215
|
and not _is_under_any_dir(p, reserved)
|
|
2150
|
-
and is_result_path(p)
|
|
2216
|
+
and is_result_path(p, ignore=ignore)
|
|
2151
2217
|
}
|
|
2152
2218
|
)
|
|
2153
2219
|
|
|
@@ -2155,6 +2221,7 @@ def detect_results(
|
|
|
2155
2221
|
def detect_presentations(
|
|
2156
2222
|
candidate_paths: list[str],
|
|
2157
2223
|
reserved_paths: list[str] | tuple[str, ...] = (),
|
|
2224
|
+
ignore: pathspec.PathSpec | None = None,
|
|
2158
2225
|
) -> list[str]:
|
|
2159
2226
|
"""Auto-detected presentation paths among ``candidate_paths``."""
|
|
2160
2227
|
reserved = list(reserved_paths)
|
|
@@ -2164,7 +2231,7 @@ def detect_presentations(
|
|
|
2164
2231
|
for p in candidate_paths
|
|
2165
2232
|
if not _is_hidden_path(p)
|
|
2166
2233
|
and not _is_under_any_dir(p, reserved)
|
|
2167
|
-
and is_presentation_path(p)
|
|
2234
|
+
and is_presentation_path(p, ignore=ignore)
|
|
2168
2235
|
}
|
|
2169
2236
|
)
|
|
2170
2237
|
|
|
@@ -2254,18 +2321,28 @@ def detect_project_artifacts(
|
|
|
2254
2321
|
|
|
2255
2322
|
if ck_info is None:
|
|
2256
2323
|
ck_info = calkit.load_calkit_info(wdir=wdir)
|
|
2324
|
+
ignore = load_detection_ignore(wdir=wdir)
|
|
2257
2325
|
reserved = _reserved_artifact_paths(wdir=wdir, ck_info=ck_info)
|
|
2258
2326
|
candidates = list(
|
|
2259
2327
|
dict.fromkeys(
|
|
2260
2328
|
[*list_repo_files(wdir=wdir), *list_dvc_tracked_files(wdir=wdir)]
|
|
2261
2329
|
)
|
|
2262
2330
|
)
|
|
2263
|
-
figures = detect_figures(
|
|
2331
|
+
figures = detect_figures(
|
|
2332
|
+
candidates, reserved_paths=reserved, ignore=ignore
|
|
2333
|
+
)
|
|
2264
2334
|
datasets = detect_datasets(
|
|
2265
|
-
candidates,
|
|
2335
|
+
candidates,
|
|
2336
|
+
reserved_paths=reserved,
|
|
2337
|
+
figure_paths=figures,
|
|
2338
|
+
ignore=ignore,
|
|
2339
|
+
)
|
|
2340
|
+
results = detect_results(
|
|
2341
|
+
candidates, reserved_paths=reserved, ignore=ignore
|
|
2342
|
+
)
|
|
2343
|
+
presentations = detect_presentations(
|
|
2344
|
+
candidates, reserved_paths=reserved, ignore=ignore
|
|
2266
2345
|
)
|
|
2267
|
-
results = detect_results(candidates, reserved_paths=reserved)
|
|
2268
|
-
presentations = detect_presentations(candidates, reserved_paths=reserved)
|
|
2269
2346
|
return {
|
|
2270
2347
|
"figures": figures,
|
|
2271
2348
|
"datasets": datasets,
|
|
@@ -337,3 +337,32 @@ def ensure_path_is_not_ignored(
|
|
|
337
337
|
if target_repo.ignored(target_path) and _depth < 10:
|
|
338
338
|
ensure_path_is_not_ignored(target_repo, target_path, _depth + 1)
|
|
339
339
|
return True
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def ensure_dvc_pointer_is_not_ignored(repo, path: str) -> None:
|
|
343
|
+
"""Ensure the .dvc pointer for ``path`` will not be Git-ignored.
|
|
344
|
+
|
|
345
|
+
A broad pattern in a ``.gitignore`` (e.g. ``*.pdf*``) can also match the
|
|
346
|
+
``<path>.dvc`` pointer DVC commits to Git, causing ``dvc add`` to fail with
|
|
347
|
+
"bad DVC file name ... is git-ignored". This appends a ``!*.dvc`` negation
|
|
348
|
+
to the ``.gitignore`` in the pointer's own directory (which wins under Git
|
|
349
|
+
precedence) so pointers stay tracked. Idempotent.
|
|
350
|
+
"""
|
|
351
|
+
path = path.replace("\\", "/").rstrip("/")
|
|
352
|
+
pointer = path + ".dvc"
|
|
353
|
+
if pointer not in repo.ignored(pointer):
|
|
354
|
+
return
|
|
355
|
+
pointer_dir = os.path.dirname(pointer)
|
|
356
|
+
gitignore_path = os.path.join(repo.working_dir, pointer_dir, ".gitignore")
|
|
357
|
+
exception = "!*.dvc"
|
|
358
|
+
existing_lines = []
|
|
359
|
+
if os.path.isfile(gitignore_path):
|
|
360
|
+
with open(gitignore_path, "r", encoding="utf-8") as f:
|
|
361
|
+
existing_lines = f.read().splitlines()
|
|
362
|
+
if exception in existing_lines:
|
|
363
|
+
return # Already present
|
|
364
|
+
os.makedirs(os.path.dirname(gitignore_path), exist_ok=True)
|
|
365
|
+
with open(gitignore_path, "a", encoding="utf-8") as f:
|
|
366
|
+
if existing_lines and existing_lines[-1] != "":
|
|
367
|
+
f.write("\n")
|
|
368
|
+
f.write(exception + "\n")
|
|
@@ -351,13 +351,23 @@ class ResultsEvidence(BaseModel):
|
|
|
351
351
|
explanation: str | None = None
|
|
352
352
|
|
|
353
353
|
|
|
354
|
+
class PublicationEvidence(BaseModel):
|
|
355
|
+
"""Evidence in the form of a publication."""
|
|
356
|
+
|
|
357
|
+
kind: Literal["publication"] = "publication"
|
|
358
|
+
path: str
|
|
359
|
+
explanation: str | None = None
|
|
360
|
+
|
|
361
|
+
|
|
354
362
|
class Question(BaseModel):
|
|
355
363
|
"""A question the project hopes to answer."""
|
|
356
364
|
|
|
357
365
|
question: str
|
|
358
366
|
hypothesis: str | None = None
|
|
359
367
|
answer: str | None = None
|
|
360
|
-
evidence:
|
|
368
|
+
evidence: (
|
|
369
|
+
list[FigureEvidence | ResultsEvidence | PublicationEvidence] | None
|
|
370
|
+
) = None
|
|
361
371
|
|
|
362
372
|
|
|
363
373
|
class Dependency(BaseModel):
|
|
@@ -1836,3 +1836,70 @@ def test_run_downstream_does_not_submit_unrelated_sweep(tmp_dir):
|
|
|
1836
1836
|
assert os.path.exists("other.txt")
|
|
1837
1837
|
assert not os.path.exists("out-1.txt")
|
|
1838
1838
|
assert not os.path.exists("out-2.txt")
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
def test_add_dvc_pointer_unignored(tmp_dir):
|
|
1842
|
+
"""Test that calkit add --to=dvc ensures the .dvc pointer is unignored."""
|
|
1843
|
+
subprocess.check_call(["git", "init"])
|
|
1844
|
+
subprocess.check_call(["calkit", "init"])
|
|
1845
|
+
os.makedirs("pubs/defense/ppt")
|
|
1846
|
+
with open("pubs/defense/ppt/.gitignore", "w") as f:
|
|
1847
|
+
f.write("*.pdf*\n")
|
|
1848
|
+
pdf_path = "pubs/defense/ppt/McCabe.pdf"
|
|
1849
|
+
with open(pdf_path, "w") as f:
|
|
1850
|
+
f.write("dummy pdf content")
|
|
1851
|
+
subprocess.check_call(["calkit", "add", "--to=dvc", pdf_path])
|
|
1852
|
+
# Assert pointer exists
|
|
1853
|
+
dvc_file = f"{pdf_path}.dvc"
|
|
1854
|
+
assert os.path.exists(dvc_file)
|
|
1855
|
+
# Assert pointer is NOT ignored (should return 1)
|
|
1856
|
+
res = subprocess.run(["git", "check-ignore", "-q", dvc_file])
|
|
1857
|
+
assert res.returncode == 1
|
|
1858
|
+
# Assert data file IS still ignored
|
|
1859
|
+
res_data = subprocess.run(["git", "check-ignore", "-q", pdf_path])
|
|
1860
|
+
assert res_data.returncode == 0
|
|
1861
|
+
# Assert it was staged
|
|
1862
|
+
staged = calkit.git.get_staged_files()
|
|
1863
|
+
assert dvc_file in staged
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
def test_add_dvc_pointer_unignored_idempotent(tmp_dir):
|
|
1867
|
+
"""Test that the unignore logic does not duplicate lines."""
|
|
1868
|
+
subprocess.check_call(["git", "init"])
|
|
1869
|
+
subprocess.check_call(["calkit", "init"])
|
|
1870
|
+
os.makedirs("data")
|
|
1871
|
+
with open("data/.gitignore", "w") as f:
|
|
1872
|
+
f.write("*.pdf*\n")
|
|
1873
|
+
pdf_path = "data/doc.pdf"
|
|
1874
|
+
with open(pdf_path, "w") as f:
|
|
1875
|
+
f.write("doc")
|
|
1876
|
+
subprocess.check_call(["calkit", "add", "--to=dvc", pdf_path])
|
|
1877
|
+
with open("data/.gitignore") as f:
|
|
1878
|
+
content1 = f.read()
|
|
1879
|
+
# Second add should not duplicate
|
|
1880
|
+
subprocess.check_call(["calkit", "add", "--to=dvc", pdf_path])
|
|
1881
|
+
with open("data/.gitignore") as f:
|
|
1882
|
+
content2 = f.read()
|
|
1883
|
+
assert content1 == content2
|
|
1884
|
+
assert content1.count("!*.dvc") == 1
|
|
1885
|
+
|
|
1886
|
+
|
|
1887
|
+
def test_call_dvc_passthrough_hint(tmp_dir):
|
|
1888
|
+
"""Test that calkit dvc add prints a hint if .dvc is ignored."""
|
|
1889
|
+
subprocess.check_call(["git", "init"])
|
|
1890
|
+
subprocess.check_call(["calkit", "init"])
|
|
1891
|
+
os.makedirs("data")
|
|
1892
|
+
with open("data/.gitignore", "w") as f:
|
|
1893
|
+
f.write("*.pdf*\n")
|
|
1894
|
+
pdf_path = "data/doc.pdf"
|
|
1895
|
+
with open(pdf_path, "w") as f:
|
|
1896
|
+
f.write("doc")
|
|
1897
|
+
# Run the passthrough dvc add which will fail because it's ignored
|
|
1898
|
+
res = subprocess.run(
|
|
1899
|
+
["calkit", "dvc", "add", pdf_path], capture_output=True, text=True
|
|
1900
|
+
)
|
|
1901
|
+
assert res.returncode != 0
|
|
1902
|
+
assert (
|
|
1903
|
+
"Hint: If DVC failed because a .dvc pointer file is git-ignored"
|
|
1904
|
+
in res.stderr
|
|
1905
|
+
)
|