cellpy 2.1.2a2__tar.gz → 2.1.2a4__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.
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.gitignore +4 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/cycle_status_2026-08-09.md +18 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_original.md +51 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_plan.md +124 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue845_status.md +49 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_original.md +36 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_plan.md +54 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue846_status.md +13 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_original.md +65 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_plan.md +46 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue849_status.md +13 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_original.md +53 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_plan.md +44 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue850_status.md +13 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_original.md +52 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_plan.md +134 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue851_status.md +97 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_original.md +66 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_plan.md +42 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue853_status.md +12 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_original.md +9 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_plan.md +47 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue855_status.md +12 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_original.md +45 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_plan.md +90 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue862_status.md +28 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_original.md +35 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_plan.md +103 -0
- cellpy-2.1.2a4/.issueflows/03-solved-issues/issue863_status.md +33 -0
- cellpy-2.1.2a4/.issueflows/04-designs-and-guides/atomic-file-writes.md +45 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/test-registry.md +24 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/HISTORY.md +9 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/PKG-INFO +1 -1
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/cli_api.py +27 -7
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/__init__.py +4 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/collection.py +2 -2
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/collector.py +8 -0
- cellpy-2.1.2a4/cellpy/collect/dva.py +95 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/options.py +10 -1
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/__init__.py +3 -1
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/loader.py +136 -21
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/models.py +46 -1
- cellpy-2.1.2a4/cellpy/config/session.py +128 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/ica.py +30 -55
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/mpl.py +13 -4
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/plotly.py +12 -4
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/collected.py +77 -1
- cellpy-2.1.2a4/cellpy/readers/cellpy_file/atomic.py +78 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/v9.py +36 -11
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/write.py +4 -2
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellreader.py +248 -43
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/config_declarations.py +8 -8
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/uv.lock +3 -3
- cellpy-2.1.2a2/cellpy/config/session.py +0 -92
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.aliases +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/commands/build.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/commands/create-original-issue-file.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/cellpy-core-migration.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/cellpy-workspace.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/graphify.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/issueflow-rules.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/kiss.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/rules/this-project.mdc +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/caveman/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/gh-ci/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/grill-me/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-archive/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-auto/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-build/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-cleanup/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-close/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-comments/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-cycle/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-doctor/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-epic/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-fix/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-graphify/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-history-update/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-init/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-issue/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-pause/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-pick/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-plan/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-review/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-status/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-version-bump/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/iflow-yolo/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.cursor/skills/issueflow-build/SKILL.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.dockerignore +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.editorconfig +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.env_example +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.gitattributes +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/README.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/migrate_prms_calls.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/scan_hardcoded_headers.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/00-tools/scan_member_usage.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/01-current-issues/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/02-partly-solved-issues/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/2026-07-09_archived_issues.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/2026-07-31_archived_issues.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/cycle_status_2026-07-31.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/cycle_status_2026-08-08.md +0 -0
- {cellpy-2.1.2a2/.issueflows/02-partly-solved-issues → cellpy-2.1.2a4/.issueflows/03-solved-issues}/issue459_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue786_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue799_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue800_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue801_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue802_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue804_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue809_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue816_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue817_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue818_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue819_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue820_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue821_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue822_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue825_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue837_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_original.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/03-solved-issues/issue839_status.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/batch-load-orchestrator.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/bdf-export.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/build-and-versioning.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-architecture.excalidraw +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-branching.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-v2-epic.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpy-workspace-repos.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cellpycell-di-restructuring.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/ci-tiers.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cli-api-echo-binder.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/cli-light-startup.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/docs-on-master.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/essential-tests.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/filters-and-plot-filtering.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/harmonized-raw-default.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/marimo-docs.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/otherpath-upath.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-backends.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-batch-summary.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-collected.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-cycle-legend.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-prepare.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/plotting-registry.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/release-procedure.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/testing-and-coverage.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/this-project.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/04-designs-and-guides/v2-cellpycore-pin-gate.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/05-epics/.gitkeep +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/05-epics/epic567_plan.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.issueflows/config.toml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/.readthedocs.yaml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/AGENTS.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/AUTHORS.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/CODE_OF_CONDUCT.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/CONTRIBUTING.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/DEPRECATIONS.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/LICENSE.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/README.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_data_structure.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_folder_structure.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_packaging_pypi.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_cellpy_setup.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_conda_package.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_docs.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_loaders_and_instruments.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/dev_various.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_developers_guide/index.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/01_getting_started_tutorial.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/02_read_cell_data.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/03_more_about_get.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/04_other_interactions.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/05_configuring.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/06_pandas.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/07_data_mining.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basic_interactions/08_the_cellpy_cmd.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/basics.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/examples.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/index.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/01_arbin.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/02_maccor.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/03_PEC.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/04_Neware.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/05_biologics.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders/06_custom.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/loaders.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/images/templates_jupyterlab_001.png +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/images/templates_jupyterlab_002.png +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_get_cap.ipynb +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_simple_plot.ipynb +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_templates.ipynb +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks/tutorial_xxx-checkpoint.ipynb +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/notebooks.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/tips_and_tricks.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/batch.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/collectors.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/custom_file_loaders.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/easyplot.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/figures/tutorials_utils_plotting_fig1.png +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/figures/tutorials_utils_plotting_fig2.png +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/ica.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/plotting.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/templates.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils/tut_ocv_rlx.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_examples_and_tutorials/utils.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/formats.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/index.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/installation.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/_main_description/usage.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/_old_docs/index.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/README.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/baselines/v1x_ubuntu_py313.json +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/check_baseline.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/conftest.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/paths.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/benchmarks/test_performance.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/_deprecation.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/_version.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/_dbengine.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/aggregate.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/db.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/facade.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/journal.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/layout.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/outputs.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/policy.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/qc.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/result.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/runner.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/batch/store.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/cli.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/_summary_ops.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/cells.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/curves.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/ica.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/collect/summary.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/credentials.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/legacy.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/migrate.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/reference.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/sources.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/config/types.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exceptions.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/bdf.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/exporters/tabular.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/cycles.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/filters/summary.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/connections.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/internals/otherpath.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_alias_module.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_importing.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_module.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_syncronized.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/_version.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/apipkg/py.typed +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/LICENSE +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/btsda.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/cli.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/dicts.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/formats.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/main.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/nda.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/ndax.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/utils.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/libs/local_fastnda/version.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/log.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/logging.json +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/.cellpy_prms_default.conf +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/cell_schema.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/internal_settings.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/legacy/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/legacy/update_headers.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/prmreader.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/parameters/prms.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/backends/base.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/batch_summary.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/context.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/cycle_legend.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/figures.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/headers.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/labels.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/curves.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/ica.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/raw.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/steps.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/prepare/summary.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/registry.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/spec.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/plotting/theme.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/capacity_curves.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/dtype.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/fids.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/format.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/keys.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/legacy_read.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/meta.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/meta_archive.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/read.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/selectors.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/cellpy_file/translate.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/data_structures.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/dbreader.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/do.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/filefinder.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/SQL Table IDs.txt +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/_aux_map.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_res.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_7.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_config.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_csv.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_h5.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/arbin_sql_xlsx.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/base.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/batmo_bdf.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/biologics_mpr.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/batmo_bdf_bdf.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_one.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_three.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_two.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/maccor_txt_zero.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_one.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_two.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/configurations/neware_txt_zero.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/contract.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/custom.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/declarations.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/ext_nda_reader.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/harmonize.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/hooks.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/loader_specific_modules/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/loader_specific_modules/biologic_file_format.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/local_instrument.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/maccor_txt.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/maccor_txt_native.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_nda.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_txt.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/neware_xlsx.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/pec_csv.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/post_processors.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/processors/pre_processors.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/registry.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/instruments/testing.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/journal_layer.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/json_dbreader.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/merger.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/meta_resolver.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/native_core.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/provenance.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/slicing.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/sql_dbreader.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/readers/test_meta.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/units.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/__init__.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/batch.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/collectors.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/diagnostics.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/example_data.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/helpers.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/ica.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/live.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/ocv_rlx.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/plotutils.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/processor.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/cellpy/utils/template_registry.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/backfill_notebook_plotly_pngs.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/README.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/cellpy/meta.yaml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/conda-recipes/cellpycore/meta.yaml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/make_bad_fixtures.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/plot_preview_common.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_curve_plots.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_ica_plots.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_plots.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/preview_summary_plots.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/regenerate_goldens.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/render_example_notebooks.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/snapshot_cli_surface.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/dev/snapshot_figure_specs.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/Dockerfile.build-test +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/README.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/compose.yml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/data/hello.txt +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/docker/sftp-test/data/nested/sample.txt +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/environment.yml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/environment_dev.yml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/github_actions_environment.yml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/noxfile.py +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/.github/workflows/draft-pdf.yml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/.gitignore +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/Cellpy-Utils.jpg +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/CellpyCell.jpg +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/Figures/CellpyData.jpg +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/paper.bib +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/paper/paper.md +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/pyproject.toml +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/scripts/build_test.sh +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/scripts/dev_sync.sh +0 -0
- {cellpy-2.1.2a2 → cellpy-2.1.2a4}/zensical.toml +0 -0
|
@@ -24,6 +24,10 @@ tmp/
|
|
|
24
24
|
#testdata/hdf5/20160306_snx001_10_cc_01.h5
|
|
25
25
|
# testdata/batchfiles/cellpy_batch_test.json
|
|
26
26
|
|
|
27
|
+
# Stray local leftovers (not essential-suite tmp_path output; do not commit) (#855)
|
|
28
|
+
/cellpy_batch_test_batch.json
|
|
29
|
+
/testdata/hdf5/20160805_test001_45_cc.cellpy
|
|
30
|
+
|
|
27
31
|
dev_data/
|
|
28
32
|
dev_data/tmp
|
|
29
33
|
dev_data/bugfixing
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Cycle status
|
|
2
|
+
|
|
3
|
+
- queue: yolo 2.1.2 (resolved `label:yolo`, milestone `v.2.1.2`)
|
|
4
|
+
- onfail: stop
|
|
5
|
+
- started: 2026-08-09T12:17:00+02:00
|
|
6
|
+
- finished: 2026-08-09
|
|
7
|
+
- repo: jepegit/cellpy
|
|
8
|
+
- stash: still on stash list as `iflow-cycle: untracked before yolo 2.1.2 batch` — restore with `git stash pop` if needed
|
|
9
|
+
|
|
10
|
+
## Queue
|
|
11
|
+
|
|
12
|
+
- [x] #846 — No selective summary rebuild after metadata edits — merged https://github.com/jepegit/cellpy/pull/856
|
|
13
|
+
- [x] #849 — config: model_dump_for_file() plaintext Arbin SQL credentials — merged https://github.com/jepegit/cellpy/pull/857
|
|
14
|
+
- [x] #850 — config: override() thread-safety — merged https://github.com/jepegit/cellpy/pull/858
|
|
15
|
+
- [x] #853 — cellpy info --configloc project cellpy.toml — merged https://github.com/jepegit/cellpy/pull/859
|
|
16
|
+
- [x] #855 — add test generated files to gitignore — merged https://github.com/jepegit/cellpy/pull/860
|
|
17
|
+
|
|
18
|
+
- [x] Done
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Issue #845: Non-atomic v9 .cellpy writes can corrupt (and destroy) the file on interrupt
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/845
|
|
4
|
+
|
|
5
|
+
## Original issue text
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
The v9 writer saves straight onto the destination path:
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
# cellpy/readers/cellpy_file/v9.py
|
|
12
|
+
with zipfile.ZipFile(path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
13
|
+
... # meta.json -> raw.parquet -> steps / summary / fid
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Mode `"w"` truncates `path` **immediately**, then members are appended in
|
|
17
|
+
order. An interrupt / kill / exception mid-write (common when parquet-serialising
|
|
18
|
+
large raw tables) leaves a zip that still opens but is missing members:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
cellpy.exceptions.CorruptCellpyFile: missing zip member 'raw.parquet'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Because the write is in place, this also **destroys the previously-good file** —
|
|
25
|
+
a re-save that fails halfway loses the old data too.
|
|
26
|
+
|
|
27
|
+
## How it surfaced
|
|
28
|
+
Building cellpy-simple-gui, a project save (many `cell.save(..., overwrite=True)`)
|
|
29
|
+
was interrupted. One `.cellpy` contained only `meta.json` (~1 KB); later cells
|
|
30
|
+
were never written and the partially-written one no longer loaded. The app now
|
|
31
|
+
stages the *project folder* atomically, but each individual `cell.save()` can
|
|
32
|
+
still truncate a good `.cellpy` in place — the app can't protect a single file.
|
|
33
|
+
|
|
34
|
+
## Suggested fix
|
|
35
|
+
Write to a same-directory temp file, then atomically replace:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
39
|
+
with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
40
|
+
...
|
|
41
|
+
# optional: verify required members are present in tmp
|
|
42
|
+
os.replace(tmp, path) # atomic on the same filesystem
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
so a reader never sees a half-written archive and a failed save leaves the
|
|
46
|
+
previous file intact. `v8`/older writers, if they share this pattern, would
|
|
47
|
+
benefit too.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
*Found while building [cellpy-simple-gui](https://github.com/cellpy/cellpy-simple-gui) on cellpy 2.1.1.post7. Context: [CELLPY_PAINPOINTS.md §19](https://github.com/cellpy/cellpy-simple-gui/blob/main/CELLPY_PAINPOINTS.md).*
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Issue #845 — plan: atomic `.cellpy` / `.h5` writes
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/845
|
|
4
|
+
Milestone: v.2.1.2 · Labels: bug, v2
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
|
|
8
|
+
A failed or interrupted `CellpyCell.save()` must never leave a half-written
|
|
9
|
+
cellpy-file, and must never destroy the previously-good file. Writers stage into
|
|
10
|
+
a same-directory temp file and `os.replace()` it into place only after the
|
|
11
|
+
archive is complete.
|
|
12
|
+
|
|
13
|
+
## Constraints
|
|
14
|
+
|
|
15
|
+
- **Two destructive spots, not one.** Besides the non-atomic zip write in
|
|
16
|
+
`v9.save`, `CellpyCell.save` already `os.remove(...)`s the destination
|
|
17
|
+
*before* calling the writer
|
|
18
|
+
([`cellreader.py:1663-1676`](../../cellpy/readers/cellreader.py)). Fixing only
|
|
19
|
+
the writer still loses the old file on failure — the pre-delete must go.
|
|
20
|
+
- **Dropping the pre-delete forces the v8/HDF5 path in too.** `write.save` opens
|
|
21
|
+
`pandas.HDFStore(path)` in default mode `"a"`; without the pre-delete it would
|
|
22
|
+
*append into* an existing file instead of replacing it. So v8 must become
|
|
23
|
+
atomic in the same change — not scope creep, a correctness consequence.
|
|
24
|
+
- Keep `overwrite=False` semantics: existing file → refuse and return (no write).
|
|
25
|
+
- Keep the legacy locked-file UX: a `PermissionError` on a locked destination
|
|
26
|
+
logs critical and returns rather than raising out of `save()`.
|
|
27
|
+
- Remote save is already rejected upstream (`outfile_all.is_external` →
|
|
28
|
+
`ValueError`), so writers only ever see local paths. `OtherPath` implements
|
|
29
|
+
`__fspath__`, so `Path(outfile_all)` is safe.
|
|
30
|
+
- Same filesystem required for atomicity — temp file goes in the **destination's
|
|
31
|
+
own directory**, never `TMPDIR`.
|
|
32
|
+
- Cross-platform: `os.replace` is atomic on POSIX and overwrites on Windows, but
|
|
33
|
+
raises `PermissionError` if the destination is open in another process.
|
|
34
|
+
|
|
35
|
+
### Prior art
|
|
36
|
+
|
|
37
|
+
- `.issueflows/00-tools/` (README index) — nothing about file IO; no reuse.
|
|
38
|
+
- Grep `os.replace` / `atomic` / `.tmp` across `cellpy/` — **no existing atomic
|
|
39
|
+
write helper**; this is the first one.
|
|
40
|
+
- `cellpy/readers/cellpy_file/v9.py` `save` — the writer to wrap.
|
|
41
|
+
- `cellpy/readers/cellpy_file/write.py` `save` — v8/HDF5 writer, same pattern.
|
|
42
|
+
- `cellpy/internals/otherpath.py` `OtherPath` — provides `__fspath__`,
|
|
43
|
+
`with_name`, `is_file`; no staging/replace logic to mirror.
|
|
44
|
+
- graphify: `graphify-out/` present but not consulted for this narrow IO fix.
|
|
45
|
+
|
|
46
|
+
## Approach
|
|
47
|
+
|
|
48
|
+
1. **New helper** `cellpy/readers/cellpy_file/atomic.py` — one context manager:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
@contextlib.contextmanager
|
|
52
|
+
def atomic_write(path, *, verify=None):
|
|
53
|
+
"""Yield a same-dir temp path; os.replace() it onto `path` on success."""
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- temp name `f"{path.name}.tmp{os.getpid()}"` in `path.parent`, removed first
|
|
57
|
+
if a stale one exists (so the writer, not the helper, creates the file —
|
|
58
|
+
`HDFStore` and `ZipFile` both want to create it themselves);
|
|
59
|
+
- `path.parent.mkdir(parents=True, exist_ok=True)` before yielding;
|
|
60
|
+
- on exception inside the block: unlink the temp, re-raise (destination
|
|
61
|
+
untouched);
|
|
62
|
+
- on success: run `verify(tmp)` if given, then `os.replace(tmp, path)`;
|
|
63
|
+
- if `os.replace` itself fails (locked destination on Windows): **keep** the
|
|
64
|
+
temp file, log critical with its path so the data is recoverable, re-raise.
|
|
65
|
+
|
|
66
|
+
2. **`v9.save`** — wrap the `zipfile.ZipFile(...)` block in `atomic_write`, and
|
|
67
|
+
pass a `verify` that reopens the temp zip and asserts the required members
|
|
68
|
+
(`meta.json`, `raw/steps/summary.parquet`, plus `fid.parquet` when written)
|
|
69
|
+
are in `namelist()`. Cheap (central-directory read only, no `testzip()`
|
|
70
|
+
decompression of the whole raw table).
|
|
71
|
+
|
|
72
|
+
3. **`write.save` (v8/HDF5)** — same wrap; `HDFStore(tmp, mode="w", …)`. No
|
|
73
|
+
member verification (would mean reopening the store).
|
|
74
|
+
|
|
75
|
+
4. **`CellpyCell.save`** — the `outfile_all.is_file()` branch keeps the
|
|
76
|
+
`overwrite=False` refusal but **stops deleting** the file when
|
|
77
|
+
`overwrite=True`; the writers now replace it. Wrap the two writer calls so a
|
|
78
|
+
`PermissionError` still logs critical + returns instead of propagating.
|
|
79
|
+
|
|
80
|
+
## Files to touch
|
|
81
|
+
|
|
82
|
+
| Path | Change |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `cellpy/readers/cellpy_file/atomic.py` | **new** — `atomic_write` context manager (~40 lines incl. docstring) |
|
|
85
|
+
| `cellpy/readers/cellpy_file/v9.py` | `save` writes through `atomic_write` + member verification |
|
|
86
|
+
| `cellpy/readers/cellpy_file/write.py` | `save` writes through `atomic_write`, `HDFStore(tmp, mode="w")` |
|
|
87
|
+
| `cellpy/readers/cellreader.py` | drop pre-write `os.remove`; keep overwrite refusal; `PermissionError` guard around writer calls |
|
|
88
|
+
| `tests/test_cellpy_file_v9.py` | new interrupt/atomicity tests (`@pytest.mark.essential`) |
|
|
89
|
+
| `HISTORY.md` | changelog entry at `/iflow-close` |
|
|
90
|
+
|
|
91
|
+
## Test strategy
|
|
92
|
+
|
|
93
|
+
Command: `conda activate cellpy_dev_313 && pytest` (project rule); merge gate is
|
|
94
|
+
`pytest -m essential`. Targeted runs:
|
|
95
|
+
`pytest tests/test_cellpy_file_v9.py tests/test_cellpy_file_roundtrip.py -m ""`.
|
|
96
|
+
|
|
97
|
+
New tests (fault injection by monkeypatching
|
|
98
|
+
`v9._frame_to_parquet_bytes` to raise on the summary frame, i.e. after
|
|
99
|
+
`meta.json` + `raw.parquet` are already inside the temp zip):
|
|
100
|
+
|
|
101
|
+
1. **Old file survives a failed re-save** — save a good `.cellpy`, snapshot it,
|
|
102
|
+
make the second `save(..., overwrite=True)` blow up, then assert the file
|
|
103
|
+
still loads with identical frames.
|
|
104
|
+
2. **No debris** — after that failure, no `*.tmp*` left in the directory.
|
|
105
|
+
3. **Failed first save leaves no file** — a fresh destination that fails
|
|
106
|
+
mid-write must not exist afterwards (no more "opens but missing
|
|
107
|
+
`raw.parquet`" `CorruptCellpyFile`).
|
|
108
|
+
4. **Member verification** — an incomplete-but-successful zip (patch the writer
|
|
109
|
+
to skip `summary.parquet`) is rejected before `os.replace`, leaving the old
|
|
110
|
+
file intact.
|
|
111
|
+
5. **HDF5 path** — same "old file survives" check for
|
|
112
|
+
`cellpy_file_format="hdf5"`, skipped when HDF5 support is unavailable.
|
|
113
|
+
|
|
114
|
+
Marker: `essential` for 1–3 (data-loss guard, cheap); 4–5 plain.
|
|
115
|
+
|
|
116
|
+
## Open questions
|
|
117
|
+
|
|
118
|
+
1. **Keep or discard the staged temp file when `os.replace` fails?** Plan keeps
|
|
119
|
+
it + logs critical (recoverable data, small debris risk on a locked file).
|
|
120
|
+
Alternative: always unlink for a spotless directory.
|
|
121
|
+
2. **v8/HDF5 in this PR?** Plan says yes — required once the pre-delete is
|
|
122
|
+
dropped. Splitting it out would mean keeping a v8-only pre-delete branch.
|
|
123
|
+
3. **Other writers out of scope** (`to_csv`, batch journal JSON, exporters) —
|
|
124
|
+
confirm they stay for a follow-up issue.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Issue #845 — status
|
|
2
|
+
|
|
3
|
+
Branch: `845-atomic-cellpy-writes`
|
|
4
|
+
|
|
5
|
+
- [x] Done
|
|
6
|
+
|
|
7
|
+
## What's done
|
|
8
|
+
|
|
9
|
+
- **`cellpy/readers/cellpy_file/atomic.py`** (new) — `atomic_write(path,
|
|
10
|
+
verify=...)` context manager: stages `<name>.tmp<pid>` in the destination's own
|
|
11
|
+
directory (same filesystem, so `os.replace` stays atomic), unlinks the staged
|
|
12
|
+
file if the write raises, and runs an optional `verify` before replacing.
|
|
13
|
+
Per the confirmed decision, a failing `os.replace` (locked destination on
|
|
14
|
+
Windows) **keeps** the staged file and logs critical with its path.
|
|
15
|
+
- **`v9.save`** — writes the zip through `atomic_write` and verifies the staged
|
|
16
|
+
archive holds every required member (`meta.json`, raw/steps/summary parquet,
|
|
17
|
+
plus `fid.parquet` when written) from the central directory only, so an
|
|
18
|
+
incomplete-but-openable zip is rejected before it can replace a good file.
|
|
19
|
+
- **`write.save` (v8/HDF5)** — same staging, `HDFStore(staged, mode="w")`.
|
|
20
|
+
- **`CellpyCell.save`** — dropped the pre-write `os.remove` that destroyed the
|
|
21
|
+
old file before the writer even started; kept the `overwrite=False` refusal and
|
|
22
|
+
the locked-file "log critical + return" UX (now via `PermissionError` around
|
|
23
|
+
the writer calls). Docstring documents the atomicity guarantee.
|
|
24
|
+
- **Docs** — `docs/getting_started/agents.md` notes that `save` is atomic, so
|
|
25
|
+
app builders (this came from cellpy-simple-gui) don't need per-file staging.
|
|
26
|
+
- **Tests** (`tests/test_cellpy_file_v9.py`, 4 new): failed re-save keeps the old
|
|
27
|
+
file bit-for-bit + reloads; failed first save leaves no file; incomplete
|
|
28
|
+
archive rejected before replace; HDF5 re-save keeps the old file. First three
|
|
29
|
+
marked `essential`. Verified they **fail on the pre-fix code** and pass after.
|
|
30
|
+
|
|
31
|
+
## Test results
|
|
32
|
+
|
|
33
|
+
- `uv run pytest tests/test_cellpy_file_v9.py -m ""` → 7 passed.
|
|
34
|
+
- `uv run pytest -m essential` (merge gate) → 679 passed, 1 skipped.
|
|
35
|
+
- Full default suite → 1525 passed, 3 failed: `test_search_for_files`,
|
|
36
|
+
`test_search_for_files_with_dirs`, `test_search_for_files_recursive` —
|
|
37
|
+
**pre-existing**, reproduced on the stashed baseline. Same for the three
|
|
38
|
+
`slowtest` `cellpy new` cookiecutter failures seen with `-m ""`.
|
|
39
|
+
- Note: the conda env `cellpy_dev_313` has a broken `pyarrow` DLL
|
|
40
|
+
(`ImportError: DLL load failed while importing lib`), so tests were run through
|
|
41
|
+
`uv run` / `.venv` instead. Unrelated to this issue, but worth repairing.
|
|
42
|
+
- `flake8`/`black` findings on the touched files are the repo's pre-existing
|
|
43
|
+
E501-at-79 and F401 noise; the new module is black-clean.
|
|
44
|
+
|
|
45
|
+
## Remaining work
|
|
46
|
+
|
|
47
|
+
- `HISTORY.md` changelog bullet — owned by `/iflow-close`.
|
|
48
|
+
- Out of scope (candidates for a follow-up issue): other writers that still
|
|
49
|
+
write in place (`to_csv`, batch journal JSON, exporters).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Issue #846: No selective summary rebuild after metadata edits (mass/area/nom-cap/cycle mode)
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/846
|
|
4
|
+
|
|
5
|
+
## Original issue text
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
Editing physical metadata after load — mass, active electrode area, nominal
|
|
9
|
+
capacity, cycle mode — requires a **full** `cell.make_summary()`. There's no
|
|
10
|
+
public API to rebuild only the dependent summary columns (e.g. gravimetric
|
|
11
|
+
capacities after a mass change, C-rates after a nominal-capacity change).
|
|
12
|
+
|
|
13
|
+
There's also no documented **meta → summary-column dependency map**, so an app
|
|
14
|
+
can't do targeted updates or tell the user precisely what a change will affect.
|
|
15
|
+
|
|
16
|
+
## How it surfaced
|
|
17
|
+
cellpy-simple-gui's "Manage Cells" lets users change these knobs post-load. The
|
|
18
|
+
app assigns `cell.mass` / `cell.active_electrode_area` / `cell.nominal_capacity`
|
|
19
|
+
/ `cell.cycle_mode` and then always calls the full `make_summary()`. Correct,
|
|
20
|
+
but opaque and potentially expensive for large cells, and easy to get subtly
|
|
21
|
+
wrong (which attribute invalidates which column?).
|
|
22
|
+
|
|
23
|
+
## Suggested fix (either is useful)
|
|
24
|
+
1. Cheap selective-refresh helpers keyed by meta field, e.g.
|
|
25
|
+
`cell.refresh_after(("mass",))` that recomputes just the affected columns; or
|
|
26
|
+
2. A small documented dependency map / note ("`nominal_capacity` affects
|
|
27
|
+
`charge_c_rate`, `discharge_c_rate`, normalized cycle index; `mass` affects
|
|
28
|
+
`*_gravimetric`; `area` affects `*_areal`; …") so GUIs can scope rebuilds
|
|
29
|
+
and messaging.
|
|
30
|
+
|
|
31
|
+
Dedicated setters (vs bare attribute assignment) would also help
|
|
32
|
+
discoverability of "what do I call after changing mass?".
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
*Found while building [cellpy-simple-gui](https://github.com/cellpy/cellpy-simple-gui) on cellpy 2.1.1.post7. Context: [CELLPY_PAINPOINTS.md §19](https://github.com/cellpy/cellpy-simple-gui/blob/main/CELLPY_PAINPOINTS.md).*
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Plan: #846 selective summary rebuild after metadata edits
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Give apps (e.g. cellpy-simple-gui) a public meta→summary dependency map and a
|
|
6
|
+
`refresh_after(...)` helper so post-load mass/area/nom-cap/cycle-mode edits
|
|
7
|
+
do not require an opaque full `make_summary()` when a summary already exists.
|
|
8
|
+
|
|
9
|
+
## Constraints
|
|
10
|
+
|
|
11
|
+
- Yolo-sized: no new selective column engine; reuse existing
|
|
12
|
+
`core.add_scaled_summary_columns` seam already used by `_make_summary`.
|
|
13
|
+
- Document **current** behaviour accurately: C-rate columns come from the step
|
|
14
|
+
table and are **not** derived from `nominal_capacity` (core comment).
|
|
15
|
+
- Keep setters as assignment; point them at `refresh_after` in docstrings.
|
|
16
|
+
- Do not change default `make_summary` behaviour.
|
|
17
|
+
|
|
18
|
+
### Prior art
|
|
19
|
+
|
|
20
|
+
- `_make_summary` → `core.make_core_summary` + `core.add_scaled_summary_columns`
|
|
21
|
+
(`cellpy/readers/cellreader.py`) — scaled path is already the meta-dependent half.
|
|
22
|
+
- `cellpycore.summarizers.generate_specific_summary_columns` /
|
|
23
|
+
`equivalent_cycles_to_summary` — overwrite via `with_columns` (safe re-run).
|
|
24
|
+
- Toolbox: none relevant.
|
|
25
|
+
|
|
26
|
+
## Approach
|
|
27
|
+
|
|
28
|
+
1. Add module-level `SUMMARY_META_DEPENDENCIES` (and alias map) describing what
|
|
29
|
+
each meta field invalidates / affects.
|
|
30
|
+
2. Add `CellpyCell.refresh_after(fields, **kwargs)`:
|
|
31
|
+
- normalize field names (`mass`/`active_mass`, `area`/`active_electrode_area`,
|
|
32
|
+
`nom_cap`/`nominal_capacity`, `cycle_mode`);
|
|
33
|
+
- if no summary → `make_summary(**kwargs)`;
|
|
34
|
+
- else recompute `nom_cap_abs` + specific conversion factors and call
|
|
35
|
+
`core.add_scaled_summary_columns` in place (no `make_core_summary`).
|
|
36
|
+
3. Docstring notes on mass / area / nom_cap / cycle_mode setters.
|
|
37
|
+
4. One paragraph in `docs/getting_started/agents.md`.
|
|
38
|
+
5. Unit tests for map keys + mass change updates gravimetric columns without
|
|
39
|
+
requiring a full rebuild path assertion.
|
|
40
|
+
|
|
41
|
+
## Files to touch
|
|
42
|
+
|
|
43
|
+
- `cellpy/readers/cellreader.py` — map + `refresh_after` + setter docs
|
|
44
|
+
- `docs/getting_started/agents.md` — short usage note
|
|
45
|
+
- `tests/test_refresh_after_meta.py` — new focused tests
|
|
46
|
+
- `.issueflows/01-current-issues/issue846_{plan,status}.md`
|
|
47
|
+
|
|
48
|
+
## Test strategy
|
|
49
|
+
|
|
50
|
+
`uv run pytest tests/test_refresh_after_meta.py -q` then `uv run pytest -m essential -q`.
|
|
51
|
+
|
|
52
|
+
## Open questions
|
|
53
|
+
|
|
54
|
+
None — batch cycle confirm covers Accept.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Status: #846
|
|
2
|
+
|
|
3
|
+
- [x] Done
|
|
4
|
+
|
|
5
|
+
## What's done
|
|
6
|
+
|
|
7
|
+
- `SUMMARY_META_DEPENDENCIES` + `normalize_summary_meta_fields`
|
|
8
|
+
- `CellpyCell.refresh_after` / `_refresh_scaled_summary_columns`
|
|
9
|
+
- Setter docs; agents.md note; tests (1 essential); HISTORY bullet
|
|
10
|
+
|
|
11
|
+
## Remaining work
|
|
12
|
+
|
|
13
|
+
None.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Issue #849: config: model_dump_for_file() writes legacy Arbin SQL credentials in plaintext
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/849
|
|
4
|
+
|
|
5
|
+
## Original issue text
|
|
6
|
+
|
|
7
|
+
**cellpy version:** 2.1.2a2
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
`CellpyConfig.model_dump_for_file()` is documented as *"Dump config suitable for TOML persistence (secrets excluded)"*, and it does drop the `[secrets]` section. But `ArbinConfig` (and the other instrument models) are `model_config = ConfigDict(extra="allow")`, so the **legacy** Arbin SQL credentials — `SQL_PWD`, `SQL_UID` — pass straight through and get written to `cellpy.toml` in plaintext.
|
|
12
|
+
|
|
13
|
+
The asymmetry is the dangerous part: a hand-written `[secrets]` block is correctly rejected on load, but the same credential smuggled in under `[instruments.Arbin]` is written **and** silently accepted on reload.
|
|
14
|
+
|
|
15
|
+
## Reproduction
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import tempfile
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from cellpy.config.models import CellpyConfig
|
|
21
|
+
from cellpy.config.loader import write_toml, load_config, LoadOptions
|
|
22
|
+
|
|
23
|
+
tmp = Path(tempfile.mkdtemp())
|
|
24
|
+
toml_path = tmp / "cellpy.toml"
|
|
25
|
+
|
|
26
|
+
cfg = CellpyConfig.model_validate(
|
|
27
|
+
{"instruments": {"Arbin": {"SQL_PWD": "hunter2", "SQL_UID": "jepe"}}}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
write_toml(toml_path, cfg.model_dump_for_file()) # the "secrets excluded" dump
|
|
31
|
+
print("hunter2" in toml_path.read_text()) # -> True
|
|
32
|
+
|
|
33
|
+
res = load_config(None, LoadOptions(user_config_file=toml_path, skip_env=True))
|
|
34
|
+
print(res.config.instruments.Arbin.SQL_PWD) # -> hunter2 (no error)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Output:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
written TOML contains plaintext password: True
|
|
41
|
+
['SQL_PWD = "hunter2"', 'SQL_UID = "jepe"']
|
|
42
|
+
reload accepted the file (no ConfigurationError)
|
|
43
|
+
value round-tripped: hunter2
|
|
44
|
+
[secrets] correctly rejected: ConfigurationError
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Why this bites app developers
|
|
48
|
+
|
|
49
|
+
This is on the realistic migration path, not a contrived one:
|
|
50
|
+
|
|
51
|
+
1. A user has a legacy `.cellpy_prms_*.conf` with a real `Instruments.Arbin.SQL_PWD`.
|
|
52
|
+
2. The legacy loader merges it (`_drop_legacy_secrets` only pops the top-level `secrets` key, so `instruments.Arbin.SQL_PWD` survives).
|
|
53
|
+
3. Any app offering a **Settings → Save** button calls the documented secrets-safe dump…
|
|
54
|
+
4. …and writes the user's database password to `%LOCALAPPDATA%\cellpy\cellpy\cellpy.toml` — a file they'd reasonably share, sync, or commit alongside a project.
|
|
55
|
+
|
|
56
|
+
We hit this while designing a settings UI for [cellpy-simple-gui](https://github.com/cellpy/cellpy-simple-gui); we now have to scrub `instruments.*.SQL_*` ourselves before writing, which is exactly the kind of thing the `model_dump_for_file()` contract should be handling.
|
|
57
|
+
|
|
58
|
+
## Suggested fixes (any one would do)
|
|
59
|
+
|
|
60
|
+
- **Map on legacy load** — translate `Instruments.Arbin.SQL_PWD`/`SQL_UID`/`SQL_server` into the `secrets` section during YAML migration, so they land in the one place that already knows they're credentials.
|
|
61
|
+
- **Scrub on dump** — strip known credential-ish keys (`*PWD*`, `*UID*`, `*password*`) from instrument sections in `model_dump_for_file()`.
|
|
62
|
+
- **Type them** — declare `SQL_PWD` on `ArbinConfig` as `SecretStr | None` with `exclude=True` rather than letting it ride in on `extra="allow"`.
|
|
63
|
+
- **Symmetric guard** — have `_reject_secrets_from_file` (or a warning) also catch credential keys in instrument sections, so a file that does contain one is not silently honoured.
|
|
64
|
+
|
|
65
|
+
Happy to send a PR if you have a preference on which route.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Plan: #849 scrub instrument credentials from file dump
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Honour `model_dump_for_file()` contract: never write legacy Arbin `SQL_PWD` /
|
|
6
|
+
`SQL_UID` (or other credential-ish instrument keys) to `cellpy.toml`, and stop
|
|
7
|
+
silently accepting them on TOML load.
|
|
8
|
+
|
|
9
|
+
## Constraints
|
|
10
|
+
|
|
11
|
+
- Env-only secrets stay the rule (existing `[secrets]` rejection).
|
|
12
|
+
- Legacy YAML migration: drop instrument credential keys with a warning (do
|
|
13
|
+
not strand users), same spirit as `_drop_legacy_secrets`.
|
|
14
|
+
- KISS: scrub/reject known keys; do not redesign `ArbinConfig` as SecretStr in
|
|
15
|
+
this issue.
|
|
16
|
+
|
|
17
|
+
### Prior art
|
|
18
|
+
|
|
19
|
+
- `CellpyConfig.model_dump_for_file` — pops `[secrets]` only today.
|
|
20
|
+
- `_reject_secrets_from_file` / `_drop_legacy_secrets` in `loader.py`.
|
|
21
|
+
- `tests/test_config_secrets.py` — essential suite for this contract.
|
|
22
|
+
|
|
23
|
+
## Approach
|
|
24
|
+
|
|
25
|
+
1. Shared helper listing credential-ish instrument keys (`SQL_PWD`, `SQL_UID`,
|
|
26
|
+
`*PASSWORD*`, `*_PWD` / `PWD` suffixes).
|
|
27
|
+
2. `model_dump_for_file`: deep-scrub instruments before return.
|
|
28
|
+
3. TOML load: reject those keys with `ConfigurationError` (symmetric to
|
|
29
|
+
`[secrets]`).
|
|
30
|
+
4. Legacy YAML: pop + warn.
|
|
31
|
+
5. Extend `test_config_secrets.py` with the issue reproduction.
|
|
32
|
+
|
|
33
|
+
## Files to touch
|
|
34
|
+
|
|
35
|
+
- `cellpy/config/models.py` — scrub in dump
|
|
36
|
+
- `cellpy/config/loader.py` — reject / drop on load
|
|
37
|
+
- `tests/test_config_secrets.py` — new essential cases
|
|
38
|
+
- issue tracking + HISTORY
|
|
39
|
+
|
|
40
|
+
## Test strategy
|
|
41
|
+
|
|
42
|
+
`uv run pytest tests/test_config_secrets.py -q` then `uv run pytest -m essential -q`.
|
|
43
|
+
|
|
44
|
+
## Open questions
|
|
45
|
+
|
|
46
|
+
None.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Issue #850: config: override() is process-global and not thread-safe (cross-talk in threaded apps)
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/850
|
|
4
|
+
|
|
5
|
+
## Original issue text
|
|
6
|
+
|
|
7
|
+
**cellpy version:** 2.1.2a2
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
`cellpy.config.override()` reads as a scoped, reentrant context manager, but it mutates a module-level `_override_stack` and swaps the global `_session` via `reload()`. In a threaded application, one thread's "scoped" override is visible to every other thread, and the `finally: pop + reload` can land while another thread is still inside its own block.
|
|
12
|
+
|
|
13
|
+
## Reproduction
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import time
|
|
17
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
18
|
+
from cellpy import config
|
|
19
|
+
|
|
20
|
+
def worker(mode):
|
|
21
|
+
with config.override(reader={"cycle_mode": mode}):
|
|
22
|
+
time.sleep(0.05) # let the two blocks interleave
|
|
23
|
+
return mode, config.get_config().reader.cycle_mode
|
|
24
|
+
|
|
25
|
+
with ThreadPoolExecutor(max_workers=2) as ex:
|
|
26
|
+
print(list(ex.map(worker, ["anode", "cathode"])))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Result:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
[('anode', 'anode'), ('cathode', 'anode')]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `cathode` worker observes `anode` **inside its own `override()` block**. Each worker should see the value it asked for.
|
|
36
|
+
|
|
37
|
+
## Why this bites app developers
|
|
38
|
+
|
|
39
|
+
Any GUI/service that runs cellpy work off the request thread hits this. In [cellpy-simple-gui](https://github.com/cellpy/cellpy-simple-gui) loads/exports run in a `ThreadPoolExecutor`, so the natural pattern — "override `reader.cycle_mode` / `units` for *this* job" — is quietly racy: two concurrent jobs can silently compute with each other's settings, and the result is wrong numbers rather than an exception. That failure mode is much worse than a crash, because nothing signals it.
|
|
40
|
+
|
|
41
|
+
Our workaround is to resolve config on the main thread and pass concrete values into the job, and to treat `override()` as main-thread-only. That works, but it means the config stack can't be used for per-job policy at all.
|
|
42
|
+
|
|
43
|
+
## Suggested fix
|
|
44
|
+
|
|
45
|
+
Back the override stack (and ideally the session) with a **`contextvars.ContextVar`** rather than a module global:
|
|
46
|
+
|
|
47
|
+
- `ContextVar` isolation is per-thread *and* per-async-task, so it fixes threaded apps and any future async usage in one move.
|
|
48
|
+
- `copy_context()` semantics also make the intent ("scoped to this unit of work") actually true.
|
|
49
|
+
- Worth pairing with a note in the configuration docs about which parts of the stack are process-global (`reload`, `set_load_options`) versus scoped.
|
|
50
|
+
|
|
51
|
+
If a full `ContextVar` migration is too invasive for a patch release, an interim step would be documenting `override()` as process-global/not thread-safe and adding a `threading.RLock` around the stack mutation + reload, so at least the stack cannot be corrupted.
|
|
52
|
+
|
|
53
|
+
Happy to send a PR if you'd like the `ContextVar` version.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Plan: #850 thread-safe config.override via ContextVar
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Make `config.override()` scoped per thread / async task so concurrent jobs
|
|
6
|
+
cannot observe each other's overrides.
|
|
7
|
+
|
|
8
|
+
## Constraints
|
|
9
|
+
|
|
10
|
+
- Keep nested LIFO stacking and existing tests in `tests/test_config.py`.
|
|
11
|
+
- `reload` / `set_load_options` stay process-global (document in docstring).
|
|
12
|
+
- Yolo-sized: ContextVar for override stack + active config; do not
|
|
13
|
+
ContextVar-migrate the whole session.
|
|
14
|
+
|
|
15
|
+
### Prior art
|
|
16
|
+
|
|
17
|
+
- `cellpy/config/session.py` — `_override_stack` + `reload()` swap global `_session`.
|
|
18
|
+
- `tests/test_config.py` — nested override + essential fixture.
|
|
19
|
+
|
|
20
|
+
## Approach
|
|
21
|
+
|
|
22
|
+
1. Replace module `_override_stack` with `ContextVar` tuple stack +
|
|
23
|
+
`ContextVar` for the active overridden `CellpyConfig`.
|
|
24
|
+
2. `get_config()` returns context config when set, else session config.
|
|
25
|
+
3. `override()` pushes stack, builds config via deep-merge on
|
|
26
|
+
`session.config.model_dump()` + `CellpyConfig.model_validate`, yields it;
|
|
27
|
+
no global `_session` swap.
|
|
28
|
+
4. `reload()` rebuilds global session **without** applying the override stack;
|
|
29
|
+
if currently inside an override, refresh the context-local config.
|
|
30
|
+
5. Threaded regression test matching the issue reproduction.
|
|
31
|
+
|
|
32
|
+
## Files to touch
|
|
33
|
+
|
|
34
|
+
- `cellpy/config/session.py`
|
|
35
|
+
- `tests/test_config.py`
|
|
36
|
+
- HISTORY + issue tracking
|
|
37
|
+
|
|
38
|
+
## Test strategy
|
|
39
|
+
|
|
40
|
+
`uv run pytest tests/test_config.py -q` then `uv run pytest -m essential -q`.
|
|
41
|
+
|
|
42
|
+
## Open questions
|
|
43
|
+
|
|
44
|
+
None.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Issue #851: cellpy info / edit config / info --check still point at the legacy .conf after migration
|
|
2
|
+
|
|
3
|
+
Source: https://github.com/jepegit/cellpy/issues/851
|
|
4
|
+
|
|
5
|
+
## Original issue text
|
|
6
|
+
|
|
7
|
+
## Problem / context
|
|
8
|
+
|
|
9
|
+
After `cellpy setup migrate` writes `cellpy.toml`, the config *loader* correctly prefers it — `load_config` only falls back to the legacy YAML when no TOML was loaded (`cellpy/config/loader.py:203-213`). But the CLI still reports and edits the old file:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
❯ cellpy setup migrate
|
|
13
|
+
[cellpy] (setup migrate) source: C:\Users\jepe\.cellpy_prms_jepe.conf
|
|
14
|
+
[cellpy] (setup migrate) target: C:\Users\jepe\AppData\Local\cellpy\cellpy\cellpy.toml
|
|
15
|
+
[cellpy] (setup migrate) done - the old file is kept untouched.
|
|
16
|
+
|
|
17
|
+
❯ cellpy info
|
|
18
|
+
[cellpy] version: 2.1.1.post8.dev1+fad801ac
|
|
19
|
+
[cellpy] -> C:\Users\jepe\.cellpy_prms_jepe.conf # ← stale, nothing reads this
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Root cause: `_configloc()` (`cellpy/cli_api.py:1276`) calls `prmreader.get_user_dir_and_dst()`, which composes `~/.cellpy_prms_<user>.conf` unconditionally and never consults `cellpy.config.loader.user_config_path()`.
|
|
23
|
+
|
|
24
|
+
Three surfaces are affected, and two are worse than a wrong printout:
|
|
25
|
+
|
|
26
|
+
- **`cellpy info` / `cellpy info --configloc`** — names a file that no longer has any effect.
|
|
27
|
+
- **`cellpy edit config`** (`cli_api.py:1876`) — opens the stale `.conf`, so the user edits a file that is silently ignored. Most damaging: the edit appears to succeed and changes nothing.
|
|
28
|
+
- **`cellpy info --check`** (`_check_config_file`, `cli_api.py:999-1011`) — validates the stale YAML via `_read_prm_file_without_updating`, so it checks paths the runtime is not using.
|
|
29
|
+
|
|
30
|
+
`cellpy info --show-config` is already correct — it goes through `config.get_config()` with provenance (`_dump_config_resolved`, `cli_api.py:1223`).
|
|
31
|
+
|
|
32
|
+
## Spec
|
|
33
|
+
|
|
34
|
+
- Resolve the *active* config file with the same precedence as `load_config`: user `cellpy.toml` → legacy `.conf` → none. Put this in one helper (e.g. `cellpy.config.loader.active_config_file()`) so the CLI and the loader cannot drift apart again.
|
|
35
|
+
- `_configloc()` reports that file. When a legacy `.conf` exists but is shadowed by a TOML, say so explicitly, e.g.
|
|
36
|
+
`[cellpy] (legacy C:\Users\jepe\.cellpy_prms_jepe.conf is ignored — cellpy.toml takes precedence)`.
|
|
37
|
+
- `cellpy edit config` opens the active file.
|
|
38
|
+
- `_check_config_file` validates the active file; for a TOML, check it through the config models rather than the YAML reader.
|
|
39
|
+
|
|
40
|
+
## Acceptance criteria
|
|
41
|
+
|
|
42
|
+
- With a `cellpy.toml` present, `cellpy info` prints the TOML path and mentions the shadowed legacy file when one exists.
|
|
43
|
+
- With no TOML, output is unchanged from today (legacy path).
|
|
44
|
+
- `cellpy edit config` opens the TOML when it exists.
|
|
45
|
+
- `cellpy info --check` passes/fails based on the TOML's paths, not the legacy YAML's.
|
|
46
|
+
- Tests cover all three states: TOML only, legacy only, both (TOML wins + shadow notice). Marked `essential` — this is the "which config am I actually using" question and it must stay honest.
|
|
47
|
+
|
|
48
|
+
## Out of scope
|
|
49
|
+
|
|
50
|
+
- Changing precedence itself, or deprecating/deleting the legacy `.conf`.
|
|
51
|
+
- `cellpy setup migrate` behaviour (it works correctly).
|
|
52
|
+
- Project-level `cellpy.toml` discovery (`find_project_config_file`) — worth a follow-up, since a project file also outranks the user file and `info` will not mention it either.
|